Date & time functions
NOW, CURDATE, DATE_FORMAT, DATE_ADD, DATEDIFF, TIMESTAMPDIFF, EXTRACT and time zones.
Almost every application stores dates: sign-ups, orders, deadlines, logins. MySQL has a rich set of functions to get the current time, pull dates apart, format them, do date arithmetic and convert between time zones. (The date types themselves were covered in Data types.)
Sample data#
The current date and time#
(Your values will differ, of course.) NOW() and CURRENT_TIMESTAMP are the same thing and use the session time zone. NOW() returns the time the statement started, so it's the same in every row of a query. SYSDATE() returns the exact moment it's evaluated. NOW(3) adds milliseconds.
Extracting parts of a date#
Others you'll meet: MONTHNAME(), WEEKDAY() (Monday = 0), DAYOFWEEK() (Sunday = 1), DAYOFYEAR(), WEEK(), LAST_DAY() (last day of that month) and EXTRACT(unit FROM d), the standard-SQL form, e.g. EXTRACT(YEAR_MONTH FROM ordered_at) → 202601.
Formatting dates with DATE_FORMAT#
The reverse is STR_TO_DATE, which parses text in a known format. It's very useful when importing CSV files:
Date arithmetic#
Add or subtract an interval with DATE_ADD/DATE_SUB or with +/-:
Note how MySQL handles month ends: 31 January + 1 month is 28 February (2026 isn't a leap year). Units include SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER and YEAR, plus combined units like INTERVAL '1:30' HOUR_MINUTE.
Never add plain numbers to dates:
'2026-09-30' + 1converts the string to the number 2026 and returns2027!
Differences between dates#
DATEDIFF(a, b)isa − bin whole days and ignores the time part.TIMESTAMPDIFF(unit, start, end)isend − startin any unit. Note the opposite argument order.- Any arithmetic with
NULLgivesNULL, so order 3, which hasn't shipped, shows NULL.
Computing someone's age correctly (it handles birthdays that haven't happened yet this year):
Common date queries#
Orders in the last 7 days, written so that an index on ordered_at can be used:
Orders in February 2026, using a half-open range:
This form is better than WHERE MONTH(ordered_at) = 2 AND YEAR(ordered_at) = 2026: it gives the same answer, but MySQL can use an index because the column isn't wrapped in a function.
Orders per month:
Time zones#
The server has a global time zone and each connection has a session time zone:
Named zones such as 'Asia/Kolkata' or 'Europe/London' work only after the time-zone tables are loaded (mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql on Linux). Until then, CONVERT_TZ with names returns NULL. Named zones are worth loading because they handle daylight-saving time, which fixed offsets don't.
A robust convention: store UTC, convert at the edges. Have the app connect with time_zone = '+00:00', store DATETIME values in UTC, and convert to the user's zone only for display.
Unix timestamps#
Many APIs use seconds since 1970-01-01 UTC:
Both functions use the session time zone, which is +05:30 here after the SET above. That's why midnight local time gives 1767205800, and 1767225600 (midnight UTC) reads back as 05:30.
Common mistakes#
- Using
BETWEEN '2026-02-01' AND '2026-02-28'on aDATETIMEand missing the 28th after midnight. - Swapping the argument order of
DATEDIFF(a − b) andTIMESTAMPDIFF(end − start). - Storing dates as
VARCHARlike'30/09/2026'. They won't sort or compare correctly, so convert them withSTR_TO_DATEon import. - Mixing time zones: the database, the app server and users' browsers may all be in different zones. Decide on UTC storage early.
What's next#
Now you can transform single values. Next you'll summarise many rows at once with aggregate functions, GROUP BY and HAVING.
Check your understanding
Quick quiz
1.Which expression gives the date 30 days after
order_date?2.What does
DATEDIFF('2026-03-01', '2026-02-01')return?3.Which format string turns
2026-09-30into30 Sep 2026?
Finished reading?
Mark this lesson complete to track your progress.