SQL Date Functions Cheat Sheet: PostgreSQL, MySQL, BigQuery, SQL Server
Every date operation you actually write — now, truncate, add intervals, diff, format, parse, end-of-month — mapped across four dialects in one table, plus the week-numbering and timezone traps.
Quick Answer
- Truncate to a period:
DATE_TRUNC('month', d)in PostgreSQL and BigQuery; MySQL has no equivalent — useDATE_FORMAT(d, '%Y-%m-01'); SQL Server 2022+ hasDATETRUNC(month, d). - Add or subtract time: PostgreSQL uses interval arithmetic (
d - INTERVAL '1 month'), the other three use functions (DATE_ADD,DATE_SUB,DATEADD). - End of month:
LAST_DAY(d)covers MySQL and BigQuery; PostgreSQL needsDATE_TRUNC('month', d) + INTERVAL '1 month - 1 day'; SQL Server usesEOMONTH(d).
The Master Table
| Task | PostgreSQL | MySQL 8 | BigQuery | SQL Server |
|---|---|---|---|---|
| Now (timestamp) | NOW() | NOW() | CURRENT_TIMESTAMP() | SYSDATETIME() |
| Today (date) | CURRENT_DATE | CURDATE() | CURRENT_DATE() | CAST(GETDATE() AS DATE) |
| Truncate to month | DATE_TRUNC('month', d) | DATE_FORMAT(d, '%Y-%m-01') | DATE_TRUNC(d, MONTH) | DATETRUNC(month, d) (2022+) |
| Truncate to week | DATE_TRUNC('week', d) (Monday) | DATE_SUB(d, INTERVAL WEEKDAY(d) DAY) | DATE_TRUNC(d, WEEK) (Sunday) | DATETRUNC(week, d) (Sunday) |
| Add N days | d + INTERVAL '7 days' or d + 7 | DATE_ADD(d, INTERVAL 7 DAY) | DATE_ADD(d, INTERVAL 7 DAY) | DATEADD(day, 7, d) |
| Subtract 1 month | d - INTERVAL '1 month' | DATE_SUB(d, INTERVAL 1 MONTH) | DATE_SUB(d, INTERVAL 1 MONTH) | DATEADD(month, -1, d) |
| Difference in days | d2 - d1 (integer) | DATEDIFF(d2, d1) | DATE_DIFF(d2, d1, DAY) | DATEDIFF(day, d1, d2) |
| Extract year | EXTRACT(YEAR FROM d) | YEAR(d) | EXTRACT(YEAR FROM d) | YEAR(d) |
| Format as text | TO_CHAR(d, 'YYYY-MM') | DATE_FORMAT(d, '%Y-%m') | FORMAT_DATE('%Y-%m', d) | FORMAT(d, 'yyyy-MM') |
| Parse a string | TO_DATE(s, 'YYYY-MM-DD') | STR_TO_DATE(s, '%Y-%m-%d') | PARSE_DATE('%Y-%m-%d', s) | TRY_PARSE(s AS DATE) |
| End of month | DATE_TRUNC('month', d) + INTERVAL '1 month - 1 day' | LAST_DAY(d) | LAST_DAY(d) | EOMONTH(d) |
| Age in years | EXTRACT(YEAR FROM AGE(now, d)) | TIMESTAMPDIFF(YEAR, d, NOW()) | DATE_DIFF(CURRENT_DATE(), d, YEAR) | DATEDIFF(year, d, GETDATE()) |
Grouping by Week: The ISO Trap
"Revenue by week" is where date code goes wrong most often, because the four engines define week boundaries and week numbers differently. PostgreSQL's DATE_TRUNC('week', d) starts weeks on Monday; BigQuery and SQL Server start them on Sunday. If you need ISO week numbers (Monday-based, week 1 contains the first Thursday), MySQL gives them via DATE_FORMAT(d, '%x-W%v') — note the lowercase %x%v for ISO; uppercase %X%V is the Sunday-based variant. Mixing those two formats for year and week is a classic bug that splits one ISO week across two grouping buckets every January.
Adding Months: End-of-Month Drift
DATE '2026-01-31' + INTERVAL '1 month' in PostgreSQL returns 2026-02-28 — the last valid day. MySQL's DATE_ADD('2026-01-31', INTERVAL 1 MONTH) returns the same. But SQL Server's DATEADD(month, 1, '2026-01-31') also returns 2026-02-28, while DATEADD(month, 1, '2026-01-30') returns 2026-02-28 too (it clamps, never overflows). The trap runs the other direction as well: repeated "+1 month" on a clamped date (Jan 31 → Feb 28 → Mar 28) drifts away from the true month-end. For subscription and billing queries, always compute from an anchor date like the first of month, not by iterating.
Timezones
PostgreSQL stores timestamptz in UTC and renders in the session timezone: created_at AT TIME ZONE 'America/New_York' converts explicitly. MySQL's TIMESTAMP auto-converts using the session time_zone, but DATETIME does not convert at all — half the "my dates are off by an hour" reports trace to this. For date-bucket reporting, the safe pattern in every engine is: store UTC, convert once at query time, and group by the converted value — never group by UTC day and label it with a local timezone.
Filling Date Gaps
Date functions get you the axis; the missing-days problem is separate — generating a complete date spine and LEFT JOINing your data onto it. We covered that with dialect-specific recipes here: Find missing dates in SQL.
Try It Yourself
Describe the date logic in plain English — "monthly revenue for the last 6 months in MySQL" — and our free SQL generator writes the dialect-correct functions for you.