Month boundaries — for billing periods, monthly buckets, and “month to date”.
First day of the month
SELECT DATE_FORMAT(d, '%Y-%m-01'); -- MySQL
SELECT DATE_TRUNC('month', d)::date; -- PostgreSQL / DuckDB
SELECT DATE_TRUNC(d, MONTH); -- BigQuery
SELECT DATE_TRUNC('month', d)::date; -- Snowflake
SELECT DATE(d, 'start of month'); -- SQLite
Last day of the month
-- Built-in LAST_DAY (MySQL, BigQuery, Snowflake, DuckDB)
SELECT LAST_DAY(d);
-- PostgreSQL (first of next month minus a day)
SELECT (DATE_TRUNC('month', d) + INTERVAL '1 month - 1 day')::date;
-- SQLite
SELECT DATE(d, 'start of month', '+1 month', '-1 day');
Days in the month
SELECT DAY(LAST_DAY(d)); -- MySQL / Snowflake / DuckDB
SELECT EXTRACT(DAY FROM (DATE_TRUNC('month', d) + INTERVAL '1 month - 1 day')); -- Postgres
Gotchas: Postgres has no LAST_DAY before v15 — the “next month minus a day” form works
everywhere and is the portable fallback. DATE_TRUNC('month', d) returns a timestamp in some
engines; cast to ::date if you want a pure date. LAST_DAY correctly handles February in leap
years, so prefer it where available.