Chapter 07 · SQLite Expressions, Functions, CTEs, Window Functions, and Dialect Features

Date and Time Functions in SQLite

Work safely with SQLite time-values, date/time functions, Unix epochs, Julian days, modifiers, elapsed-time calculations, and timezone limitations.

Beginner100–120 minutesTime-value analysis labSQLite 3.53.4 baselinetimediff(): SQLite 3.43.0+MATERIALIZED hints: SQLite 3.35.0+if() alias: 3.48.0+; variadic iif(): 3.49.0+Last reviewed: August 2026

Learning outcomes

Chapter 4 separated logical dates/times from SQLite storage classes. Here we operate on those representations. SQLite's date/time functions are powerful for offsets, buckets, and elapsed calculations, but they are not a substitute for an application timezone database.

01

Use date(), time(), datetime(), julianday(), unixepoch(), strftime(), and timediff().

02

Choose modifiers that match the actual stored representation.

03

Calculate precise elapsed seconds/days separately from human-friendly timediff output.

04

Explain UTC/localtime behavior without claiming named-zone or DST-rule database support.

05

Build day buckets, expiration predicates, and elapsed-time reports.

06

Recognize lexical-order and mixed-representation traps.

SQLite accepts time-values; it does not add a sixth date storage class

The date/time functions accept ISO-8601-style text, Julian-day numbers, and—when appropriate modifiers are supplied—Unix timestamps. The underlying value is still TEXT, REAL, or INTEGER.

RepresentationExampleStrengthMain risk
ISO-8601 TEXT2026-08-12T03:15:00ZReadable; lexically sortable when normalized to one format/timezone.Mixed offsets/precision/formats can break lexical chronology.
Julian day REAL2461264.6354...Convenient for day arithmetic.Floating-point representation; unfamiliar to most APIs.
Unix epoch INTEGER1786504500Compact; easy elapsed seconds.Requires a clear unit and conversion convention.
sql · functions return ordinary SQLite storage classes
SELECT  typeof('2026-08-12 03:15:00') AS text_type,  typeof(julianday('2026-08-12 03:15:00')) AS jd_type,  typeof(unixepoch('2026-08-12 03:15:00')) AS epoch_type;

The seven core date/time functions answer different questions

sql · one time-value through several functions
SELECT  date('2026-08-12 03:15:42') AS d,  time('2026-08-12 03:15:42') AS t,  datetime('2026-08-12 03:15:42') AS dt,  julianday('2026-08-12 03:15:42') AS jd,  unixepoch('2026-08-12 03:15:42') AS epoch,  strftime('%Y-%m-%d %H:%M', '2026-08-12 03:15:42') AS formatted,  timediff('2026-08-13 05:45:42','2026-08-12 03:15:42') AS human_span;

timediff(A,B) returns a human-oriented calendar span that, when added to B, reaches A. If you need an exact number of seconds or days, subtract unixepoch() or julianday() values instead.

Modifiers form a left-to-right transformation pipeline

SQLite applies modifiers in order. This is powerful, but order is part of the query's meaning. Use explicit modifiers for Unix timestamps and normalize representation at ingestion so later queries stay simple.

sql · modifier order is semantic
SELECT  datetime(1786504500, 'unixepoch') AS from_epoch,  datetime('2026-08-12 03:15:00', '+2 hours') AS plus_two,  date('2026-08-31', '+1 month', 'floor') AS floor_example,  datetime('now','start of day','+1 day') AS next_utc_day_start;

The floor and ceiling modifiers for ambiguous month/year shifts are available in modern SQLite (3.46.0+). Record a minimum SQLite version if your production SQL depends on them.

UTC, localtime, and what SQLite does not know

SQLite date/time functions internally work from time-values and can convert between UTC and the host's local-time rules using localtime or utc modifiers. Core SQLite does not ship an IANA timezone database and does not accept named zones such as America/Toronto as a general conversion facility.

sql · host local-time conversion is environment-dependent
SELECT datetime('2026-08-12 12:00:00','localtime') AS host_local;SELECT datetime('2026-08-12 12:00:00','utc') AS interpreted_local_to_utc;

Those results depend on the machine/process environment. Do not use a development laptop's local conversion as proof that a server, container, browser, or mobile device will interpret civil-time rules identically. For scheduling across real named timezones and DST transitions, keep timezone identity in the application/domain model and use a timezone-aware library.

Production default

For event timestamps, normalized UTC storage is usually easier to compare and exchange. Convert to user-local presentation at the application boundary unless the domain genuinely requires preserving a civil time and named timezone separately.

Elapsed time: choose human-friendly or numerically exact

sql · three meanings of duration
WITH x(started_at, ended_at) AS (  VALUES ('2026-08-12 03:15:00','2026-08-12 04:45:30'))SELECT  timediff(ended_at, started_at) AS human_span,  unixepoch(ended_at)-unixepoch(started_at) AS elapsed_seconds,  round((julianday(ended_at)-julianday(started_at))*24.0, 3) AS elapsed_hoursFROM x;

The precise elapsed result here is 5,430 seconds, or 1.508 hours. timediff() is for a calendar-aware human description; it can report the same month-based span for intervals containing different numbers of days, so it is not the metric for SLA seconds.

Daily buckets and expiration predicates

sql · bucket and expiration
DROP TABLE IF EXISTS event_time_lab;CREATE TABLE event_time_lab(  event_id INTEGER PRIMARY KEY,  device_code TEXT NOT NULL,  occurred_at TEXT NOT NULL,  expires_at TEXT NOT NULL);INSERT INTO event_time_lab(device_code,occurred_at,expires_at) VALUES('PUMP-007','2026-08-12T01:15:00Z','2026-08-12T06:00:00Z'),('PUMP-007','2026-08-12T18:20:00Z','2026-08-13T06:00:00Z'),('FAN-014','2026-08-13T02:10:00Z','2026-08-13T03:00:00Z');SELECT date(occurred_at) AS utc_day, count(*) AS eventsFROM event_time_labGROUP BY date(occurred_at)ORDER BY utc_day;SELECT event_id, device_code, expires_atFROM event_time_labWHERE unixepoch(expires_at) <= unixepoch('2026-08-13T04:00:00Z')ORDER BY event_id;

The bucket works lexically/functionally because every stored timestamp uses one normalized UTC convention. If rows mix offsets, bare dates, local timestamps, and epochs, one expression cannot safely infer the intended chronology.

Lexical ordering is safe only under a representation contract

sql · text order can differ from chronological order
WITH mixed(ts) AS (  VALUES    ('2026-08-12T09:00:00+09:00'),    ('2026-08-12T01:30:00Z'))SELECT ts FROM mixed ORDER BY ts;

The strings sort by characters, not by converted instants. The +09:00 value represents 00:00Z and is earlier chronologically, yet textual ordering can place it after the 01:30Z string. Normalize before storing or compare through an explicit conversion such as unixepoch().

Time-value lab

sql · elapsed, bucket, and expiration in one report
WITH jobs(job_id,started_at,finished_at,ttl_seconds) AS (  VALUES    (1,'2026-08-12T01:00:00Z','2026-08-12T01:12:30Z',900),    (2,'2026-08-12T23:55:00Z','2026-08-13T00:25:00Z',1200),    (3,'2026-08-13T03:00:00Z',NULL,1800))SELECT job_id,       date(started_at) AS start_day,       CASE WHEN finished_at IS NULL THEN NULL            ELSE unixepoch(finished_at)-unixepoch(started_at) END AS elapsed_seconds,       datetime(started_at, printf('+%d seconds', ttl_seconds)) AS expires_at,       CASE WHEN finished_at IS NULL THEN 'running'            WHEN unixepoch(finished_at)-unixepoch(started_at) <= ttl_seconds THEN 'within-ttl'            ELSE 'late' END AS outcomeFROM jobsORDER BY job_id;

Expected outcomes are within-ttl for job 1, late for job 2, and running for job 3. The SQL makes the missing-finish rule explicit instead of silently treating NULL as zero seconds.

Date/time checkpoint

Choose the representation-aware answer.

  1. Does SQLite have a DATE storage class?
  2. When is timediff() preferable to unixepoch subtraction?
  3. What does localtime depend on?
  4. Why can ISO-like TEXT order still be chronologically wrong?
  5. What minimum version is needed for timediff()?
Review the answers

No; date/time values use ordinary TEXT/REAL/INTEGER representations. timediff() is for human calendar spans; subtract epochs for exact seconds. localtime depends on host environment rules. Mixed offsets/formats can sort lexically differently from their actual instants. timediff() was added in SQLite 3.43.0.

Summary and bridge

Time correctness comes from a representation contract first and functions second. Normalize formats, state whether timestamps are UTC or civil time, preserve named-zone identity outside core SQLite when required, and use numeric differences for precise elapsed metrics. Lesson 4 uses CTEs to organize multi-step queries and introduces safe recursive traversal.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.