~ / topics / Data & Protocols
$man databases

Databases

In brief: Most modern databases store dates in ways that last for thousands of years. The main exceptions are MySQL's TIMESTAMP column type, which stops at 19 January 2038, and any table where developers chose to store "seconds since 1970" in a 32-bit integer column. MariaDB extended its TIMESTAMP to 2106 in 2024; MySQL still has the 2038 limit for that type. Some smaller internal fields, such as MongoDB's ObjectId timestamp, run out in 2106.

Risk level High (MySQL TIMESTAMP and INT epoch columns); Low (most native date types)
Status MariaDB ≥ 11.5 (first LTS: 11.8) extends TIMESTAMP to 2106; MySQL TIMESTAMP still ends 2038 (8.0.28 widened UNIX_TIMESTAMP/FROM_UNIXTIME only); schema-level INT epochs need migration
Last reviewed 2026-10-21

2038 status at a glance

Not affected or different deadline (3), and why

Overview

Databases matter for 2038 in two ways. First, a column type can have a hard upper limit. Second, applications often store future dates long before they happen: a 20-year mortgage, a certificate expiry, a warranty end date, or a "never expires" sentinel. Those values cross 2038 now, so database limits can cause errors or silent corruption years ahead of the date itself.

The good news is that the main SQL engines' native date/time types are wide. The problems are concentrated in a few legacy types, in functions that convert to and from Unix time, and in schemas designed around 32-bit integers.

Technical details

MySQL

MySQL's TIMESTAMP stores seconds since the epoch and "has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC", while DATETIME covers '1000-01-01 00:00:00' to '9999-12-31 23:59:59' [1].

MySQL 8.0.28 (released 2022-01-18) changed the conversion functions, not the type [2]:

A MySQL feature request to extend TIMESTAMP the way MariaDB did (bug #118267) is marked as verified but not implemented (as of 2026-10-21) [3]. Percona Server has not adopted the extension either [4].

MariaDB

MariaDB 11.5.0 (2024-03-18) extended TIMESTAMP: "The maximal allowed value for timestamps was '2038-01-19 03:14:07 UTC', and is now '2106-02-07 06:28:15 UTC'" (MDEV-32188) [5]. The on-disk format is unchanged, so tables remain readable by older servers while values stay in the old range, and the extension applies only on 64-bit platforms [5]. MariaDB 11.5 was a short-lived rolling release that is no longer maintained. The first long-term-support release with the extension is MariaDB 11.8 LTS; its announcement says no data conversion is needed except for system-versioned tables, whose rows and indexes must be rewritten during the upgrade, which can take a long time on large tables [15].

PostgreSQL

timestamp and timestamp with time zone are 8 bytes, range 4713 BC to 294276 AD with 1-microsecond resolution; date is 4 bytes, 4713 BC to 5874897 AD [6]. Native types are safe. Risk remains in to_timestamp(int4) style code paths, application columns declared integer for epochs, and extensions or clients that convert to a 32-bit time_t.

SQLite

SQLite has no dedicated date type; dates are stored as ISO-8601 text, Julian day numbers (REAL) or Unix timestamps (INTEGER) [7]. Its date functions work for "dates between 0000-01-01 00:00:00 and 9999-12-31 23:59:59" [7]. SQLite integers are up to 64-bit, so the engine is safe. The risk is in the application: code on 32-bit platforms that reads an INTEGER column into a C time_t or int.

Oracle Database

DATE has a "valid date range from January 1, 4712 BC, to December 31, 9999 AD" and the YEAR datetime field ranges from -4712 to 9999 (excluding year 0), which also governs TIMESTAMP [8]. Oracle native types are not affected by 2038.

Microsoft SQL Server

datetime covers 1753-01-01 through 9999-12-31 (8 bytes, rounded to .000/.003/.007 s), and Microsoft advises: "Avoid using datetime for new work" [9]. datetime2 covers 0001-01-01 through 9999-12-31 with 100 ns accuracy [10]. Native types are safe.

MongoDB

Redis

Key expiry is set with EXPIREAT (seconds) or PEXPIREAT (milliseconds) Unix times [13]. In the RDB persistence format, opcode 0xFC stores expiry as an 8-byte millisecond timestamp (RDB version 3 and later) and the older opcode 0xFD stores it as a 4-byte value in seconds [14]. Current Redis writes the millisecond form. Old RDB files, or third-party parsers that only handle 0xFD as 32-bit, are the only notable exposure.

Schemas that store epochs in INT

A very common pattern is created_at INT or expires INT holding Unix seconds. In MySQL, PostgreSQL and SQL Server, INT is a signed 32-bit integer (max 2,147,483,647), so it overflows exactly at 2038-01-19 03:14:07 UTC. An INT UNSIGNED (MySQL) lasts to 2106-02-07 06:28:15 UTC. ORMs and migration tools sometimes generate these columns by default.

Affected versions / products

Database / type Field width Max date Status / fix
MySQL TIMESTAMP (all versions) 4-byte epoch seconds 2038-01-19 03:14:07 UTC [1][2] Migrate to DATETIME
MySQL FROM_UNIXTIME/UNIX_TIMESTAMP/CONVERT_TZ < 8.0.28, or on 32-bit 32-bit 2038 Upgrade to ≥ 8.0.28 on 64-bit [2]
MySQL ≥ 8.0.28 functions (64-bit) 64-bit 3001-01-18 23:59:59.999999 UTC [2] —
MariaDB TIMESTAMP < 11.5 4-byte signed 2038 Upgrade to 11.8 LTS or later (64-bit) [5][15]
MariaDB TIMESTAMP ≥ 11.5 (64-bit) 4-byte, unsigned range 2106-02-07 06:28:15 UTC [5] —
PostgreSQL timestamp[tz] 8 bytes 294276 AD [6] Safe
SQLite date functions text/real/64-bit int 9999-12-31 [7] Safe (check app code)
Oracle DATE/TIMESTAMP 7+ bytes 9999-12-31 [8] Safe
SQL Server datetime / datetime2 8 / 6–8 bytes 9999-12-31 [9][10] Safe
MongoDB BSON Date 64-bit signed ms ~290 million years [11] Safe
MongoDB ObjectId timestamp 32-bit unsigned s 2106-02-07 [12] Check drivers treat as unsigned
Redis RDB 0xFD expiry 4-byte seconds 2106 if unsigned Legacy only; 0xFC is 8-byte ms [14]
Any INT epoch column 32-bit signed 2038-01-19 03:14:07 UTC Migrate to BIGINT or native type
Any INT UNSIGNED epoch column 32-bit unsigned 2106-02-07 06:28:15 UTC Migrate to BIGINT or native type

Risk & compliance notes

Detection & mitigation

Find MySQL/MariaDB TIMESTAMP and integer columns with time-like names:

SELECT table_schema, table_name, column_name, column_type
FROM information_schema.columns
WHERE data_type = 'timestamp'
   OR (data_type IN ('int','mediumint') AND column_name REGEXP '(time|date|_at$|expir|epoch|ts$)');

Find PostgreSQL integer columns with time-like names:

SELECT table_schema, table_name, column_name, data_type
FROM information_schema.columns
WHERE data_type = 'integer'
  AND column_name ~* '(time|date|_at$|expir|epoch|ts$)';

Remediation options:

  1. MySQL: ALTER TABLE … MODIFY col DATETIME(6) (note: DATETIME has no implicit time-zone conversion, unlike TIMESTAMP; store UTC explicitly).
  2. MariaDB: upgrade to 11.8 LTS or later on a 64-bit platform, allowing time for system-versioned tables to be rewritten [15], or convert to DATETIME.
  3. Epoch columns: ALTER … TYPE BIGINT (PostgreSQL) / MODIFY … BIGINT (MySQL), then fix application code to use 64-bit integers.
  4. Test by inserting '2038-01-19 03:14:08' and '2106-02-07 06:28:16' into staging copies and running application flows.

See also Testing-and-Detection and Remediation-Guide.

See also

References

  1. MySQL 8.0 Reference Manual §13.2.2, The DATE, DATETIME, and TIMESTAMP Types — Oracle — TIMESTAMP and DATETIME ranges.
  2. MySQL 8.0.28 Release Notes — Oracle, 2022-01-18 — WL #14630: 64-bit FROM_UNIXTIME/UNIX_TIMESTAMP/CONVERT_TZ; TIMESTAMP unchanged.
  3. MySQL bug #118267 — bugs.mysql.com — feature request to extend TIMESTAMP range (seen in search results).
  4. Can we extend timestamp range … as MariaDB did? — Percona Community Forum — Percona Server still has the 2038 limit (seen in search results).
  5. MariaDB 11.5.0 Release Notes — MariaDB, 2024-03-18 — MDEV-32188 TIMESTAMP to 2106; 64-bit only; format unchanged.
  6. PostgreSQL 18 documentation §8.5 Date/Time Types — PostgreSQL Global Development Group — Table 8.9 ranges.
  7. Date And Time Functions — SQLite — storage forms and 0000–9999 range.
  8. Oracle Database 19c SQL Language Reference, Data Types — Oracle — DATE range 4712 BC–9999 AD.
  9. datetime (Transact-SQL) — Microsoft Learn — range 1753–9999; "Avoid using datetime for new work".
  10. datetime2 (Transact-SQL) — Microsoft Learn — range 0001–9999, 100 ns.
  11. BSON Types — MongoDB Manual — Date, ObjectId and internal Timestamp definitions.
  12. ObjectId specification — mongodb/specifications (GitHub) — timestamp is unsigned 32-bit, good to 2106.
  13. PEXPIREAT — Redis documentation — absolute expiry in Unix milliseconds (seen in search results).
  14. Redis RDB File Format — rdb.fnordig.de — 0xFD 4-byte seconds, 0xFC 8-byte milliseconds expiry opcodes (seen in search results).
  15. MariaDB 11.8 LTS Released — MariaDB Foundation, 2025 — extended TIMESTAMP in an LTS release; system-versioned tables need rewriting (seen in search results).