DATEDIFF · TIMEDIFF — 날짜·시간 차이 완전 정리
날짜를 다루다 보면 "이 두 시점 사이에 며칠이 지났는가", "이 두 시각 사이에 몇 시간 몇 분이 지났는가"를 계산해야 할 때가 있다. MySQL은 이 둘을 서로 다른 함수로 분리해 놓았다. 아래는 두 함수를 한 문장에서 같이 써 본 원본 코드다.
오늘이 2026-07-04라고 가정하고 두 함수를 각각 실행하면 아래와 같은 값이 나온다.
| 함수 호출 | 결과 | 반환 타입 | 의미 |
|---|---|---|---|
DATEDIFF('2025-01-01', NOW()) |
-549 | 정수(일수) | 2025-01-01이 오늘보다 549일 더 과거라는 뜻 |
TIMEDIFF('23:23:59', '12:11:10') |
11:12:49 | TIME (시:분:초) | 23시23분59초에서 12시11분10초를 뺀 시간 간격 |
숫자 하나(DATEDIFF)와 시:분:초 문자열(TIMEDIFF)이라는 반환 형태 자체가 완전히 다르다는 점을 눈으로 먼저 확인하고 넘어가자.
DATEDIFF는 인자로 DATETIME이나 TIMESTAMP를 넣어도 내부적으로 시간 부분을 버리고 날짜 부분만 비교한다. "2025-01-01 23:59:59"와 "2025-01-02 00:00:01"은 시간상으론 2초밖에 차이 나지 않지만, DATEDIFF는 날짜가 하루 바뀌었다는 사실만 보고 결과를 1로 낸다. 이것이 DATEDIFF가 시:분:초를 반환하지 않고 오직 정수 하나만 반환하는 이유다 — '일' 단위에는 애초에 시:분:초라는 개념이 들어설 자리가 없다.
TIMEDIFF는 날짜를 버리지 않고 시간의 흐름 자체를 표현해야 하는 함수다. 그래서 결과를 정수 하나로 뭉개지 않고 HH:MM:SS 형태의 TIME 값으로 돌려준다. 예를 들어 출근 시각과 퇴근 시각의 차이를 구할 때 "8"이라는 숫자만 받으면 8시간인지 8분인지 알 수 없다. TIME 형으로 반환해야 "08:00:00"처럼 단위가 명확한 결과를 사람이 그대로 읽을 수 있다.
두 함수 모두 함수명(첫번째, 두번째)일 때 계산 공식은 "첫번째 - 두번째"다. 원본 코드의 DATEDIFF('2025-01-01', NOW())는 '2025-01-01에서 오늘 날짜를 뺀 값'이라는 뜻이라, 2025-01-01이 오늘보다 과거이면 결과는 음수가 된다. 반대로 DATEDIFF(NOW(), '2025-01-01')로 순서를 바꾸면 부호만 뒤집혀 양수가 나온다. "얼마나 지났는가"를 양수로 보고 싶다면 항상 더 최근 날짜를 첫 번째 인자에 넣어야 한다.
DATEDIFF의 결과는 그냥 숫자(정수)이므로 WHERE DATEDIFF(...) > 30처럼 부등호 비교, 정렬, 합계 계산에 바로 쓸 수 있다. 반면 TIMEDIFF의 결과는 TIME 형이라서 숫자처럼 보이지만 실제로는 시:분:초 구조를 가진 값이다. 이 값을 순수한 '초' 단위 숫자로 바꾸고 싶다면 TIME_TO_SEC()으로 한 번 더 변환해야 사칙연산이나 평균 계산이 정확해진다.
단위: 일(day) 고정
바로 산술 연산 가능
단위: 시:분:초 표현
산술 연산 전 TIME_TO_SEC() 변환 권장
DATEDIFF는 앞서 말했듯 인자에서 날짜 부분만 추출해 비교하기 때문에, DATE 형인 CURDATE() 대신 DATETIME 형인 NOW()를 넣어도 문제없이 동작한다. 반면 TIMEDIFF는 시:분:초 정밀도가 중요한 함수이므로, 날짜가 섞인 값을 비교할 때는 두 인자의 날짜 부분이 서로 다르면 결과가 24시간을 넘거나 예상과 다르게 나올 수 있다. "오늘 안에서만" 시간 차이를 구하고 싶다면 CURTIME()처럼 순수 시간값끼리 비교하는 편이 안전하다.
- NOW() — 날짜 + 시간 (DATETIME)
- CURDATE() — 날짜만 (DATE)
- CURTIME() — 시간만 (TIME)
- CURRENT_DATE, CURRENT_TIME — CURDATE(), CURTIME()과 동일한 별칭 표현
- SYSDATE() — NOW()와 비슷하지만 함수가 '실행되는 순간'의 시각을 반환
TIME 자료형의 표현 범위가 -838:59:59 ~ 838:59:59로 24시간을 훌쩍 넘어서게 설계된 이유가 바로 TIMEDIFF 때문이다. 두 인자에 날짜가 포함된 DATETIME 값을 넣으면 TIMEDIFF는 날짜 차이까지 반영해 "34일 걸린 시간"도 시:분:초 하나로 눌러 담아야 한다. 이때 34일은 816시간이 넘으므로, 결과가 "일 몇 시간"이 아니라 "몇백 시간 몇 분"처럼 나와 사람이 읽기 불편해진다. 여러 날에 걸친 시간 차이를 다룰 때는 TIMEDIFF보다 TIMESTAMPDIFF(HOUR, ...)나 TIMESTAMPDIFF(MINUTE, ...)처럼 단위를 직접 지정하는 함수가 훨씬 다루기 쉽다.
회원 관리 테이블에서 "가입한 지 며칠 됐는지"를 보여주고 싶다면 DATEDIFF(NOW(), join_date) 한 줄로 끝난다. 반대로 근태 기록 테이블에서 "이 사람이 오늘 몇 시간 몇 분 일했는지"를 계산하려면 퇴근 시각과 출근 시각이 같은 날짜 안에 있다는 전제하에 TIMEDIFF(check_out, check_in)을 쓰면 "08:15:00"처럼 바로 화면에 붙일 수 있는 결과가 나온다.
TIMEDIFF의 결과가 아무리 사람이 읽기 좋은 "08:30:00" 형태라도, 여러 근무 기록의 평균 근무시간을 구하거나 총합을 내야 한다면 이야기가 달라진다. TIME 값끼리는 SUM()이나 AVG()를 곧이곧대로 적용하기 어렵기 때문에, 먼저 TIME_TO_SEC()으로 순수한 초 단위 정수로 풀어낸 다음 계산하고, 마지막에 다시 SEC_TO_TIME()으로 사람이 읽는 형태로 되돌리는 흐름을 탄다.
DATEDIFF(a, b)와 TIMESTAMPDIFF(DAY, b, a)는 인자 순서만 다를 뿐 대부분의 경우 같은 결과를 낸다. 차이는 유연성에 있다. DATEDIFF는 오직 '일' 단위만 계산하도록 이름부터 고정돼 있지만, TIMESTAMPDIFF는 첫 번째 인자에 YEAR·MONTH·DAY·HOUR·MINUTE·SECOND 중 원하는 단위를 자유롭게 골라 넣을 수 있다. 즉 DATEDIFF는 "일수 계산 전용 단축 함수"이고, TIMESTAMPDIFF는 "단위를 직접 지정하는 범용 함수"라고 이해하면 된다.
| 함수 | 인자 형태 | 단위 선택 | 주 용도 |
|---|---|---|---|
| DATEDIFF(a, b) | 날짜 2개 | 일(day) 고정 | 단순 경과일 계산 |
| TIMEDIFF(a, b) | 시간 2개 | 시:분:초 고정 | 같은 날 안의 시간 차 계산 |
| TIMESTAMPDIFF(단위, b, a) | 단위 + 날짜/시간 2개 | YEAR~SECOND 자유 선택 | 여러 날에 걸친 임의 단위 차이 계산 |
실제로 두 함수가 같은 결과를 내는지 나란히 놓고 비교해보면 이해가 빠르다.
| 실수 | 증상 | 해결 |
|---|---|---|
| DATEDIFF 인자 순서를 반대로 넣음 | D-day, 경과일이 음수로 표시됨 | "최근 날짜 - 과거 날짜" 순서로 항상 최근 값을 앞에 배치 |
| 여러 날에 걸친 시간차를 TIMEDIFF로 계산 | 결과가 "34:00:00"처럼 이상하게 커 보이거나 읽기 어려움 | TIMESTAMPDIFF(HOUR, 시작, 종료) 등 단위 지정 함수로 대체 |
| TIMEDIFF 결과를 숫자로 착각해 바로 더하기·평균 계산 | 합계·평균 값이 실제 시간과 맞지 않음 | TIME_TO_SEC()으로 초 단위 정수로 변환 후 연산 |
| DATEDIFF에 시:분:초까지 다른 걸 기대 | 같은 날 안의 시간차가 무시되고 항상 0이나 정수만 나옴 | 시간 단위 차이가 필요하면 TIMEDIFF나 TIMESTAMPDIFF 사용 |
| TIMEDIFF에 서로 다른 날짜의 DATETIME을 넣고 24시간 이내라고 가정 | 결과가 예상보다 훨씬 큰 시:분:초로 출력됨 | 날짜가 섞일 가능성이 있으면 TIMESTAMPDIFF로 전환 |
| TIMESTAMPDIFF의 단위 인자를 빼먹거나 잘못 지정 | 기대와 다른 스케일(초 대신 일 등)의 값이 나옴 | YEAR/MONTH/DAY/HOUR/MINUTE/SECOND 중 원하는 단위를 첫 인자에 정확히 명시 |
날짜 차이 함수는 결과를 화면에 보여주는 데서 끝나지 않고, 집계·정렬·필터 조건에 다시 들어가는 경우가 많다. 그래서 "보여줄 값"과 "계산할 값"을 분리해서 생각하면 실수가 줄어든다.
핵심 한 줄 요약
Tags
'php' 카테고리의 다른 글
| MySQL SUM(IF()) — 피벗 테이블 만들기 완전 정리 (0) | 2026.06.28 |
|---|---|
| MySQL REPEAT · REPLACE · REVERSE — 문자열 변형 완전 정리 (0) | 2026.06.28 |
| MySQL ADDDATE · SUBDATE — 날짜 더하기·빼기 완전 정리 (0) | 2026.06.27 |
| MySQL CONCAT_WS — 구분자 지정 문자열 연결 완전 정리 (0) | 2026.06.27 |
| MySQL ELT · LOCATE · INSTR — 위치·값 찾기 완전 정리 (0) | 2026.06.27 |
왕진 블로그

댓글