Agentsql

BigQuery Date Functions: Format, Add, Subtract, and Group by Date

Marcus Feld, Analytics·Jul 21, 2026·8 min read

The BigQuery date functions you actually use: format a date, add and subtract days, measure the gap between two dates, and group a trend by day, week or month.

Connected · demo_shop · Postgres · read‑only

Ask your data a question:

›_

Writing SQL… Running (read‑only)… SQL Agentsql wrote

Refine: refined ✓

Click a question. Agentsql writes the SQL, runs it read-only, and answers.

BigQuery date functions let you format a date, do arithmetic on it, and group rows by day, week or month. The ones you reach for daily are CURRENT_DATE(), FORMAT_DATE(), DATE_ADD() and DATE_SUB(), DATE_DIFF(), DATE_TRUNC() and EXTRACT(). This is a working reference for each, with the exact syntax and the mistakes that cost people an afternoon, plus how the DATE, DATETIME and TIMESTAMP types differ so you call the right function family.

The functions you will use most

FunctionWhat it doesExample
CURRENT_DATE()Today's dateCURRENT_DATE()
DATE_ADD(d, INTERVAL n unit)Add days, months, yearsDATE_ADD(d, INTERVAL 7 DAY)
DATE_SUB(d, INTERVAL n unit)Subtract a periodDATE_SUB(d, INTERVAL 1 MONTH)
DATE_DIFF(a, b, unit)Gap between two datesDATE_DIFF(a, b, DAY)
DATE_TRUNC(d, unit)Round down to period startDATE_TRUNC(d, MONTH)
EXTRACT(part FROM d)Pull out year, month, dayofweekEXTRACT(YEAR FROM d)
FORMAT_DATE(fmt, d)Date to a formatted stringFORMAT_DATE('%Y-%m', d)
PARSE_DATE(fmt, s)String to a real DATEPARSE_DATE('%Y%m%d', s)

Formatting a date as a string

Use FORMAT_DATE() to turn a DATE into text in whatever layout you need. The format elements follow the standard strftime codes.

SELECT
  FORMAT_DATE('%Y-%m-%d', order_date) AS iso,      -- 2026-07-21
  FORMAT_DATE('%Y%m%d', order_date)   AS yyyymmdd, -- 20260721
  FORMAT_DATE('%Y-%m', order_date)    AS year_month -- 2026-07
FROM sales.orders

The %Y%m%d form (no separators) is the one people search for most, usually because a downstream system or filename wants a compact 20260721. Remember the result is a STRING, so do not try to do date math on it afterwards. Format at the very end, once the arithmetic is done.

Adding and subtracting time

DATE_ADD and DATE_SUB take an INTERVAL and a unit (DAY, WEEK, MONTH, QUARTER, YEAR). They are the safe way to shift a date, because they handle month lengths and leap years for you.

-- orders in the last 30 days
SELECT order_id
FROM sales.orders
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)

Do not build date windows by hand with string math or by adding raw numbers to a date. DATE_ADD(DATE '2026-01-31', INTERVAL 1 MONTH) correctly returns 2026-02-28, which no naive arithmetic gets right.

Measuring the gap between two dates

DATE_DIFF(later, earlier, unit) returns the whole-unit difference. The argument order is later date first, and getting it backwards gives you a negative number, which is the single most common DATE_DIFF bug.

SELECT
  customer_id,
  DATE_DIFF(CURRENT_DATE(), signup_date, DAY) AS days_since_signup
FROM customers

Note that DATE_DIFF counts boundary crossings, not elapsed time. DATE_DIFF('2026-01-01', '2025-12-31', DAY) is 1, and by MONTH two dates one day apart across a month boundary return 1 as well. If you need true elapsed days, DAY is what you want; if you need calendar-month counts, expect boundary behavior.

Grouping a trend by day, week, or month

This is the reason date functions exist for most analysts: turning per-row timestamps into a time series. DATE_TRUNC() rounds each date down to the start of its period, so all of January collapses to 2026-01-01 and groups cleanly.

SELECT
  DATE_TRUNC(order_date, MONTH) AS month,
  COUNT(*) AS orders,
  SUM(total) AS revenue
FROM sales.orders
GROUP BY month
ORDER BY month

Truncate rather than FORMAT_DATE for grouping, because DATE_TRUNC keeps the result a real DATE that sorts chronologically. If you group by a '%Y-%m' string instead, December 2025 and January 2026 still sort correctly, but any format that leads with the month sorts wrong. Truncate to group and sort, format only for display at the end.

DATE vs DATETIME vs TIMESTAMP

BigQuery has three date and time types, and the function you call has to match, or you get a type error.

TypeHoldsFunction prefix
DATECalendar date, no timeDATE_*
DATETIMEDate and time, no zoneDATETIME_*
TIMESTAMPAbsolute instant, UTC-basedTIMESTAMP_*

TIMESTAMP is the tricky one because it is zone-aware. TIMESTAMP_TRUNC(ts, DAY) truncates in UTC unless you pass a time zone as a third argument, so TIMESTAMP_TRUNC(ts, DAY, 'America/New_York') is what you want when a "day" should mean a US calendar day rather than a UTC one. Grouping revenue by day without the time zone is a classic cause of numbers that are slightly off near midnight.

Ask it in plain English instead

You do not have to memorize which of the DATE, DATETIME and TIMESTAMP functions to call. Agentsql connects read-only to BigQuery, turns a plain-English question like "monthly revenue for the last year" into the correct Standard SQL, picks the right DATE_TRUNC and time zone, runs it, and shows you the query so you can check it. See the BigQuery integration for how it connects and the SQL query generator for how the translation works, then see how it works and ask BigQuery a question in plain English.

See Agentsql write and run the SQL live.

Ask a question in plain English, watch the query appear, and get a chart and an answer with the SQL shown. Then point Agentsql at your own database.

See how it works

Ask your data in plain English.