SQL example

How to get only the date from a datetime in SQL

8 min read Updated Oct 2026 Runs in an isolated runtime
Quick answer

In SQLite, use date(placed_at): it turns '2026-03-14 09:30:00' into '2026-03-14'. PostgreSQL, MySQL and SQL Server use the standard CAST(placed_at AS date) (SQL Server also has CONVERT(date, placed_at)). To filter one day, compare a range, placed_at >= '2026-03-14' AND placed_at < '2026-03-15', rather than wrapping the column in a function.

A datetime column holds a day and a time of day together, and most reports only care about the day: show it without the clock, count orders per day, or find everything that happened on 14 March. Every database can cut the time off, but the function differs, and the obvious WHERE placed_at = '2026-03-14' matches nothing. The examples run on SQLite, which stores dates as text; section 6 gives the PostgreSQL, MySQL and SQL Server forms. Each example runs on this page: hit Run, then edit the query and run it again.

1date(): keep only the dayRecommended

SQLite has no separate date or datetime type. A timestamp is usually stored as ISO-8601 text, 'YYYY-MM-DD HH:MM:SS', and the date functions parse that text. date(x) returns the 'YYYY-MM-DD' part. It accepts a T separator, a trailing Z and missing seconds, and with the 'unixepoch' modifier it reads a Unix timestamp stored as a number.

date-only.sql

Output

All four formats come back as a plain day. The second query shows the edges: typeof reports text, because SQLite has no date type to return, and a day-first string such as '14/03/2026 09:30' gives NULL rather than an error. A column full of NULLs from date() usually means the dates are stored in a format SQLite doesn't parse. The Unix timestamp 1773480600 is 2026-03-14 09:30:00 UTC, so it lands on the same day. Because the result is year-month-day text, it sorts and compares correctly as a string.

2CAST(x AS DATE): the standard form, and why SQLite gets it wrong

In PostgreSQL, MySQL and SQL Server, CAST(placed_at AS date) is the usual answer, and it returns a real date. SQLite accepts the same syntax without complaint, but DATE isn't a type there. SQLite gives an unknown type name numeric affinity, so the cast reads the number at the start of the text and stops at the first -.

cast-pitfall.sql

Output

The cast returns 2026, an integer: only the year survives, and every row from 2026 looks the same. There is no error to warn you, so a query copied from a SQL Server or PostgreSQL answer silently groups a whole year into one row. In SQLite, use date(); everywhere else, CAST(x AS date) is the portable choice.

3Filter the rows from one day

WHERE placed_at = '2026-03-14' compares a full timestamp with a bare date, and they are never equal. WHERE date(placed_at) = '2026-03-14' works, but the database has to compute date() for every row before it can compare, so an index on placed_at goes unused. A half-open range on the raw column gives the same rows and lets the index do the work. EXPLAIN QUERY PLAN shows the difference.

one-day.sql

Output

The equality test counts 0 rows. The range keeps rows 2 to 4, from exactly midnight to one second before the next midnight, and drops the rows from the 13th and the 15th. The two plans are the point: filtering on date(placed_at) gives SCAN orders, a read of the whole table, while the range gives SEARCH ... USING COVERING INDEX, a jump straight to the matching index entries. On five rows it makes no difference; on millions it does, and the same rule holds for CAST(x AS date) or DATE(x) in other databases. The upper bound is < the next day, not <= '2026-03-14 23:59:59', so fractional seconds such as 23:59:59.5 can't slip through.

4Group by day or by month

To count or sum per day, group by the date expression. For months, strftime formats the timestamp: '%Y-%m' gives '2026-03', and '%Y' gives the year. If you'd rather have a real day, the 'start of month' modifier turns any timestamp into the first of its month.

group-by-day.sql

Output

The two orders on 14 March fold into one row with a total of 54, and the month query puts all three March orders together. Days with no orders don't appear at all; filling the gaps needs a calendar of days to join against. SQLite, MySQL and PostgreSQL let GROUP BY use a column alias such as day; SQL Server doesn't, so there you repeat the expression, GROUP BY CAST(placed_at AS date).

5Get the time part instead

The other half works the same way. time(x) returns 'HH:MM:SS', strftime picks any parts you name, and casting strftime('%H', x) to INTEGER gives the hour as a number for arithmetic or bucketing. Because the time is zero-padded text, comparing it with '12:00:00' works too.

time-part.sql

Output

strftime('%H', ...) returns text such as '09'; the cast turns it into the number 9. The noon filter keeps orders 1 and 3, from two different days. In PostgreSQL, MySQL and SQL Server, CAST(x AS time) returns the time part as a time value.

6Other databases

The same jobs in PostgreSQL, MySQL and SQL Server. Each form below was run on PostgreSQL 18, MySQL 8.4 and SQL Server's T-SQL (through Babelfish) with the timestamp 2026-03-14 09:30:00. Oracle's DATE type keeps a time of day, so there the usual answer is TRUNC(x), which sets the time to midnight.

SyntaxDatabaseNotes
CAST(x AS date)PostgreSQL, MySQL, SQL ServerStandard SQL; returns a date (2026-03-14). In SQLite it returns 2026
DATE(x)MySQL, SQLiteMySQL returns a DATE; SQLite returns the text 2026-03-14
CONVERT(date, x)SQL ServerSame result as CAST; the date type exists since SQL Server 2008
DATEADD(day, DATEDIFF(day, 0, x), 0)SQL ServerThe pre-2008 idiom: keeps datetime, time set to 00:00:00
DATETRUNC(day, x)SQL ServerSQL Server 2022+; stays a datetime, time set to 00:00:00
x::datePostgreSQLShorthand for CAST(x AS date)
date_trunc('day', x)PostgreSQLStays a timestamp: 2026-03-14 00:00:00
strftime('%Y-%m', x)SQLiteMonth as text, 2026-03, for GROUP BY
DATE_FORMAT(x, '%Y-%m')MySQLMonth as text, 2026-03
date_trunc('month', x)PostgreSQLFirst of the month: 2026-03-01 00:00:00
FORMAT(x, 'yyyy-MM')SQL ServerMonth as text, 2026-03; DATETRUNC(month, x) on 2022+
CAST(x AS time)PostgreSQL, MySQL, SQL ServerThe time part, 09:30:00; SQLite uses time(x)

Frequently asked questions

How do I get only the date from a datetime in SQL Server?

Use CAST(x AS date) or CONVERT(date, x); both return a date, so 2026-03-14 09:30:00 becomes 2026-03-14. For today, CAST(GETDATE() AS date). On SQL Server 2005 and older, which have no date type, the old idiom DATEADD(day, DATEDIFF(day, 0, x), 0) keeps a datetime with the time set to midnight.

Why does CAST(x AS DATE) return 2026 in SQLite?

SQLite has no DATE type. It treats the unknown type name as numeric, so CAST('2026-03-14 09:30:00' AS DATE) reads the leading number and returns the integer 2026, without an error. Use date(x), which returns the text 2026-03-14.

Why does WHERE datetime_column = '2026-03-14' return no rows?

The column holds a time as well, so 2026-03-14 09:30:00 is not equal to 2026-03-14. Compare a range instead: WHERE placed_at >= '2026-03-14' AND placed_at < '2026-03-15'. It returns every row of that day and can use an index on the column. WHERE date(placed_at) = '2026-03-14' also works, but it scans the whole table.

Should I use BETWEEN to select one day?

Not with dates alone. placed_at BETWEEN '2026-03-14' AND '2026-03-14' matches nothing, because the upper bound is midnight at the start of the day, and BETWEEN '2026-03-14' AND '2026-03-15' includes a row at exactly 2026-03-15 00:00:00, since BETWEEN includes both ends. The half-open range >= day AND < next day has neither problem.

How do I group by month or year in SQL?

Group by an expression that drops the smaller parts. In SQLite, GROUP BY strftime('%Y-%m', placed_at) for months and strftime('%Y', placed_at) for years. In PostgreSQL, date_trunc('month', placed_at); in MySQL, DATE_FORMAT(placed_at, '%Y-%m'); in SQL Server, FORMAT(placed_at, 'yyyy-MM'), or DATETRUNC(month, placed_at) on 2022 and later.

Does SQLite date() handle time zones?

It converts a timestamp with an offset to UTC before taking the date. date('2026-03-15 01:00:00+02:00') returns 2026-03-14, because that moment is 23:00 on the 14th in UTC. If you want the local date as written, take the first ten characters with substr(x, 1, 10), which gives 2026-03-15.

Run it yourself

Open any of these in the full SQLite editor to change the data, run it and share it.

SQLite playground