- 1 1. MySQL에서 현재 날짜/시간을 가져오는 가장 짧은 SQL
- 2 2. NOW() / CURRENT_TIMESTAMP / SYSDATE()의 차이점
- 3 3. 현재 날짜/시간의 표시 형식 변경
- 4 4. 현재 시간을 이용한 Datetime 계산
- 5 5. 범위 쿼리에 대해 BETWEEN보다 더 안전한 방법
- 6 6. DEFAULT CURRENT_TIMESTAMP 및 ON UPDATE (테이블 설계 기본)
- 7 7. 시간대 설계 (UTC 저장, 로컬 시간 표시)
- 8 8. 바로 사용할 수 있는 실용 예제
- 9 9. 자주 묻는 질문 (FAQ)
- 10 10. 요약
1. MySQL에서 현재 날짜/시간을 가져오는 가장 짧은 SQL
MySQL에서 현재 날짜/시간을 가져오려면, 기억해야 할 첫 번째 함수는 NOW()와 CURRENT_TIMESTAMP입니다. 아래는 사용 사례별 가장 짧은 SQL 예제입니다.
1.1 현재 날짜/시간 가져오기 (기본)
SELECT NOW();
또는
SELECT CURRENT_TIMESTAMP;
- 둘 다 현재 날짜/시간 (YYYY-MM-DD HH:MM:SS) 을 반환합니다.
- 동일한 쿼리 내에서 시간은 “쿼리 시작 시간”에 고정됩니다.
사용 사례
- 로깅
- 레코드 생성 타임스탬프 가져오기
- 참조 시간 가져오기
일반적인 실수
- 앱 시간대와 DB 시간대가 다르고 “시간이 이동합니다.” →
SELECT @@session.time_zone;으로 확인하세요.
1.2 오늘 날짜만 가져오기
SELECT CURDATE();
- 반환 형식:
YYYY-MM-DD - 타입은 DATE (날짜만, datetime 아님)
사용 사례
- “오늘의 데이터” 검색
- 일일 집계
주의사항
NOW()와 달리 시간을 포함하지 않습니다.- 범위 쿼리에 시간이 필요하다면
NOW()를 사용하세요.
1.3 현재 시간만 가져오기
SELECT CURTIME();
- 반환 형식:
HH:MM:SS - 타입은 TIME (시간만)
사용 사례
- 배치 실행 시간 표시
- 로그에서 시간 부분만 출력
일반적인 함정
- 날짜를 포함하지 않기 때문에 datetime 비교에 사용할 수 없습니다.
1.4 UTC로 현재 시간 가져오기 (중요)
SELECT UTC_TIMESTAMP();
- 서버 시간대와 관계없이 UTC를 반환합니다.
- 글로벌 서비스에 권장됩니다.
실제 프로젝트의 기본 정책
- UTC로 저장
- 표시할 때 로컬 시간으로 변환
주의사항
- 앱이 이미 UTC로 변환했다면 이중 변환에 주의하세요.
1.5 밀리초 / 마이크로초 가져오기
SELECT NOW(3); -- milliseconds (3 digits after the decimal point)
SELECT NOW(6); -- microseconds (6 digits after the decimal point)
- MySQL 5.6.4 이상에서 사용 가능합니다.
- 고정밀 로그와 트랜잭션 감사에 사용됩니다.
일반적인 오해
NOW()만으로는 소수점 초를 반환할 수 없습니다.- 열도 정밀도를 지정해야 합니다. 예:
DATETIME(6).
1.6 사용 사례별 최적 함수 (확실하지 않으면 여기부터 시작)
| Use case | Recommended function |
|---|---|
| General current datetime | NOW() |
| Table default value | CURRENT_TIMESTAMP |
| Date only | CURDATE() |
| Time only | CURTIME() |
| Unified UTC management | UTC_TIMESTAMP() |
| High-precision logs | NOW(6) |
사람들이 자주 놓치는 핵심 포인트
NOW()와CURRENT_TIMESTAMP는 실질적으로 동일합니다 (같은 값)- 동일한 쿼리 내에서 시간은 고정됩니다
- 표시되는 값은 시간대 설정에 따라 변경됩니다
- 마이크로초의 경우, 열 정의에서 정밀도를 지정해야 합니다
2. NOW() / CURRENT_TIMESTAMP / SYSDATE()의 차이점
현재 시간을 가져오는 여러 함수가 있지만, 가장 혼란스러운 부분은 NOW(), CURRENT_TIMESTAMP, SYSDATE()의 차이점입니다.
이것을 올바르게 이해하지 않으면 로그와 트랜잭션 처리에서 예상치 못한 동작이 발생할 수 있습니다.
2.1 NOW()와 CURRENT_TIMESTAMP는 실질적으로 동일
SELECT NOW(), CURRENT_TIMESTAMP;
- 둘 다 현재 datetime (DATETIME 값) 을 반환합니다.
- 동일한 쿼리 내에서 시간은 “쿼리 시작 시간”에 고정됩니다.
CURRENT_TIMESTAMP는 함수 호출 구문으로도 사용할 수 있습니다:SELECT CURRENT_TIMESTAMP();
실제 프로젝트에서 선택 방법
| Use case | Recommended |
|---|---|
| Simple retrieval | NOW() |
| Table DEFAULT / ON UPDATE | CURRENT_TIMESTAMP |
중요 포인트
- 반환되는 값의 의미는 동일합니다.
- 주요 차이점은 구문/사용법 (예: DEFAULT에서 허용)입니다.
- 타입 차이가 아닙니다 (오해하기 쉽습니다).
2.2 SYSDATE()는 “평가 시간”의 시간을 반환
SYSDATE()는 NOW()와 유사해 보이지만, 값이 고정되는 시점이 다릅니다.
SELECT NOW(), SLEEP(2), NOW();
결과 (예시):
2025-02-23 14:00:00
2025-02-23 14:00:00
SELECT SYSDATE(), SLEEP(2), SYSDATE();
결과 (예시):
2025-02-23 14:00:00
2025-02-23 14:00:02
본질적인 차이점
| Function | When the time is fixed |
|---|---|
NOW() | Query start time |
SYSDATE() | Evaluation time |
즉, SYSDATE()는 시간이 지나면서 동일한 쿼리 내에서 변경될 수 있습니다.
2.3 트랜잭션 및 복제에 대한 참고 사항
NOW()는 쿼리 시작 시점에 고정되므로
트랜잭션 내부에서 일관된 기준 시간이 필요할 때 더 안전합니다.
반면 SYSDATE()는 실행 시점에 따라 달라지기 때문에 다음과 같은 경우 재현성에 영향을 줄 수 있습니다:
- 복제
- 배치 작업
- 대량 로그 처리
경험 법칙
- 고정된 기준 시간이 필요 →
NOW() - 매번 정확한 순간이 필요 →
SYSDATE()(제한적인 사용)
2.4 참고: CURRENT_DATE / CURRENT_TIME / LOCALTIME
MySQL은 또한 다음을 지원합니다:
SELECT CURRENT_DATE;
SELECT CURRENT_TIME;
SELECT LOCALTIME;
CURRENT_DATE→CURDATE()와 동일CURRENT_TIME→CURTIME()와 동일LOCALTIME→NOW()와 동일
흔한 혼동
- 대부분 “표면적인 차이”이며 기능적인 차이는 거의 없습니다.
- 안전을 위해 가독성을 우선시하고 프로젝트 내에서 표준화하세요.
이 섹션의 주요 요점
NOW()와CURRENT_TIMESTAMP는 본질적으로 같은 의미입니다.SYSDATE()는 평가 시점에 기반하므로 동작이 다릅니다.- 고정된 기준 시간이 필요할 때는 기본값으로
NOW()를 사용합니다. DEFAULT와ON UPDATE에는CURRENT_TIMESTAMP를 사용합니다.
3. 현재 날짜/시간의 표시 형식 변경
NOW()로 얻은 현재 시간은 기본적으로 YYYY-MM-DD HH:MM:SS 형태로 표시됩니다. 실제 작업에서는 다음과 같은 형태가 자주 필요합니다:
- 날짜만 표시
YYYY/MM/DD사용- 로컬화된 형식 사용 (예: 2025년 2월 23일)
- 밀리초 표시
이를 위해 DATE_FORMAT() (datetime을 임의 문자열 형식으로 변환) 을 사용합니다.
3.1 DATE_FORMAT() 기본 구문
DATE_FORMAT(datetime, 'format_string')
예시: 현재 시간을 YYYY/MM/DD HH:MM 형태로 변환
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i');
출력 예시:
2025/02/23 14:35
일반적인 형식 지정자
| Specifier | Meaning | Example |
|---|---|---|
%Y | 4-digit year | 2025 |
%m | 2-digit month | 02 |
%d | 2-digit day | 23 |
%H | Hour (24-hour) | 14 |
%i | Minutes | 35 |
%s | Seconds | 50 |
%f | Microseconds | 123456 |
주의 사항
- 일부 지정자는 대소문자에 따라 다르게 동작합니다.
%h는 12시간제,%H는 24시간제입니다.
3.2 일반적인 형식 예시 (실무에서 자주 사용)
1. 슬래시 구분
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d');
2. 로컬화 (영어)
SELECT DATE_FORMAT(NOW(), '%b %d, %Y');
3. 초 없이
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i');
공통 함정
- 문자열을 반환하므로 숫자 연산에 사용할 수 없습니다.
- 비교 및 검색 시에는 원본
DATETIME타입을 사용해야 합니다.
3.3 날짜 또는 시간 부분만 추출
문자열로 포맷팅하지 않고 타입을 유지하면서 부분을 추출할 수 있습니다.
SELECT DATE(NOW()); -- date only (DATE type)
SELECT TIME(NOW()); -- time only (TIME type)
SELECT YEAR(NOW()); -- year only
SELECT MONTH(NOW()); -- month only
SELECT DAY(NOW()); -- day only
권장 사용법
| Goal | Recommended |
|---|---|
| Formatting for display | DATE_FORMAT |
| Calculation / comparison | DATE() / TIME() |
3.4 마이크로초 표시
고정밀 로그가 필요할 때:
SELECT NOW(6);
포맷 적용:
SELECT DATE_FORMAT(NOW(6), '%Y-%m-%d %H:%i:%s.%f');
중요한 참고 사항
- 컬럼은
DATETIME(6)또는TIMESTAMP(6)이어야 하며, 그렇지 않으면 정밀도가 저장되지 않습니다. - MySQL 5.6.4 이전 버전에서는 지원되지 않습니다 (환경에 따라 다름).
흔한 실수
DATE_FORMAT()을WHERE절에 사용하면 인덱스가 사용되지 않습니다. wp:list /wp:list- 잘못된 예:
WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2025-02-23' - 권장 예:
WHERE created_at >= '2025-02-23 00:00:00' AND created_at < '2025-02-24 00:00:00' - 표시용 문자열을 그대로 저장하면 검색 성능이 저하됩니다.
- 잘못된 예:
이 섹션의 주요 요점
DATE_FORMAT()을 사용해 표시 형식을 변경합니다.- 계산·비교 시에는 원본 타입을 유지합니다.
- 마이크로초가 필요하면 컬럼 정의에 정밀도를 지정합니다.
WHERE절에서 함수를 사용하면 성능 문제가 발생할 수 있습니다.
4. 현재 시간을 이용한 Datetime 계산
MySQL에서 현재 시간을 기준으로 “N시간 후”, “N일 전”, 또는 “지난 N일” 같은 것을 계산하는 것은 일반적입니다.
가장 일반적인 실세계 사례는 “지난 24시간 동안의 데이터를 가져오기.”입니다.
날짜시간 산술의 기초는 INTERVAL입니다 (시간 단위를 더하거나 빼는 구문).
4.1 INTERVAL을 사용한 더하기/빼기
1시간 후 가져오기
SELECT NOW() + INTERVAL 1 HOUR;
3일 전 가져오기
SELECT NOW() - INTERVAL 3 DAY;
1개월 후 가져오기
SELECT NOW() + INTERVAL 1 MONTH;
일반적인 단위
| Unit | Meaning |
|---|---|
| SECOND | Seconds |
| MINUTE | Minutes |
| HOUR | Hours |
| DAY | Days |
| WEEK | Weeks |
| MONTH | Months |
| YEAR | Years |
일반적인 실수
INTERVAL 1 DAY에서DAY는 소문자에서도 작동하지만, 팀 내에서 표준화해야 합니다.- 월말 계산은 일수의 변동으로 인해 예상과 다를 수 있습니다 (예: 1월 31일 + 1개월).
4.2 지난 24시간 동안의 데이터 가져오기 (가장 일반적인 패턴)
SELECT *
FROM users
WHERE created_at >= NOW() - INTERVAL 1 DAY;
의미
- “지금부터 24시간 전까지”를 대상으로 합니다.
주의사항
CURDATE()를 사용하면 기준이 “오늘 00:00”이 되어 의미가 변경됩니다.
예시 (오늘의 데이터):
SELECT *
FROM users
WHERE created_at >= CURDATE();
차이점을 이해하는 것이 중요합니다
| Expression | Meaning |
|---|---|
NOW() - INTERVAL 1 DAY | Past 24 hours |
CURDATE() | Since today 00:00 |
4.3 DATEDIFF()를 사용한 일수 차이 계산
SELECT DATEDIFF('2025-03-01', '2025-02-23');
결과:
6
- 단위는 “일”
- 시간 부분은 무시됩니다
주의사항
- 인수 순서에 따라 부호가 변경됩니다.
- 시간/분/초 단위의 차이를 계산할 수 없습니다.
4.4 시간/분/초를 위한 TIMESTAMPDIFF() 사용
SELECT TIMESTAMPDIFF(HOUR,
'2025-02-23 12:00:00',
'2025-02-23 18:30:00');
결과:
6
분
SELECT TIMESTAMPDIFF(MINUTE,
'2025-02-23 12:00:00',
'2025-02-23 12:30:00');
초
SELECT TIMESTAMPDIFF(SECOND,
'2025-02-23 12:00:00',
'2025-02-23 12:00:45');
사용 사례
- 세션 지속 시간 계산
- 만료 확인
- 타임아웃 결정
4.5 월 시작 / 월 끝 가져오기 (일반적인 실세계 함정)
이번 달의 첫 번째 날 가져오기:
SELECT DATE_FORMAT(NOW(), '%Y-%m-01');
다음 달의 첫 번째 날:
SELECT DATE_FORMAT(NOW() + INTERVAL 1 MONTH, '%Y-%m-01');
주의사항
- 월 끝은 고정된 날짜가 아닙니다.
- 범위 쿼리의 경우 “>= 시작 AND < 다음 달의 첫 번째 날”이 안전합니다.
일반적인 실수 요약
- 날짜만 있는 값으로
BETWEEN을 사용하면서 시간을 잊음 - WHERE 절에서
DATE(created_at)를 사용해 인덱스를 비활성화함 - 월 계산에서 “31일” 문제에 걸림
NOW()와CURDATE()의 차이를 오해함
이 섹션의 주요 요점
INTERVAL은 날짜시간 산술의 기초입니다.- 지난 24시간 =
NOW() - INTERVAL 1 DAY. - 일 =
DATEDIFF(); 작은 단위 =TIMESTAMPDIFF(). - 월 기반 계산의 끝 조건에 주의하세요.
5. 범위 쿼리에 대해 BETWEEN보다 더 안전한 방법
MySQL에서 날짜 범위 검색을 할 때 많은 초보자들이 BETWEEN을 사용합니다. 그러나 시간 경계를 잘못 처리하면 의도하지 않은 결과를 초래하는 경우가 많아, 실제 작업에서는 더 안전한 패턴을 권장합니다.
5.1 BETWEEN 기본과 함정
일반적인 쿼리
SELECT *
FROM orders
WHERE order_date BETWEEN '2025-02-01' AND '2025-02-10';
괜찮아 보이지만, 내부적으로는 다음과 동등합니다:
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date <= '2025-02-10 00:00:00'
따라서 2월 10일 00:00 이후의 데이터는 포함되지 않습니다.
5.2 더 안전한 패턴 (권장)
권장 패턴
SELECT *
FROM orders
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date < '2025-02-11 00:00:00';
주요 포인트
- 끝 경계를 “다음 날 00:00보다 작음”으로 지정
<= 23:59:59보다 안전함 (마이크로초 지원)
5.3 오늘 데이터 가져오는 올바른 방법
잘못된 예시:
WHERE DATE(created_at) = CURDATE();
문제점:
- 컬럼에 함수를 적용하면 인덱스가 비활성화됩니다
- 대용량 테이블에서 심각한 속도 저하를 초래할 수 있습니다
권장 방법:
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
이렇게 하면:
- 인덱스를 사용할 수 있음
- 빠른 검색
- 경계 문제 없음
5.4 지난 7일 / 지난 30일에 대한 안전한 패턴
지난 7일
WHERE created_at >= NOW() - INTERVAL 7 DAY;
지난 30일
WHERE created_at >= NOW() - INTERVAL 30 DAY;
※ 참고
CURDATE() - INTERVAL 7 DAY는 “오늘 00:00”을 기준으로 함NOW() - INTERVAL 7 DAY는 “현재 시간”을 기준으로 함- 요구사항에 따라 선택
5.5 인덱스를 효과적으로 유지하기 위한 핵심 규칙
잘못된 예:
WHERE DATE(created_at) = '2025-02-23';
올바른 예:
WHERE created_at >= '2025-02-23 00:00:00'
AND created_at < '2025-02-24 00:00:00';
이유:
- 인덱스는 “컬럼 자체”에 대해 작동함
- 함수를 적용하면 인덱스 사용이 불가능해짐(전체 스캔)
일반적인 실수 요약
BETWEEN에서 끝 경계에 시간을 포함하는 것을 잊음23:59:59사용 시 마이크로초를 놓침- WHERE 절에
DATE()를 사용해 쿼리를 느리게 함 - “now”와 “today 00:00”을 모호하게 남김
이 섹션의 주요 요점
- 범위 쿼리는
>= start AND < end형태가 가장 안전함. BETWEEN은 경계 처리를 신중히 해야 함.- 인덱스를 유지하려면 컬럼을 함수로 감싸지 말 것.
NOW()기반 로직과CURDATE()기반 로직을 명확히 선택할 것.
6. DEFAULT CURRENT_TIMESTAMP 및 ON UPDATE (테이블 설계 기본)
데이터베이스 설계에서는 created_at 및 updated_at을 자동으로 관리하는 것이 일반적입니다.
MySQL에서는 CURRENT_TIMESTAMP를 사용해 삽입 및 업데이트 시 현재 시간을 자동으로 설정할 수 있습니다.
6.1 created_at 자동 설정 (DEFAULT CURRENT_TIMESTAMP)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
동작:
INSERT INTO users (name) VALUES ('Alice');
→ 현재 시간이 자동으로 created_at에 삽입됩니다.
※ 핵심 포인트
CURRENT_TIMESTAMP는 DEFAULT에 사용할 수 있음.NOW()는 DEFAULT에 직접 사용할 수 없음.
※ 일반적인 실수
created_at DATETIME DEFAULT NOW(); -- error
이유:
- MySQL에서는 일반적으로 함수를 DEFAULT로 설정할 수 없으며 (예외는 CURRENT_TIMESTAMP).
6.2 updated_at 자동 업데이트 (ON UPDATE)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
동작:
UPDATE users SET name = 'Bob' WHERE id = 1;
→ updated_at가 현재 시간으로 자동 업데이트됩니다.
※ 활용 사례
- 마지막 로그인 추적
- 업데이트 기록
- 감사 로그
6.3 DATETIME vs TIMESTAMP (중요)
| Type | Range | Time zone impact |
|---|---|---|
| DATETIME | Year 1000 to 9999 | No |
| TIMESTAMP | 1970 to 2038 | Yes |
※ 핵심 차이점
TIMESTAMP는 내부적으로 UTC로 저장되며 표시 시 세션 시간대로 변환됩니다.DATETIME은 그대로 값을 저장하며 변환되지 않음.
※ 가이드라인
| Case | Recommended type |
|---|---|
| Log management | TIMESTAMP |
| Future dates (after 2038) | DATETIME |
| Fixed values not requiring TZ conversion | DATETIME |
6.4 CURRENT_TIMESTAMP를 DATETIME에도 사용할 수 있음
MySQL 5.6 이상에서는 DATETIME 컬럼에 대해서도 CURRENT_TIMESTAMP를 DEFAULT 및 ON UPDATE로 지정할 수 있습니다.
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
※ 참고
- 구버전 MySQL은 제한이 있음(환경에 따라 다름).
- 정밀도가 필요하다면:
DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)
6.5 NOW()를 사용하고 싶을 때: 대안 (트리거)
CREATE TRIGGER set_created_at
BEFORE INSERT ON logs
FOR EACH ROW
SET NEW.created_at = NOW();
※ 활용 사례
- 복잡한 초기값 로직
- 조건부 타임스탬프 할당
노트
- 트리거는 디버깅하기 어렵습니다.
- 필요하지 않은 경우 피하십시오.
일반적인 설계 실수
created_at와updated_at모두에 ON UPDATE를 추가하기- DATETIME과 TIMESTAMP 동작을 혼동하기
- 시간대 결정을 미루기
이 섹션의 주요 요점
- DEFAULT에
CURRENT_TIMESTAMP사용하기. - 업데이트 추적을 위해
ON UPDATE CURRENT_TIMESTAMP사용하기. - 시간대 동작 및 2038 문제를 기준으로 타입 선택하기.
- 정밀도가 필요하면
(6)을 잊지 마세요.
7. 시간대 설계 (UTC 저장, 로컬 시간 표시)
현재 시간을 다룰 때, 잘못된 시간대 설계는 시간 흐름 오류와 이중 변환을 초래합니다.
실제로 가장 안전한 기본값은 “UTC에 저장하고 표시할 때 로컬 시간으로 변환”입니다.

그림: MySQL에서 UTC를 저장하고 로컬 시간을 표시하는 기본 아키텍처
7.1 현재 시간대 확인
먼저 MySQL이 실행 중인 시간대를 확인하세요.
SELECT @@global.time_zone, @@session.time_zone;
@@global.time_zone→ 서버 전체 설정@@session.time_zone→ 현재 연결/세션 설정SYSTEM이 표시되면 OS 설정에 따라 달라집니다
일반적인 문제
- 운영 및 개발 서버가 서로 다른 OS 시간대를 가지고 있음
- 앱이 UTC로 변환하고 DB가 다시 변환 (이중 변환)
7.2 세션별 시간대 변경
SET time_zone = 'Asia/Tokyo';
or unify to UTC:
SET time_zone = '+00:00';
중요 포인트
- 연결이 닫히면 원래대로 돌아갑니다.
- 커넥션 풀링을 사용하는 경우 애플리케이션 설정을 확인하세요.
7.3 왜 UTC에 저장하는가 (실제 원칙)
권장 정책
- 데이터를 UTC에 저장
- 표시할 때 사용자의 시간대로 변환
이유:
- 국제 지원이 용이
- 일광 절약 시간(DST) 문제 회피
- 서버 이동 시 영향을 최소화
UTC 가져오기:
SELECT UTC_TIMESTAMP();
7.4 CONVERT_TZ()로 시간대 변환
예시: UTC → JST
SELECT CONVERT_TZ('2025-02-23 05:35:00', '+00:00', '+09:00');
시간대 이름 사용:
SELECT CONVERT_TZ('2025-02-23 05:35:00', 'UTC', 'Asia/Tokyo');
실제 예시
SELECT CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo')
FROM users;
7.5 CONVERT_TZ()가 NULL을 반환하는 이유
NULL을 반환하면 일반적인 원인은 다음과 같습니다:
- MySQL 시간대 테이블이 로드되지 않음
- 지정된 시간대 이름이 존재하지 않음
Linux에서 로드 예시:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
노트
- 운영 환경에서는 권한과 재시작 필요 여부를 확인하세요.
- Docker에서는 이미지에 따라 zoneinfo가 포함되지 않을 수 있습니다.
7.6 서버 전체 시간대 변경
구성 파일 (my.cnf / my.ini):
[mysqld]
default_time_zone = '+00:00'
재시작 후 확인:
SELECT @@global.time_zone;
중요
- 운영 환경 변경 시 영향 범위를 확인하세요.
- 기존 데이터에 주의 (때로는 표시만 변경될 수 있음)
일반적인 실수
- UTC 대신 로컬 시간을 저장하고 나중에 국제 지원을 추가
- 앱과 DB 사이의 이중 변환
- 시간대를 고려하지 않고 DATETIME으로 설계
- 테스트와 운영 간 TZ 설정 차이
이 섹션의 주요 요점
- UTC에 저장하고 표시 시 변환.
UTC_TIMESTAMP()사용.CONVERT_TZ()사용 시 TZ 테이블을 확인.- 항상 환경별 시간대 차이를 고려.
8. 바로 사용할 수 있는 실용 예제
여기에는 실제 개발/운영에서 MySQL 현재 시간을 사용하는 방법을 보여주는 구체적인 SQL 예제가 있습니다.
복사하여 바로 붙여넣을 수 있도록 작성되었습니다.
8.1 로그에 현재 시간을 자동 삽입
Create table
CREATE TABLE system_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
level VARCHAR(50),
message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
로그 삽입
INSERT INTO system_logs (level, message)
VALUES ('ERROR', 'Failed to connect to the DB');
핵심 포인트
created_at은 자동으로 삽입됩니다- 앱에서 타임스탬프를 전달할 필요가 없습니다
- 감사 및 사고 조사에 필수적인 설계
일반적인 실수
- 앱과 DB 모두에서 시간을 관리함
- 통합되지 않은 시간대 때문에 발생하는 시간 불일치
8.2 마지막 로그인 시간 업데이트
테이블 설계
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
로그인 시 업데이트
UPDATE users
SET last_login = NOW()
WHERE id = 1;
주의사항
ON UPDATE는 컬럼이 변경될 때만 작동합니다- 일부 UPDATE 문은 값이 변경되지 않아 업데이트되지 않을 수 있습니다
8.3 배치 작업에서 기준 시간 고정
장시간 실행되는 프로세스에서는 기준 시간을 고정하는 것이 더 안전합니다.
SET @base_time = NOW();
SELECT *
FROM orders
WHERE created_at >= @base_time - INTERVAL 1 DAY;
이유
- 프로세스 중에 시간이 이동하지 않습니다
- 일관성이 유지됩니다
8.4 오늘 / 어제 / 지난 7일에 대한 집계
오늘 매출
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
어제 매출
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE() - INTERVAL 1 DAY
AND created_at < CURDATE();
지난 7일
SELECT COUNT(*)
FROM users
WHERE created_at >= NOW() - INTERVAL 7 DAY;
중요
- WHERE 절에서 컬럼에 함수를 적용하지 마세요
- 인덱스를 활용할 수 있는 조건을 작성하세요
8.5 만료 검사 (세션/토큰)
SELECT *
FROM sessions
WHERE expires_at > NOW();
만료된 행 삭제
DELETE FROM sessions
WHERE expires_at <= NOW();
일반적인 실수
CURDATE()를 사용하고 시간을 무시함- UTC를 저장하지만 로컬
NOW()와 비교함
8.6 N시간 이내에 만료되는 행 가져오기
SELECT *
FROM coupons
WHERE expires_at <= NOW() + INTERVAL 1 HOUR;
사용 사례:
- 만료 알림
- 만료 통지
항상 염두에 두어야 할 사항
- 기준이 “현재(now)”인지 “오늘 00:00”인지 명확히 하세요
- 인덱스가 효율적으로 작동하도록 조건을 작성하세요
- 시간대 설계를 사전에 결정하세요
- 명확한 정책 없이
NOW()와UTC_TIMESTAMP()를 혼용하지 마세요
이 섹션의 주요 요점
- 로그/감사/업데이트 추적에는
CURRENT_TIMESTAMP를 사용하세요 - 집계에는 안전한
>= AND <패턴을 사용하세요 - 세션 관리에는
NOW()와 비교하세요 - 처리를 일관되게 유지하려면 기준 시간을 고정하세요
9. 자주 묻는 질문 (FAQ)
다음은 MySQL 현재 시간에 대한 일반적인 질문들로, 실제 작업에서 발생하는 함정들을 다룹니다.
9.1 NOW()와 CURRENT_TIMESTAMP의 차이점은 무엇인가요?
결론: 반환값의 의미는 동일합니다.
SELECT NOW(), CURRENT_TIMESTAMP;
두 함수 모두 현재 날짜와 시간을 반환합니다.
주요 차이점은 구문 사용에 있습니다
CURRENT_TIMESTAMP는DEFAULT와ON UPDATE에 사용할 수 있습니다NOW()는DEFAULT에 직접 사용할 수 없습니다
주의사항
- 타입 차이는 아닙니다
(6)과 같이 인자를 사용해 정밀도를 지정할 수 있습니다
9.2 SYSDATE()를 사용해야 할까요?
SYSDATE()는 “평가 시점”의 시간을 반환합니다.
SELECT SYSDATE(), SLEEP(2), SYSDATE();
같은 쿼리 내에서도 값이 변할 수 있습니다.
사용 시기
- 정확한 실시간 순간을 기록해야 할 때
피해야 할 경우
- 복제
- 일관성이 중요한 트랜잭션 처리
대부분의 경우 NOW()를 사용하면 충분합니다.
9.3 시간이 왜 이동하나요?
주요 원인:
- 서버 시간대 설정
- 세션 시간대 설정
- 애플리케이션과의 이중 변환
- TIMESTAMP 타입의 자동 변환 동작
다음으로 확인:
SELECT @@global.time_zone, @@session.time_zone;
완화 방안
- 기본적으로 UTC로 저장
- 표시할 때만 변환
- 애플리케이션과 DB 간 정책 통합
9.4 CONVERT_TZ()가 NULL을 반환함
원인:
- 시간대 테이블이 로드되지 않음
- 잘못된 시간대 이름
해결:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
Docker 환경에서는 특히 주의하세요.
9.5 BETWEEN 사용 시 범위 이동
잘못된 예:
WHERE created_at BETWEEN '2025-02-01' AND '2025-02-10';
올바른 예:
WHERE created_at >= '2025-02-01 00:00:00'
AND created_at < '2025-02-11 00:00:00';
이유:
- 끝 경계 시간 문제
- 마이크로초 누수
- 인덱스 효율성
9.6 DATETIME과 TIMESTAMP 중 어떤 것을 선택할까?
- 국제 지원 / UTC 관리 → TIMESTAMP
- 2038년 이후 또는 고정된 날짜시간 → DATETIME
설계 단계에서 결정하세요.
9.7 마이크로초가 저장되지 않음
원인:
- 컬럼 정의에 정밀도가 지정되지 않음
해결:
created_at DATETIME(6)
이 섹션의 주요 요점
NOW()와CURRENT_TIMESTAMP로 시작- 범위 쿼리에서는
>= AND <사용 - UTC 저장이 가장 안전한 기본값
- 타입 선택과 정밀도를 잊지 말 것
10. 요약
이 글에서는 MySQL에서 현재 시간을 조회, 포맷, 계산 및 관리하는 실용적인 방법을 정리했습니다.
마지막으로 안전하게 작업하기 위해 반드시 알아야 할 최소 필수 사항을 소개합니다.
10.1 현재 시간 가져오기 (기본)
- 일반 조회 →
NOW() - 테이블 DEFAULT / 업데이트 추적 →
CURRENT_TIMESTAMP - 날짜만 →
CURDATE() - 시간만 →
CURTIME() - UTC →
UTC_TIMESTAMP() - 고정밀도 →
NOW(6)
대략적인 규칙
- 확신이 서지 않을 때는 대부분
NOW()를 사용해도 무방합니다. - 스키마 설계 시에는
CURRENT_TIMESTAMP를 사용하세요.
10.2 범위 쿼리: “>= AND <”가 황금 규칙
안전한 패턴:
WHERE created_at >= 'START'
AND created_at < 'END'
이유:
- 끝 경계 행 누락 방지
- 마이크로초와 호환
- 인덱스 사용 가능 유지
잘못된 예
WHERE DATE(created_at) = CURDATE();
컬럼을 함수로 감싸지 마세요.
10.3 날짜시간 연산 기본
- 더하기/빼기 →
INTERVAL - 일 차이 →
DATEDIFF() - 시간 차이 →
TIMESTAMPDIFF()
기준이 “현재”인지 “오늘 00:00”인지 항상 염두에 두세요.
10.4 시간대 설계 원칙
- UTC로 저장
- 표시 시 변환
- 애플리케이션과 DB 간 정책 통합
다음으로 확인:
SELECT @@global.time_zone, @@session.time_zone;
10.5 테이블 설계 모범 사례
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
- 감사/로그/업데이트 추적을 위한 표준 장비
- 정밀도가 필요하면
(6)을 지정
가장 중요한 실제 포인트 4가지
NOW()와CURRENT_TIMESTAMP의 차이를 오해하지 말 것- 범위 쿼리에서는
>= AND <사용 - 기본적으로 UTC로 저장
- 설계 단계에서 타입(DATETIME vs TIMESTAMP) 결정
MySQL 현재 시간 처리는 로깅, 매출 집계, 인증/세션 관리, 배치 작업 등 다양한 분야의 기반이 됩니다.
이 글의 원칙을 따르면 시간 드리프트, 경계 버그, 성능 저하와 같은 흔한 문제를 많이 예방할 수 있습니다.


