Complete reference for MariaDB date and time functions covering NOW, DATE_FORMAT, DATEDIFF, TIMESTAMPDIFF, STR_TO_DATE, FROM_UNIXTIME, CONVERT_TZ, and 57+ more. Every function includes syntax, format specifiers, MySQL compatibility notes, and timezone examples. Updated for MariaDB 11.x.
MariaDB date and time functions provide comprehensive tools for date arithmetic, formatting, and timezone conversion. DATE_FORMAT() formats a datetime value using a format string with % specifiers (similar to C strftime), DATEDIFF() returns the number of days between two dates, and TIMESTAMPDIFF() computes the difference in any specified unit (SECOND, MINUTE, HOUR, DAY, MONTH, YEAR). MariaDB date functions are broadly compatible with MySQL but differ significantly from PostgreSQL date functions in syntax.
NOW
MariaDB 5.5+→ DATETIME
Returns the current date and time as a DATETIME value.
Signature
NOW([fsp])
Parameters
Parameter
Type
Description
fsp
INT
Fractional seconds precision 0-6 (optional)
Examples
Current datetime
sql
SELECTNOW();
→-- 2024-03-15 10:30:00
With milliseconds
sql
SELECTNOW(3);
→-- 2024-03-15 10:30:00.123
Extract date part
sql
SELECTDATE(NOW());
→-- 2024-03-15
Comparison
sql
SELECTNOW() >'2024-01-01';
→-- 1
Audit column
sql
INSERTINTOlogs (created_at) VALUES (NOW());
→-- inserted
⚠Anti-Pattern— Using SYSDATE() in replication
SYSDATE() is non-deterministic and causes unsafe replication in statement-based mode.
✓ Instead: Use NOW() for replication-safe queries.
NOW() returns the same timestamp for every row in a single statement, ensuring consistency across an entire INSERT or UPDATE batch. This makes it safe for replication and auditing. For long-running procedures where you need wall-clock drift, use SYSDATE() instead.
Returns the actual current date and time at the moment of execution, not statement start.
Signature
SYSDATE([fsp])
Parameters
Parameter
Type
Description
fsp
INT
Fractional seconds precision 0-6 (optional)
Examples
Wall-clock time
sql
SELECTSYSDATE();
→-- 2024-03-15 10:30:00
With microseconds
sql
SELECTSYSDATE(3);
→-- 2024-03-15 10:30:00.456
Diverges from NOW()
sql
SELECTNOW(), SLEEP(1), SYSDATE();
→-- NOW and SYSDATE differ by 1s
Arithmetic
sql
SELECTSYSDATE() -INTERVAL1DAY;
→-- 2024-03-14 10:30:00
Extract date
sql
SELECTDATE(SYSDATE());
→-- 2024-03-15
⚠Anti-Pattern— SYSDATE() in statement-based replication
SYSDATE() produces different values on replica, causing data drift in statement-based replication.
✓ Instead: Prefer NOW() unless you specifically need intra-statement time differences.
SYSDATE() re-evaluates on every call, so SLEEP(1) between two SYSDATE() calls returns different values. Use it only when you intentionally need to capture elapsed time within a procedure. Avoid it in statement-based replication setups.
example
SELECTNOW(), SLEEP(2), SYSDATE();
→-- NOW and SYSDATE differ by 2 seconds
◆MariaDB Note
SYSDATE() re-evaluates on every call. Can be made equivalent to NOW() with the --sysdate-is-now server option.
⇄ vs MySQL: Identical to MySQL. MariaDB added --sysdate-is-now option same as MySQL.
⇄ vs PostgreSQL: PostgreSQL has clock_timestamp() for the equivalent live wall-clock behavior.
⚠Anti-Pattern— Comparing CURDATE() to a DATETIME column without cast
CURDATE() = datetime_col misses rows after midnight due to implicit type conversion.
✓ Instead: Use DATE(datetime_col) = CURDATE() or a range condition.
CURDATE() is the cheapest current-date function because it returns a DATE with no time component. For DATETIME columns, wrap the column in DATE() or use a range: WHERE ts >= CURDATE() AND ts < CURDATE() + INTERVAL 1 DAY — the range form allows index use.
example
SELECT*FROMordersWHEREorder_date=CURDATE();
→-- all orders placed today
◆MariaDB Note
Equivalent to CURRENT_DATE. Standard SQL function.
SQL-standard synonym for CURDATE(). Returns the current date as 'YYYY-MM-DD'.
Signatures
CURRENT_DATE
CURRENT_DATE()
Examples
No parentheses needed
sql
SELECTCURRENT_DATE;
→-- 2024-03-15
Parentheses also valid
sql
SELECTCURRENT_DATE();
→-- 2024-03-15
Add a month
sql
SELECTCURRENT_DATE+INTERVAL1MONTH;
→-- 2024-04-15
Upcoming rows
sql
SELECT*FROMeventsWHEREevent_date>=CURRENT_DATE;
→-- future events
Extract month
sql
SELECTMONTH(CURRENT_DATE);
→-- 3
⚠Anti-Pattern— Mixing CURRENT_DATE with DATETIME comparisons
CURRENT_DATE has no time part; direct equality with DATETIME columns can miss records.
✓ Instead: Wrap the DATETIME column with DATE() before comparing.
Prefer CURRENT_DATE (no parentheses) in SQL scripts that may be run on other databases such as PostgreSQL. It is also accepted with parentheses in MariaDB for MySQL compatibility.
⚠Anti-Pattern— Using CURTIME() for duration math across midnight
CURTIME() wraps at midnight; subtracting times can yield negative results unexpectedly.
✓ Instead: Use TIMEDIFF() or full DATETIME arithmetic with NOW().
CURTIME() returns the current time as TIME. With the optional fsp argument (0-6), it includes fractional seconds. Useful for logging query durations when stored in TIME columns rather than full DATETIME.
example
SELECTCURTIME(6) ASstart_time;
→-- '10:30:00.123456'
◆MariaDB Note
Equivalent to CURRENT_TIME. Fractional seconds (fsp 0-6) supported in MariaDB 5.3+.
CURRENT_TIMESTAMP is fixed at statement start; it does not advance during a long transaction.
✓ Instead: Use SYSDATE() if you need live wall-clock time within a stored procedure loop.
CURRENT_TIMESTAMP (or CURRENT_TIMESTAMP()) is the SQL-standard name and is also accepted in PostgreSQL. Use it in DDL DEFAULT clauses and portable application SQL to avoid vendor lock-in.
⚠Anti-Pattern— Mixing UTC_DATE() and CURDATE() in the same query
Combining UTC and local date functions in one query creates subtle timezone-boundary bugs.
✓ Instead: Pick one timezone strategy (UTC throughout) and apply conversions consistently.
UTC_DATE() returns the UTC date regardless of the server timezone. For globally-distributed applications, always store dates as UTC and convert to local time at the application layer to avoid DST gaps and overlaps.
Returns the current UTC time as a TIME value in 'HH:MM:SS' format.
Signature
UTC_TIME([fsp])
Parameters
Parameter
Type
Description
fsp
INT
Fractional seconds precision 0-6 (optional)
Examples
Current UTC time
sql
SELECTUTC_TIME();
→-- 14:30:00
With milliseconds
sql
SELECTUTC_TIME(3);
→-- 14:30:00.123
Local vs UTC
sql
SELECTCURTIME(), UTC_TIME();
→-- differ by timezone offset
UTC hour
sql
SELECTHOUR(UTC_TIME());
→-- 14
Offset detection
sql
SELECTTIMEDIFF(UTC_TIME(), CURTIME());
→-- timezone offset
⚠Anti-Pattern— Storing UTC_TIME() without a date
Storing only a UTC time without date loses the date context needed for correct timezone conversion.
✓ Instead: Store full UTC_TIMESTAMP() values and convert to local time at the application layer.
UTC_TIME() returns the current UTC time as a TIME value. Use it for time-only logging where timezone drift would cause incorrect chronological ordering in application logs.
example
SELECTUTC_TIME(), CURTIME();
→-- UTC time vs local server time
◆MariaDB Note
Returns the UTC time. Useful for servers in non-UTC timezones.
⇄ vs MySQL: Identical to MySQL.
⇄ vs PostgreSQL: PostgreSQL uses CURRENT_TIME AT TIME ZONE 'UTC'.
⚠Anti-Pattern— Using UTC_TIMESTAMP() in a local-timezone app without conversion
Displaying raw UTC_TIMESTAMP() values to users without local conversion causes incorrect times.
✓ Instead: Convert UTC to local time using CONVERT_TZ() before presenting to end users.
UTC_TIMESTAMP() is the UTC equivalent of NOW(). Store all application timestamps in UTC using this function to prevent data inconsistencies when the server timezone changes or when data is replicated across regions.
⚠Anti-Pattern— WHERE DATE(indexed_col) = '2024-03-15'
Wrapping an indexed column in DATE() causes a full table scan, ignoring the index.
✓ Instead: Use WHERE indexed_col >= '2024-03-15' AND indexed_col < '2024-03-16'.
DATE(datetime_col) extracts just the date part, enabling GROUP BY date without time interference. However, this prevents index use on the column — for range filtering, use a >= / < range instead.
⚠Anti-Pattern— Using TIME() on an indexed DATETIME for filtering
TIME(indexed_datetime_col) = '10:00:00' cannot use the index and scans the full table.
✓ Instead: Filter on the full DATETIME column with a range or store time separately.
TIME(datetime_col) extracts the time portion as a TIME value. Combine with HOUR() to bucket events by time of day, or use in a WHERE clause to find events within a specific time window regardless of date.
Returns the year from a date expression as an integer (1000–9999).
Signature
YEAR(date)
Parameters
Parameter
Type
Description
date
DATE
A date, datetime, or date string
Examples
Year from string
sql
SELECTYEAR('2024-03-15');
→-- 2024
Current year
sql
SELECTYEAR(NOW());
→-- 2024
Column extraction
sql
SELECTYEAR(birth_date) FROMemployees;
→-- birth year
Filter by year
sql
SELECT*FROMordersWHEREYEAR(order_date) =2024;
→-- 2024 orders
Annual aggregation
sql
SELECTCOUNT(*) FROMsalesGROUPBYYEAR(sale_date);
→-- annual totals
⚠Anti-Pattern— WHERE YEAR(date_col) = 2024 on large tables
YEAR() wrapping an indexed column forces a full scan, making year-range queries slow.
✓ Instead: Use WHERE date_col >= '2024-01-01' AND date_col < '2025-01-01'.
YEAR() extracts the four-digit year from a date expression. It's the most common date-part function in reporting queries. Note that using YEAR(col) in a WHERE clause prevents index use; use a range condition for filtered queries.
⚠Anti-Pattern— Filtering a single month without specifying the year
WHERE MONTH(col) = 3 matches March of every year, not just the current year.
✓ Instead: Always pair MONTH() with YEAR() or use a date range condition.
MONTH() returns 1-12. Always pair it with YEAR() in GROUP BY clauses to avoid collapsing January of different years into the same bucket. For display, use MONTHNAME() or DATE_FORMAT with %M.
DAY() = 31 skips months with fewer than 31 days, breaking monthly schedules.
✓ Instead: Use LAST_DAY() to find the actual last day of each month.
DAY() returns the day of the month (1-31). Use it to identify billing dates, find orders on a specific day of the month, or build recurring-schedule logic without date arithmetic.
When applied to a TIME column storing elapsed durations, HOUR() can exceed 23.
✓ Instead: Use HOUR() on DATETIME for clock hours; validate range when applied to elapsed TIME.
HOUR() returns 0-23. Grouping by HOUR(created_at) gives you an intraday distribution of events — essential for capacity planning, identifying peak usage windows, and scheduling maintenance.
⚠Anti-Pattern— Rounding timestamps using MINUTE() arithmetic
Manual MINUTE() rounding is error-prone near hour boundaries.
✓ Instead: Use FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(dt)/60)*60) for clean minute truncation.
MINUTE() returns 0-59. For 15-minute bucketing combine with integer division: FLOOR(MINUTE(ts)/15)*15. This is useful for time-series charts and rate-limiting windows.
⚠Anti-Pattern— Using SECOND() to get sub-second precision
SECOND('10:30:45.789') returns 45, discarding the .789 fractional part.
✓ Instead: Use MICROSECOND() alongside SECOND() when sub-second values matter.
SECOND() extracts the seconds component (0-59). Use it to detect timestamps that have been truncated to whole minutes during data ingestion — a data quality check for time-series pipelines.
⚠Anti-Pattern— Expecting non-zero MICROSECOND from a DATETIME(0) column
DATETIME without fractional precision stores no sub-second data; MICROSECOND() always returns 0.
✓ Instead: Declare the column as DATETIME(6) to store and retrieve microseconds.
MICROSECOND() extracts the fractional seconds as microseconds (0-999999). Requires the column or expression to be DATETIME(6) or TIME(6). Used for benchmarking stored procedure durations and high-frequency event deduplication.
example
SELECTMICROSECOND(NOW(6)) ASus;
→-- '456789'
◆MariaDB Note
Returns microseconds from DATETIME(6) or TIME(6) expressions. Available since MariaDB 5.3.
⇄ vs MySQL: Identical to MySQL.
⇄ vs PostgreSQL: PostgreSQL uses EXTRACT(MICROSECONDS FROM col) which includes seconds * 1e6.
⚠Anti-Pattern— Assuming DAYOFWEEK() starts on Monday
DAYOFWEEK() starts on Sunday (1). Using it as Monday=1 causes off-by-one errors in week logic.
✓ Instead: Use WEEKDAY() where Monday=0, or subtract 1 and apply MOD for custom week starts.
DAYOFWEEK returns 1 (Sunday) through 7 (Saturday) following the ODBC standard. To find weekdays only, use DAYOFWEEK(col) BETWEEN 2 AND 6. Remember that 1=Sunday, not Monday, which differs from ISO week conventions.
Leap years have 366 days; using 365 as a fixed maximum breaks end-of-year calculations.
✓ Instead: Use DAYOFYEAR(LAST_DAY(CONCAT(YEAR(d), '-12-01'))) to get the year's actual last day number.
DAYOFYEAR returns 1-366 and is useful for finding 'same day last year' comparisons or building day-of-year heat maps. Combine with YEAR() to create a composite seasonal index key.
⚠Anti-Pattern— Confusing WEEKDAY() and DAYOFWEEK() numbering
WEEKDAY() Monday=0 and DAYOFWEEK() Sunday=1 are easy to mix up, causing wrong day filters.
✓ Instead: Add a comment or use DAYNAME() for readability when day numbering matters.
WEEKDAY returns 0 (Monday) through 6 (Sunday), following ISO convention. This is the opposite mapping from DAYOFWEEK. Use WEEKDAY when your business logic treats Monday as the start of the week.
example
SELECT*FROMschedulesWHEREWEEKDAY(work_date) <5;
→-- Monday through Friday only (0-4)
◆MariaDB Note
Returns 0 (Monday) through 6 (Sunday), following ISO convention. Standard SQL function.
⇄ vs MySQL: Identical to MySQL.
⇄ vs PostgreSQL: PostgreSQL uses EXTRACT(ISODOW FROM col) where 1=Monday, 7=Sunday.
WEEK(col) = 10 matches week 10 of every year. Always pair with YEAR() for correct weekly grouping.
✓ Instead: GROUP BY YEAR(d), WEEK(d, 1) or use YEARWEEK(d, 1) which combines both.
WEEK(date, mode) gives ISO or US-style week numbers depending on mode (0-7). Mode 3 gives ISO 8601 week numbers (Monday start, first week with Thursday). Using the wrong mode causes off-by-one errors in year-boundary weeks.
⚠Anti-Pattern— Grouping by WEEKOFYEAR() without YEAR()
WEEKOFYEAR() = 1 matches week 1 of every year. Always include YEAR() to avoid cross-year merging.
✓ Instead: Use YEARWEEK(d, 3) which returns a single YYYYWW integer combining year and ISO week.
WEEKOFYEAR always uses ISO 8601 (mode 3): Monday start, first week contains January 4th. Use it for weekly KPI reporting to get consistent week boundaries across year-end transitions.
⚠Anti-Pattern— Grouping by QUARTER() without YEAR()
QUARTER(col) = 1 matches Q1 of all years, silently merging multiple years of data.
✓ Instead: Always use GROUP BY YEAR(d), QUARTER(d) for correct quarterly breakdowns.
QUARTER returns 1-4. Always include YEAR() in GROUP BY alongside QUARTER() to avoid merging Q1 of different years. For fiscal years that don't start in January, you'll need custom date arithmetic.
Sorting on a DATE_FORMAT result sorts lexicographically, not chronologically unless format is ISO 8601.
✓ Instead: Sort on the raw date column, use DATE_FORMAT only for display.
DATE_FORMAT saves a round-trip to the application by returning locale-appropriate date strings directly. Use %W %D %M %Y for long-form dates, or %d/%m/%Y for European format. Note that results are strings — do not use DATE_FORMAT output in further date arithmetic.
Formats a time value using a format string with % specifiers.
Signature
TIME_FORMAT(time, format)
Parameters
Parameter
Type
Description
time
TIME
The time value to format
format
VARCHAR
Format string; only time specifiers (%H, %i, %s, %p) are valid
Examples
24-hour short
sql
SELECTTIME_FORMAT('14:30:00', '%H:%i');
→-- 14:30
12-hour with AM/PM
sql
SELECTTIME_FORMAT('14:30:00', '%h:%i %p');
→-- 02:30 PM
%T is shorthand for %H:%i:%s
sql
SELECTTIME_FORMAT(CURTIME(), '%T');
→-- 14:30:00
TIME can exceed 24h
sql
SELECTTIME_FORMAT('838:59:59', '%H:%i:%s');
→-- 838:59:59
NULL input returns NULL
sql
SELECTTIME_FORMAT(NULL, '%H');
→-- NULL
⚠Anti-Pattern— Using date specifiers in TIME_FORMAT
Specifiers like %Y or %d are not valid in TIME_FORMAT and return empty strings.
✓ Instead: Use DATE_FORMAT for date parts or combined datetime formatting.
TIME_FORMAT works like DATE_FORMAT but for TIME values. Use %h:%i %p for 12-hour clock with AM/PM. Useful for rendering schedule times in web UI templates without application-layer formatting.
⚠Anti-Pattern— Relying on implicit string conversion
Inserting date strings without STR_TO_DATE risks silent conversion errors for non-ISO formats.
✓ Instead: Always use STR_TO_DATE when input is not in YYYY-MM-DD format.
STR_TO_DATE converts a formatted string to a DATE/DATETIME using the same format specifiers as DATE_FORMAT — the two functions are inverse operations. Essential for importing dates from legacy systems with non-ISO formats like DD/MM/YYYY.
Interval unit: DAY, MONTH, YEAR, HOUR, MINUTE, SECOND, etc.
Examples
Add 7 days
sql
SELECTDATE_ADD('2024-03-15', INTERVAL7DAY);
→-- 2024-03-22
Add one month
sql
SELECTDATE_ADD('2024-03-15', INTERVAL1MONTH);
→-- 2024-04-15
Add hours to current time
sql
SELECTDATE_ADD(NOW(), INTERVAL2HOUR);
→-- 2 hours from now
Month-end clamping
sql
SELECTDATE_ADD('2024-01-31', INTERVAL1MONTH);
→-- 2024-02-29
Negative interval subtracts
sql
SELECTDATE_ADD('2024-03-15', INTERVAL-3DAY);
→-- 2024-03-12
⚠Anti-Pattern— Adding months to month-end dates
DATE_ADD('2024-01-31', INTERVAL 1 MONTH) clamps to the last day of February, which may surprise you.
✓ Instead: Account for month-end edge cases in application logic when needed.
DATE_ADD(date, INTERVAL n unit) is the cleanest way to compute future deadlines. INTERVAL units include SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR. For subtracting, use INTERVAL -n or DATE_SUB.
⚠Anti-Pattern— Using NOW() - INTERVAL in WHERE without index
Wrapping an indexed column in a function prevents index usage.
✓ Instead: Write WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) so the column stays unwrapped.
DATE_SUB is the explicit subtraction partner to DATE_ADD. Use it for 'show me the last N days/months' queries. For index-friendly range queries, compute the boundary once outside the query and use a >= comparison.
Adds days or an interval to a date. Alias of DATE_ADD when used with INTERVAL.
Signatures
ADDDATE(date, INTERVAL expr unit)
ADDDATE(date, days)
Parameters
Parameter
Type
Description
date
DATE/DATETIME
The starting date
days / expr
INT / INTERVAL
Number of days to add, or an interval expression
Examples
Add 10 days (integer form)
sql
SELECTADDDATE('2024-03-15', 10);
→-- 2024-03-25
INTERVAL form identical to DATE_ADD
sql
SELECTADDDATE('2024-03-15', INTERVAL1MONTH);
→-- 2024-04-15
Add zero days
sql
SELECTADDDATE(CURDATE(), 0);
→-- today's date
Negative days subtract
sql
SELECTADDDATE('2024-03-15', -5);
→-- 2024-03-10
NULL propagates
sql
SELECTADDDATE(NULL, 5);
→-- NULL
⚠Anti-Pattern— Confusing ADDDATE with ADDTIME
ADDDATE adds days or date intervals; ADDTIME adds a time expression to a datetime.
✓ Instead: Use ADDTIME to add hours/minutes/seconds to a DATETIME value.
ADDDATE(date, INTERVAL n unit) is an alias for DATE_ADD. But ADDDATE(date, n) where n is an integer adds n days — a useful shorthand for simple day-offset calculations. ADDDATE with an integer is slightly more concise than DATE_ADD for day-only math.
Subtracts days or an interval from a date. Alias of DATE_SUB when used with INTERVAL.
Signatures
SUBDATE(date, INTERVAL expr unit)
SUBDATE(date, days)
Parameters
Parameter
Type
Description
date
DATE/DATETIME
The starting date
days / expr
INT / INTERVAL
Number of days to subtract, or an interval expression
Examples
Subtract 5 days
sql
SELECTSUBDATE('2024-03-15', 5);
→-- 2024-03-10
INTERVAL form identical to DATE_SUB
sql
SELECTSUBDATE('2024-03-15', INTERVAL1MONTH);
→-- 2024-02-15
Rolling window
sql
SELECTSUBDATE(CURDATE(), 30);
→-- 30 days ago
Year boundary crossing
sql
SELECTSUBDATE('2024-01-01', 1);
→-- 2023-12-31
NULL propagates
sql
SELECTSUBDATE(NULL, 3);
→-- NULL
⚠Anti-Pattern— Using SUBDATE where DATEDIFF is appropriate
SUBDATE returns a new date; use DATEDIFF when you need the numeric difference between two dates.
✓ Instead: Use DATEDIFF(date1, date2) to get the integer day difference.
Like ADDDATE, SUBDATE(date, n) with an integer subtracts n days — no INTERVAL keyword needed. Use this for 'N days ago' lookback boundaries in dashboard queries.
Adds a time expression to a datetime or time value.
Signature
ADDTIME(expr1, expr2)
Parameters
Parameter
Type
Description
expr1
DATETIME/TIME
The base datetime or time value
expr2
TIME/VARCHAR
The time amount to add, e.g. '01:30:00'
Examples
Add 1.5 hours
sql
SELECTADDTIME('2024-03-15 10:00:00', '01:30:00');
→-- 2024-03-15 11:30:00
Add 30 minutes to TIME
sql
SELECTADDTIME('10:00:00', '00:30:00');
→-- 10:30:00
Add to current datetime
sql
SELECTADDTIME(NOW(), '00:05:00');
→-- 5 minutes from now
TIME can exceed 24 hours
sql
SELECTADDTIME('23:30:00', '01:00:00');
→-- 24:30:00
Day rollover
sql
SELECTADDTIME('2024-03-15 23:00:00', '02:00:00');
→-- 2024-03-16 01:00:00
⚠Anti-Pattern— Confusing ADDTIME and DATE_ADD for hour addition
ADDTIME requires a string like '01:00:00'; DATE_ADD(dt, INTERVAL 1 HOUR) is less error-prone.
✓ Instead: Prefer DATE_ADD(dt, INTERVAL N HOUR) for adding whole hours.
ADDTIME(datetime, time) adds a time expression to a DATETIME or TIME value. Use it to add buffer periods: ADDTIME(appointment_start, '00:30:00') gives the end time of a 30-minute meeting.
Subtracts a time expression from a datetime or time value.
Signature
SUBTIME(expr1, expr2)
Parameters
Parameter
Type
Description
expr1
DATETIME/TIME
The base datetime or time value
expr2
TIME/VARCHAR
The time amount to subtract, e.g. '00:30:00'
Examples
Subtract 1.5 hours
sql
SELECTSUBTIME('2024-03-15 10:00:00', '01:30:00');
→-- 2024-03-15 08:30:00
Subtract from TIME
sql
SELECTSUBTIME('10:00:00', '00:30:00');
→-- 09:30:00
Recent time
sql
SELECTSUBTIME(NOW(), '00:05:00');
→-- 5 minutes ago
Day boundary crossing
sql
SELECTSUBTIME('2024-03-15 01:00:00', '02:00:00');
→-- 2024-03-14 23:00:00
NULL propagates
sql
SELECTSUBTIME(NULL, '01:00:00');
→-- NULL
⚠Anti-Pattern— Using SUBTIME where TIMEDIFF is needed
SUBTIME modifies a datetime by subtracting time; TIMEDIFF returns the difference between two times.
✓ Instead: Use TIMEDIFF(t1, t2) to get the TIME difference between two values.
SUBTIME(datetime, time) subtracts a time interval from a DATETIME or TIME. Useful when you know an end time and duration but need the start time — e.g., for backwards-computing job start from completion time.
⚠Anti-Pattern— Using DATEDIFF for sub-day precision
DATEDIFF('2024-03-15 23:00', '2024-03-15 01:00') returns 0 because only dates are compared.
✓ Instead: Use TIMESTAMPDIFF(HOUR, dt1, dt2) for hour-level differences.
DATEDIFF(end, start) returns a signed integer number of days. It ignores the time component, so DATEDIFF('2024-03-15 23:59', '2024-03-14 00:01') = 1. Perfect for computing account age, days overdue, or membership duration.
TIMEDIFF clips at ±838:59:59. Differences larger than ~35 days will be capped.
✓ Instead: Use TIMESTAMPDIFF(HOUR, dt1, dt2) for large datetime spans.
TIMEDIFF(end, start) returns the difference as a TIME value. Since TIME is limited to 838 hours, use TIMESTAMPDIFF(SECOND, ...) for differences larger than about 35 days. TIMEDIFF is best for intra-day duration measurements.
⚠Anti-Pattern— Expecting TIMESTAMPDIFF(YEAR) to round
TIMESTAMPDIFF(YEAR, dob, CURDATE()) returns 0 if the birthday hasn't occurred yet this year.
✓ Instead: Use FLOOR(DATEDIFF(CURDATE(), dob) / 365.25) for a simpler age approximation if rounding matters.
TIMESTAMPDIFF(unit, start, end) returns an integer difference in the specified unit (SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR). Unlike DATEDIFF/TIMEDIFF, it handles month and year boundaries correctly. Use it for precise age calculations.
⚠Anti-Pattern— Using TIMESTAMPADD when DATE_ADD is clearer
DATE_ADD(dt, INTERVAL 7 DAY) is more readable for most use cases.
✓ Instead: Reserve TIMESTAMPADD for dynamic unit scenarios where the unit is a variable.
TIMESTAMPADD(unit, n, datetime) adds n units to a DATETIME. Unlike DATE_ADD, the unit is the first argument which makes it easier to use in dynamic SQL where the unit comes from a variable. Both produce identical results for standard units.
Converts a Unix timestamp (seconds since epoch) to a DATETIME or formatted string.
Signatures
FROM_UNIXTIME(unix_timestamp)
FROM_UNIXTIME(unix_timestamp, format)
Parameters
Parameter
Type
Description
unix_timestamp
INT/DECIMAL
Unix timestamp in seconds (supports sub-second decimals)
format
VARCHAR
Optional DATE_FORMAT-style format string
Examples
Epoch origin (UTC)
sql
SELECTFROM_UNIXTIME(0);
→-- 1970-01-01 00:00:00
Sample Unix timestamp
sql
SELECTFROM_UNIXTIME(1710504000);
→-- 2024-03-15 12:00:00
With format string
sql
SELECTFROM_UNIXTIME(1710504000, '%Y-%m-%d');
→-- 2024-03-15
Round-trip with UNIX_TIMESTAMP
sql
SELECTFROM_UNIXTIME(UNIX_TIMESTAMP());
→-- current datetime
Sub-second support
sql
SELECTFROM_UNIXTIME(1710504000.5);
→-- 2024-03-15 12:00:00.500000
⚠Anti-Pattern— Assuming UTC output from FROM_UNIXTIME
FROM_UNIXTIME converts to the session time zone, not necessarily UTC.
✓ Instead: Set @@time_zone = '+00:00' or use CONVERT_TZ for explicit UTC output.
FROM_UNIXTIME converts seconds since 1970-01-01 00:00:00 UTC to a DATETIME in the server's timezone. Pass an optional format string (same as DATE_FORMAT) to get a formatted string in a single call. Essential for processing API timestamps stored as integers.
⚠Anti-Pattern— Storing Unix timestamps in INT columns for all use cases
Unix timestamps are harder to query readably and lose time zone context.
✓ Instead: Use DATETIME or TIMESTAMP columns for most storage; convert to Unix only for interop.
UNIX_TIMESTAMP() or UNIX_TIMESTAMP(datetime) converts to seconds since epoch, stripping timezone ambiguity. INT UNSIGNED columns storing epoch times are 4 bytes vs 8 bytes for DATETIME(6) and are trivially comparable in any language.
Converts a day number (days since year 0) to a DATE value.
Signature
FROM_DAYS(N)
Parameters
Parameter
Type
Description
N
INT
Day number since year 0 (day 1 = 0001-01-01 in proleptic Gregorian)
Examples
Day number to date
sql
SELECTFROM_DAYS(738960);
→-- 2024-03-15
Round-trip with TO_DAYS
sql
SELECTFROM_DAYS(TO_DAYS('2024-03-15'));
→-- 2024-03-15
Add days via day number
sql
SELECTFROM_DAYS(TO_DAYS(CURDATE()) +30);
→-- 30 days from today
Low day numbers return zero date
sql
SELECTFROM_DAYS(1);
→-- 0000-00-00
First valid date
sql
SELECTFROM_DAYS(366);
→-- 0001-01-01
⚠Anti-Pattern— Using FROM_DAYS for general date arithmetic
Day-number arithmetic with FROM_DAYS/TO_DAYS is less readable than DATE_ADD/DATEDIFF.
✓ Instead: Use DATE_ADD(date, INTERVAL N DAY) and DATEDIFF for clarity.
FROM_DAYS(N) converts a daynumber (where day 1 = year 0001-01-01) back to a DATE. It is primarily used in conjunction with TO_DAYS() for period arithmetic or when working with legacy systems that stored dates as day counts.
⚠Anti-Pattern— Using TO_DAYS subtraction instead of DATEDIFF
TO_DAYS(d1) - TO_DAYS(d2) works but DATEDIFF(d1, d2) is more expressive and readable.
✓ Instead: Use DATEDIFF for day-count differences.
TO_DAYS(date) returns an absolute day count, allowing simple subtraction to compute day differences: TO_DAYS(end) - TO_DAYS(start). It's equivalent to DATEDIFF but explicitly returns the count from year 0. Avoid for dates before year 1582 (Gregorian cutover).
⚠Anti-Pattern— Using TO_SECONDS instead of UNIX_TIMESTAMP for epoch arithmetic
TO_SECONDS counts from year 0, not 1970. Its large values are harder to reason about.
✓ Instead: Use UNIX_TIMESTAMP for epoch-based arithmetic and interop with application code.
TO_SECONDS(datetime) returns the number of seconds since year 0, useful for computing precise absolute time differences on DATETIME values including the time component, unlike TO_DAYS which ignores time.
Returns a DATE for a given year and day-of-year number.
Signature
MAKEDATE(year, dayofyear)
Parameters
Parameter
Type
Description
year
INT
The four-digit year
dayofyear
INT
Day of year (1–366); values > year length overflow to next year
Examples
75th day of 2024
sql
SELECTMAKEDATE(2024, 75);
→-- 2024-03-15
First day of year
sql
SELECTMAKEDATE(2024, 1);
→-- 2024-01-01
Last day of leap year
sql
SELECTMAKEDATE(2024, 366);
→-- 2024-12-31
Overflow to next year
sql
SELECTMAKEDATE(2024, 400);
→-- 2025-02-04
Day 0 returns NULL
sql
SELECTMAKEDATE(2024, 0);
→-- NULL
⚠Anti-Pattern— Using MAKEDATE with zero or negative dayofyear
MAKEDATE returns NULL for dayofyear <= 0.
✓ Instead: Validate that dayofyear >= 1 before calling MAKEDATE.
MAKEDATE(year, dayofyear) constructs a DATE from a year and a 1-based day-of-year number. Useful when raw data provides year + DOY instead of full dates, which is common in scientific and satellite data formats.
example
SELECTMAKEDATE(2024, 75) AStarget_date;
→-- '2024-03-15' (75th day of 2024)
◆MariaDB Note
Returns NULL if dayofyear is 0 or negative. Standard SQL function.
⇄ vs MySQL: Identical to MySQL.
⇄ vs PostgreSQL: PostgreSQL: use (year::text||'-01-01')::date + (dayofyear-1).
Constructs a TIME value from integer hour, minute, and second components.
Signature
MAKETIME(hour, minute, second)
Parameters
Parameter
Type
Description
hour
INT
Hour component (can exceed 23 for large TIME values)
minute
INT
Minute component (0–59)
second
DECIMAL
Second component (0–59.999999), supports microseconds
Examples
Build a time value
sql
SELECTMAKETIME(14, 30, 0);
→-- 14:30:00
Maximum TIME value
sql
SELECTMAKETIME(838, 59, 59);
→-- 838:59:59
Midnight
sql
SELECTMAKETIME(0, 0, 0);
→-- 00:00:00
With microseconds
sql
SELECTMAKETIME(10, 30, 45.5);
→-- 10:30:45.500000
NULL in any arg returns NULL
sql
SELECTMAKETIME(NULL, 30, 0);
→-- NULL
⚠Anti-Pattern— Using MAKETIME to build a DATETIME
MAKETIME returns a TIME only. To build a DATETIME, combine with MAKEDATE using TIMESTAMP().
✓ Instead: Use TIMESTAMP(MAKEDATE(year, doy), MAKETIME(h, m, s)) for a full DATETIME.
MAKETIME(hour, minute, second) builds a TIME value from its components, which is useful when hours, minutes, and seconds are stored in separate integer columns or computed by formulas.
Adds a number of months to a period in YYMM or YYYYMM format and returns YYYYMM.
Signature
PERIOD_ADD(period, months)
Parameters
Parameter
Type
Description
period
INT
Period in YYMM or YYYYMM format (not a date)
months
INT
Number of months to add (negative to subtract)
Examples
Add 3 months
sql
SELECTPERIOD_ADD(202403, 3);
→-- 202406
Year rollover
sql
SELECTPERIOD_ADD(202412, 2);
→-- 202502
Subtract months
sql
SELECTPERIOD_ADD(202403, -2);
→-- 202401
YYMM format expands to YYYYMM
sql
SELECTPERIOD_ADD(9903, 1);
→-- 199904
Zero months unchanged
sql
SELECTPERIOD_ADD(202403, 0);
→-- 202403
⚠Anti-Pattern— Treating PERIOD_ADD result as a date
PERIOD_ADD returns an integer like 202406, not a DATE or DATETIME.
✓ Instead: Convert back to a date with STR_TO_DATE(PERIOD_ADD(p, n), '%Y%m') if needed.
PERIOD_ADD(P, N) adds N months to period P (YYYYMM format) and returns the resulting period. The YYYYMM format is common in financial and billing systems for monthly period codes. Note: PERIOD_ADD is not compatible with standard DATE arithmetic.
example
SELECTPERIOD_ADD(202403, 12) ASone_year_later;
→-- 202503
◆MariaDB Note
Period format is YYYYMM or YYMM. Not compatible with DATE values.
⇄ vs MySQL: Identical to MySQL.
⇄ vs PostgreSQL: PostgreSQL uses date + INTERVAL 'N months' directly on DATE types.
Returns the number of months between two periods in YYMM or YYYYMM format.
Signature
PERIOD_DIFF(period1, period2)
Parameters
Parameter
Type
Description
period1
INT
First period in YYMM or YYYYMM format
period2
INT
Second period in YYMM or YYYYMM format
Examples
3 months apart
sql
SELECTPERIOD_DIFF(202406, 202403);
→-- 3
Negative when period1 < period2
sql
SELECTPERIOD_DIFF(202403, 202406);
→-- -3
Cross-year difference
sql
SELECTPERIOD_DIFF(202501, 202312);
→-- 13
YYMM format
sql
SELECTPERIOD_DIFF(9903, 9901);
→-- 2
Same period
sql
SELECTPERIOD_DIFF(202403, 202403);
→-- 0
⚠Anti-Pattern— Using PERIOD_DIFF on actual DATE columns
PERIOD_DIFF expects YYMM/YYYYMM integers, not DATE values.
✓ Instead: Use TIMESTAMPDIFF(MONTH, date1, date2) for month differences between real dates.
PERIOD_DIFF(P1, P2) returns P1 - P2 in months where both periods are in YYYYMM format. Use it to calculate how many billing cycles separate two periods in financial reporting.
example
SELECTPERIOD_DIFF(202412, 202403) ASmonths_diff;
→-- 9 (9 months between March and December 2024)
◆MariaDB Note
Period format is YYYYMM or YYMM. Returns signed integer months.
⇄ vs MySQL: Identical to MySQL.
⇄ vs PostgreSQL: PostgreSQL uses DATE_PART('month', age(d1, d2)) for month-level date differences.
⚠Anti-Pattern— Using named zones without loading TZ tables
CONVERT_TZ with named zones returns NULL if the time zone tables are not loaded.
✓ Instead: Use offset notation ('+05:30') for portability, or populate TZ tables via mysql_tzinfo_to_sql.
CONVERT_TZ(dt, from_tz, to_tz) shifts a DATETIME between timezones. Requires the timezone tables to be populated (mysql_tzinfo_to_sql). Best practice: store all timestamps as UTC and use CONVERT_TZ only at display time.
Returns the last day of the month for a given date or datetime.
Signature
LAST_DAY(date)
Parameters
Parameter
Type
Description
date
DATE/DATETIME/VARCHAR
Any date within the target month
Examples
Last day of March
sql
SELECTLAST_DAY('2024-03-15');
→-- 2024-03-31
Leap year February
sql
SELECTLAST_DAY('2024-02-01');
→-- 2024-02-29
Non-leap February
sql
SELECTLAST_DAY('2023-02-01');
→-- 2023-02-28
Days in month
sql
SELECTDAY(LAST_DAY('2024-03-15'));
→-- 31
NULL returns NULL
sql
SELECTLAST_DAY(NULL);
→-- NULL
⚠Anti-Pattern— Hardcoding month-end dates
Hardcoding 28, 30, or 31 for month-end logic breaks for leap years and different months.
✓ Instead: Always use LAST_DAY(date) for dynamic month-end dates.
LAST_DAY(date) returns the last calendar day of the month containing the input date. Use it to generate monthly report ranges without hardcoding days-per-month or handling February edge cases.
Extracts a specified date or time part from a datetime expression.
Signature
EXTRACT(unit FROM date)
Parameters
Parameter
Type
Description
unit
keyword
Part to extract: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEK, QUARTER, etc.
date
DATE/DATETIME/TIME
The datetime value to extract from
Examples
Extract year
sql
SELECTEXTRACT(YEARFROM'2024-03-15');
→-- 2024
Extract month
sql
SELECTEXTRACT(MONTHFROM'2024-03-15');
→-- 3
Extract day
sql
SELECTEXTRACT(DAYFROM'2024-03-15');
→-- 15
Extract hour
sql
SELECTEXTRACT(HOURFROM'2024-03-15 14:30:00');
→-- 14
Compound YEAR_MONTH unit
sql
SELECTEXTRACT(YEAR_MONTHFROM'2024-03-15');
→-- 202403
⚠Anti-Pattern— Using string functions to extract date parts
Using SUBSTRING or LEFT on a date string to get the year is fragile.
✓ Instead: Always use EXTRACT(YEAR FROM date) or YEAR(date) for reliable date part extraction.
EXTRACT(unit FROM date) is the ISO SQL-standard form. Use it in cross-database scripts that must run on both MariaDB and PostgreSQL. Valid units include YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND, MICROSECOND.