Menu

SQLite 날짜/시간 함수: strftime, date(), datetime() 정리

SQLite는 날짜를 어떻게 저장하고 다룰까? 5가지 날짜 함수와 포맷 문자열, 수정자(modifier), 그리고 쿼리 성능을 좌우하는 저장 방식까지 한 번에 정리합니다.

이 페이지에는 실행 가능한 에디터가 있습니다 — 편집하고 실행하면 결과를 바로 볼 수 있습니다.

SQLite에는 사실 날짜 타입이 없습니다

Postgres나 MySQL에서 넘어온 사람이라면 거의 다 여기서 한 번씩 당황합니다. SQLite의 저장 클래스(storage class)는 NULL, INTEGER, REAL, TEXT, BLOB 이 다섯 개가 전부입니다. DATE도, DATETIME도, TIMESTAMP도 없어요. CREATE TABLE 문에 created_at DATETIME이라고 써도 SQLite는 군말 없이 받아주지만, 실제로는 그냥 텍스트나 숫자로 저장합니다.

대신 SQLite는 다음 세 가지 관례적인 포맷을 인식하는 날짜 함수들을 제공합니다.

  • ISO 8601 텍스트'2026-04-23', '2026-04-23 10:15:00', '2026-04-23T10:15:00.123Z' 같은 형식.
  • Unix timestamp — 1970-01-01 UTC를 기준으로 한 초 단위 정수값.
  • Julian day number(율리우스일) — 기원전 4714년부터 흐른 일수를 소수까지 표현한 실수값.

이 중 하나를 골라서 일관되게 쓰는 게 핵심입니다. 보통은 ISO 8601 텍스트가 가장 무난한데, 사람이 읽기 좋고 문자열 정렬을 해도 시간 순서가 맞기 때문입니다. 그래서 사실상의 기본값처럼 쓰입니다.

"지금 이 순간"을 표현하는 네 가지 방법 — 텍스트 날짜, 텍스트 datetime, Unix 초, Julian day. 결국 모두 같은 시점을 가리킵니다.

SQLite의 다섯 가지 날짜 함수

SQLite는 날짜·시간 처리에 필요한 거의 모든 작업을 다섯 개의 내장 함수로 해결합니다.

  • date(time, ...)YYYY-MM-DD 형식으로 반환합니다.
  • time(time, ...)HH:MM:SS 형식으로 반환합니다.
  • datetime(time, ...)YYYY-MM-DD HH:MM:SS 형식으로 반환합니다.
  • julianday(time, ...) — 실수(real)로 반환하며, 날짜 차이 계산에 안성맞춤입니다.
  • strftime(format, time, ...) — 원하는 형식으로 자유롭게 포맷팅한 문자열을 반환합니다.

모든 함수는 첫 번째 인자로 시간 값을 받고, 그 뒤에 수정자(modifier) 문자열을 원하는 만큼 이어서 붙일 수 있습니다.

'unixepoch'을 꼭 기억하세요. 이 인자가 있어야 SQLite가 입력 정수를 율리우스일이 아닌 Unix timestamp로 인식합니다. 빠뜨리면 그 숫자를 율리우스일(Julian day)로 간주해 버립니다.

strftime: 원하는 포맷으로 sqlite 날짜 출력하기

날짜 포맷팅의 핵심은 단연 strftime입니다. C나 Python에서 보던 그 % 포맷 코드를 그대로 쓸 수 있습니다:

자주 쓰게 되는 포맷 코드는 다음과 같습니다:

  • %Y — 네 자리 연도.
  • %m — 월 (01-12).
  • %d — 일 (01-31).
  • %H, %M, %S — 시, 분, 초.
  • %w — 요일 (0=일요일).
  • %j — 연중 며칠째인지 (001-366).
  • %s — Unix 타임스탬프.
  • %f — 소수점 단위 초 (SS.SSS).

strftime은 날짜에서 특정 부분만 뽑아낼 때도 사용합니다. SQLite에는 별도의 EXTRACTYEAR() 함수가 없거든요. 원하는 부분만 포맷으로 잘라낸 뒤, 숫자가 필요하면 캐스팅해 주면 됩니다:

strftime는 항상 텍스트를 반환하기 때문에, 숫자로 연산하거나 비교하려면 CAST(... AS INTEGER)로 감싸 주세요.

수정자(Modifier): 연산자 없이 sqlite 날짜 더하기

SQLite의 날짜 처리가 편하게 느껴지는 건 바로 이 기능 덕분입니다. 시간 인자 뒤에 수정자(modifier) 문자열을 원하는 만큼 붙이면, 적힌 순서대로 차례차례 적용됩니다:

자주 쓰게 될 modifier들을 정리하면 다음과 같습니다.

  • '+N days', '-N days'hours, minutes, seconds, months, years도 같은 방식으로 사용 가능합니다.
  • 'start of day', 'start of month', 'start of year' — 해당 경계로 잘라냅니다.
  • 'weekday N' — 다음에 오는 특정 요일로 이동합니다 (0=일요일).
  • 'localtime', 'utc' — 시간대를 서로 변환합니다.

"이번 달 마지막 날" 구하는 트릭은 꼭 외워두세요. start of month → 한 달 더하기 → 하루 빼기 순서면 됩니다. SQLite에는 LAST_DAY 함수가 따로 없지만, 이렇게 modifier를 체이닝하면 같은 결과를 얻을 수 있습니다.

UTC와 로컬 시간

'now'는 항상 UTC를 반환합니다. 로컬 시간이 필요하다면 명시적으로 요청해야 합니다.

'localtime' 수정자는 UTC 값을 시스템의 로컬 시간대로 바꿔주고, 'utc' 수정자는 그 반대로 동작합니다. 입력을 로컬 시간으로 간주해서 UTC로 변환해 주는 거죠.

안전한 습관 하나를 추천드리면, 저장은 무조건 UTC로 하고 화면에 보여줄 때만 로컬로 변환하는 겁니다. 저장 단계에서 시간대를 섞어 쓰면, 일년에 두 번 서머타임이 바뀔 때마다 골치 아픈 버그가 튀어나옵니다.

SQLite 날짜 비교와 기간 필터링

날짜를 ISO 8601 형식의 텍스트로 저장해 두면 비교 연산이나 BETWEEN 구문이 그냥 자연스럽게 동작합니다. ISO 8601은 사전순으로 정렬해도 시간순 정렬과 결과가 똑같거든요. SQLite가 이 형식을 기본으로 쓰는 이유도 바로 여기에 있습니다.

반열림 구간(>= start, < end)을 쓰는 습관은 꽤 유용합니다. "30일 자정이 포함된 거야, 안 된 거야?" 같은 헷갈리는 상황을 아예 만들지 않거든요.

"최근 7일"을 구할 때는 경계값 계산을 SQLite에 맡기면 됩니다:

날짜 차이 계산하기

SQLite에는 DATEDIFF 함수가 없다. 하지만 아래 두 가지 패턴만 알면 어떤 경우든 처리할 수 있다.

julianday()로 빼면 결과가 일(day) 단위로 나오고 소수점까지 나오기 때문에, 24를 곱하면 시간, 1440을 곱하면 분이 됩니다. 반면 strftime('%s', ...)로 빼면 초 단위 정수로 바로 떨어져서 정수 값이 필요할 때 편리합니다.

일수를 정수로만 보고 싶다면 CAST(... AS INTEGER)로 소수점 이하를 잘라내면 됩니다:

날짜 저장: 한 가지 포맷을 정해서 끝까지 밀고 가자

쓸 만한 선택지는 세 가지인데, 추천 순서대로 정리하면 이렇다:

  1. ISO 8601 문자열 (TEXT). 덤프 떴을 때 사람이 읽기 좋고, 정렬도 제대로 되고, sqlite 날짜 함수 전부와 잘 맞물린다. 별 고민 없으면 이걸 써라.
  2. Unix 초 단위 timestamp (INTEGER). 용량이 작고 비교 연산이 빠르며, 타임존 때문에 헷갈릴 일도 없다. 수백만 건 단위로 쌓이는 테이블이라면 이쪽이 낫다. 다시 읽을 때는 datetime(col, 'unixepoch')으로 변환해줘야 한다.
  3. 율리우스 일자(julianday) (REAL). 날짜 산술 연산을 빡세게 돌리거나 한 컬럼에 밀리초 이하 정밀도까지 담아야 하는 게 아니라면 굳이 쓸 이유가 없다.

절대 하지 말아야 할 건 한 컬럼에 여러 포맷을 섞어 넣는 거다. 날짜 함수들은 어느 쪽이든 군말 없이 받아주지만, 인덱스나 정렬, 비교 결과는 완전히 엉망이 된다.

DEFAULT (datetime('now'))는 SQLite에서 DEFAULT CURRENT_TIMESTAMP와 같은 역할을 합니다. 애플리케이션 쪽에서 별도로 처리하지 않아도 새 행이 추가될 때마다 현재 UTC 시간이 자동으로 기록되죠.

기간별로 그룹핑하기

월별, 주별, 시간대별로 데이터를 묶어서 보고 싶을 때 strftime 함수가 진가를 발휘합니다:

"시간대별 주문 수", "요일별 가입자 수", "분 단위 이벤트 수" 같은 통계도 모두 같은 방식이에요. 원하는 단위만 추려내는 포맷 문자열을 골라서 GROUP BY로 묶고 집계하면 끝입니다.

다음 주제: 집계 함수

그룹화 얘기가 나온 김에, 방금 사용한 COUNT(*)는 SQLite 집계 함수 중 가장 기본적인 것입니다. 다음 글에서는 SUM, AVG, MIN, MAX 같은 나머지 집계 함수들을 살펴보고, 여러 행을 하나의 요약 값으로 압축하는 방법을 알아보겠습니다.

자주 묻는 질문

SQLite에 DATE나 DATETIME 타입이 따로 있나요?

없습니다. SQLite에는 전용 날짜 타입이 없어서 ISO 8601 형식의 TEXT('2026-04-23 10:15:00'), Unix timestamp INTEGER, 또는 Julian day REAL 중 하나로 저장합니다. 내장 날짜 함수들은 이 세 가지 형식을 모두 인식하고, 기본적으로 ISO 8601 텍스트를 반환합니다.

현재 날짜와 시간은 어떻게 가져오나요?

현재 날짜는 date('now'), 시간은 time('now'), 둘 다 필요하면 datetime('now'), Unix timestamp가 필요하면 strftime('%s', 'now')를 씁니다. 기본값은 UTC라는 점을 꼭 기억하세요. 한국 시간(로컬 시간)으로 보고 싶다면 datetime('now', 'localtime')처럼 'localtime' modifier를 추가하면 됩니다.

날짜에 며칠 또는 몇 달을 더하려면 어떻게 하나요?

날짜 함수의 인자로 modifier 문자열을 넘겨주면 됩니다. 예: date('2026-04-23', '+7 days'), date('now', '-1 month'), datetime('now', '+2 hours', '+30 minutes'). modifier는 작성한 순서대로 적용되며, days, hours, minutes, seconds, months, years 단위를 지원합니다.

두 날짜의 차이는 어떻게 계산하나요?

일(day) 단위 차이는 julianday(end) - julianday(start)를 쓰면 됩니다. Julian day는 실수형이라 소수점 단위까지 나옵니다. 초 단위라면 Unix timestamp를 빼면 됩니다: strftime('%s', end) - strftime('%s', start). SQLite에는 DATEDIFF 함수가 없지만, 이 두 패턴이면 거의 모든 경우를 커버할 수 있습니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기