Dates & Times in SQLite: strftime, date() & julianday()

SQL & Querying
26 / 47

SQLite stores timestamps as ISO-8601 text, so they sort correctly as strings. strftime() pulls parts out, date() shifts them, and julianday() turns two timestamps into a number you can subtract.

The Analogy (Read This First)

A timestamp is like a postal address written biggest-to-smallest: country, city, street. Because the biggest unit comes first, alphabetical order is also chronological order - and cutting out just the city (the month) is a matter of reading one section.

Deep Dive Analysis

There is no separate date type in SQLite. Timestamps are text in ISO-8601 form, '2024-03-15 23:40:00'. Because the biggest unit comes first, plain text comparison orders them chronologically: WHERE created_at >= '2024-03-01' AND created_at < '2024-04-01' selects March.

Pulling parts out. strftime(format, value) formats a timestamp: %Y year, %m month, %d day, %H hour (00-23), %M minute and %w weekday (0 = Sunday). The result is text, so compare it with quotes: strftime('%H', created_at) >= '22' finds activity from 10 pm onward, the classic after-hours filter.

Grouping by a period is just strftime inside GROUP BY. Count or sum per month, day or hour and the shape of the data (including a spike) shows up immediately:

SELECT strftime('%Y-%m', created_at) AS month,
       SUM(amount)                    AS revenue
FROM orders
GROUP BY strftime('%Y-%m', created_at)
ORDER BY month;

Date arithmetic. date(created_at, '+7 days') shifts a date, and the modifiers include '-1 month', 'start of month' and, with datetime(), '+2 hours'. date('now') is today in UTC.

The gap between two timestamps. julianday() converts a timestamp into a day count, so subtracting two of them gives the elapsed days (the fraction is the time of day). Multiply by 24 for hours:

SELECT id,
       (julianday(resolved_at) - julianday(opened_at)) * 24 AS hours_open
FROM tickets
WHERE resolved_at IS NOT NULL;

Pitfall: a date is not the whole day. created_at <= '2024-03-15' excludes everything on the 15th after midnight, because the text '2024-03-15 08:00:00' sorts after '2024-03-15'. To mean 'through the 15th', write created_at < '2024-03-16'.

Be consistent about time zones. SQLite stores exactly the string you gave it and 'now' is UTC, so a mix of local and UTC timestamps produces gaps that look like real events. Convert once, at the edge.

Key Bullets
  • Timestamps are ISO text: string order equals chronological order.
  • strftime('%Y-%m', ts) extracts a month; use it in SELECT and GROUP BY to bucket by period.
  • julianday(a) - julianday(b) is the elapsed time in days; multiply by 24 for hours.
  • Use < next-day (not <= date) to mean 'through that whole day'.
  • strftime returns text - compare against quoted strings.
Real-World Examples
After-hours activity: WHERE strftime('%H', ts) >= '22'Monthly trend: GROUP BY strftime('%Y-%m', ts)Time to resolve a ticket: julianday(closed) - julianday(opened)

Curated Curation & Deep Insights

ScaleDojo Certified
Video Tutorial Pending

Our system architects are vetting high-quality, authorized video guides for Dates & Times in SQLite: strftime, date() & julianday() with zero third-party platform links.

Verification in Progress
Reading Material Pending

We are preparing premium, zero-competitor deep-dives for Dates & Times in SQLite: strftime, date() & julianday(). Authorized reading references will appear automatically.

Verification in Progress