MariaDB 강좌 / 날짜와 시간 저장하기: DATE·DATETIME·TIMESTAMP
DATE는 날짜만, DATETIME은 날짜와 시각을, TIMESTAMP는 세션 시간대에 따라 변환되는 시점을 저장할 때 검토합니다. 같은 문자열을 입력해도 시간대 설정을 바꾸면 TIMESTAMP의 조회값만 달라질 수 있으므로, 저장하려는 값이 달력상의 시각인지 실제 발생 시점인지 먼저 정해야 합니다.
날짜·달력상의 시각·발생 시점 구분하기
| 타입 | 저장 의미와 예 | 시간대 처리 |
|---|---|---|
| DATE | 생일·예약일처럼 날짜만 필요 | 시각과 시간대 자체를 저장하지 않음 |
| DATETIME | 2026-10-13 09:00:00 같은 날짜·시각 | 세션 시간대 변경만으로 자동 변환되지 않음 |
| TIMESTAMP | 로그·접수 시점 등 실제 시점을 표현 | 입력·조회 때 세션 시간대와 UTC 사이 변환 |
협정 세계시(Coordinated Universal Time, UTC)는 서로 다른 지역의 시점을 비교할 때 사용하는 기준입니다. 한국 시간은 UTC보다 아홉 시간 빠르므로 한국의 09:00은 UTC의 같은 날 00:00과 같은 시점입니다. 날짜 문자열 앞에 UTC라는 글자를 붙이는 것만으로 데이터베이스 시간대가 바뀌지는 않습니다.
DATETIME은 지역의 벽시계 값처럼 보관할 수 있지만, 국제 서비스를 만들 때 그것만으로 시간대와 서머타임 규칙을 복원할 수 있는 것은 아닙니다. UTC로 정규화해 저장할지, 지역 시각과 시간대 식별자를 함께 저장할지 업무 목적에 맞춰 정합니다. TIMESTAMP도 원래 입력에 사용한 시간대 이름 자체를 보관하지는 않습니다.
버전과 시간대 확인하기
2~5강에서 준비한 course_user로 shopcourse 데이터베이스에 접속한 상태에서 실행합니다. 접속·SQL 파일 실행 방법은 명령줄 클라이언트 설명을 참고하세요. 서버 버전과 선택한 데이터베이스를 먼저 확인하고, 운영 데이터가 들어 있는 연결에서는 이 실습을 실행하지 않습니다.
SELECT VERSION() AS server_version, DATABASE() AS selected_database; USE shopcourse; SELECT DATABASE() AS selected_database;
SELECT @@SESSION.time_zone AS session_zone,
@@GLOBAL.time_zone AS global_zone,
@@system_time_zone AS system_zone;
SESSION은 현재 접속, GLOBAL은 새 연결이 사용할 기본 설정입니다. SYSTEM이라는 값은 서버 운영체제의 시간대에 따라 해석되므로 서버 위치만 보고 실제 접속의 시간대를 추정하지 않습니다. 실습에서는 명시적인 +09:00과 +00:00을 사용해 이름 기반 시간대 테이블 준비 없이 차이를 관찰합니다.
DATE와 DATETIME은 1000년부터 9999년까지의 범위를 지원하지만 TIMESTAMP 범위는 서버 버전 차이를 확인해야 합니다. MariaDB 11.5 미만의 TIMESTAMP 상한은 2038-01-19 03:14:07 UTC, 11.5부터는 2106-02-07 06:28:15 UTC입니다. 하한은 1970-01-01 00:00:01 UTC이며 특수한 zero 값과 별개입니다. Ubuntu 저장소 버전과 Windows 설치 버전이 다를 수 있으므로 TIMESTAMP 버전별 범위를 실제 VERSION 결과와 비교하세요.
같은 값을 넣고 시간대만 바꾸는 전체 예제
time_lab을 새로 만들고 같은 시각을 DATETIME과 TIMESTAMP에 넣습니다. TIMESTAMP에는 NULL DEFAULT NULL을 명시해 첫 TIMESTAMP 열의 자동 기본값처럼 버전·설정에 따른 암묵적인 동작을 피합니다. 현재 연결의 시간대와 SQL 모드를 변수로 보관합니다. 관련 조건은 테이블 구조 정의에서 구체적으로 설명합니다.
USE shopcourse; SET @saved_sql_mode = @@SESSION.sql_mode; SET SESSION sql_mode = 'STRICT_TRANS_TABLES'; SET @saved_time_zone = @@SESSION.time_zone; SET SESSION time_zone = '+09:00'; CREATE TABLE time_lab ( item_id INT PRIMARY KEY, event_date DATE NOT NULL, wall_time DATETIME NOT NULL, instant_time TIMESTAMP NULL DEFAULT NULL ) ENGINE=InnoDB; INSERT INTO time_lab VALUES (1, '2026-10-13', '2026-10-13 09:00:00', '2026-10-13 09:00:00'); SELECT item_id, event_date, wall_time, instant_time FROM time_lab; SET SESSION time_zone = '+00:00'; SELECT item_id, event_date, wall_time, instant_time FROM time_lab; SET SESSION time_zone = '+09:00'; SELECT item_id, wall_time, instant_time FROM time_lab;
| 조회 시점 | event_date | wall_time | instant_time |
|---|---|---|---|
| +09:00에서 입력 후 조회 | 2026-10-13 | 2026-10-13 09:00:00 | 2026-10-13 09:00:00 |
| +00:00으로 변경 후 조회 | 2026-10-13 | 2026-10-13 09:00:00 | 2026-10-13 00:00:00 |
| +09:00으로 복원 후 조회 | 2026-10-13 | 2026-10-13 09:00:00 | 2026-10-13 09:00:00 |
TIMESTAMP의 값이 두 개의 다른 사건으로 바뀐 것이 아닙니다. 같은 시점이 접속 시간대에 맞춰 다르게 표시된 것입니다. DATETIME은 입력한 달력 시각을 그대로 조회하므로 표시가 유지됩니다. 프로그램의 접속 시간대가 바뀌면 결과도 달라질 수 있다는 점이 이 비교의 핵심입니다. DATETIME 정의와 TIMESTAMP 시간대 처리가 이 차이를 설명합니다.
기본 현재 시각과 소수 초를 명시하기
생성 시각을 자동으로 넣으려면 DEFAULT CURRENT_TIMESTAMP를 열 정의에 명시합니다. 수정 시각을 자동으로 바꾸려면 ON UPDATE CURRENT_TIMESTAMP의 조건을 따로 검토합니다. 두 속성은 같은 의미가 아니며 수정 시각 열이 사건 발생 시각 열을 대신하지 않습니다.
DATETIME(6)과 TIMESTAMP(6)처럼 괄호 안에 소수 초 정밀도를 지정할 수 있습니다. 6은 연도나 전체 길이가 아니라 초 이하 여섯 자리를 뜻합니다. 저장 열과 기본값 함수의 정밀도도 맞춰 설계하고, 모든 기록에 마이크로초가 필요하다고 가정하지 않습니다.
YYYY-MM-DD와 YYYY-MM-DD HH:MM:SS처럼 해석이 명확한 형식을 사용합니다. 프로그램에서 10/11/26 같은 지역별 형식을 그대로 넘기거나 날짜를 문자열로 저장하면, 비교와 변환의 조건이 불분명해집니다. 날짜 전용 범위와 형식은 DATE 공식 정의를 참고하세요.
존재하지 않는 날짜와 연결 설정 확인하기
정상 실습 뒤 같은 strict 모드 연결에서 아래 문장을 따로 실행합니다. 2026년 2월 30일은 존재하지 않으므로 Incorrect date value 오류가 예상됩니다. SQL 모드를 바꾸면 잘못된 날짜나 zero 값의 처리도 달라질 수 있어 오류를 회피하려고 설정을 무조건 완화하지 않습니다.
INSERT INTO time_lab VALUES (2, '2026-02-30', '2026-10-13 09:00:00', NULL);
조회 결과가 예상보다 아홉 시간 차이 나면 타입 정의와 @@SESSION.time_zone을 먼저 확인합니다. 데이터 자체를 아홉 시간 더하거나 빼는 UPDATE로 맞추기 전에, 접속 설정과 프로그램의 시간대 변환이 중복 적용되고 있는지 확인하세요. SQL 모드와 날짜 검사 조건도 함께 점검합니다.
서머타임이 있는 지역에서는 +09:00 같은 고정 오프셋만으로 연중 지역 규칙을 표현할 수 없습니다. 이름 기반 시간대는 서버의 시간대 데이터와 서비스 정책이 필요하며, 이 실습은 고정 오프셋의 기본 변환만 다룹니다.
원래 시간대와 SQL 모드 복원하기
같은 연결에서 보관한 설정을 복원합니다. 전역 시간대는 변경하지 않았습니다. 접속을 끊고 새로 열었다면 아래 변수 값도 사라지므로 현재 연결 설정을 다시 확인합니다.
SET SESSION time_zone = @saved_time_zone; SET SESSION sql_mode = @saved_sql_mode; SELECT @@SESSION.time_zone, @@SESSION.sql_mode;
반복 실습 전에는 SELECT DATABASE();와 SHOW CREATE TABLE 테이블명;으로 대상이 이 강좌에서 만든 실습 테이블인지 확인합니다. 보관할 데이터가 있다면 먼저 백업합니다. 아래 명령은 지정한 테이블과 그 안의 데이터를 삭제하므로 확인한 학습용 테이블에만 실행하세요.
DROP TABLE shopcourse.time_lab;
삭제 후에는 본문의 전체 예제를 다시 실행할 수 있습니다. DROP TABLE 같은 데이터 정의 문장은 일반적인 트랜잭션의 ROLLBACK으로 복구할 수 있다고 기대하지 않습니다. 기존 products 테이블은 이 초기화 대상에 포함하지 않습니다.







