VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
DATETIME vs TIMESTAMP
These two types look similar but behave differently at the critical moment — retrieval.| Type | Storage | Range | Time zone behavior |
|---|---|---|---|
DATETIME | 8 bytes | 1000-01-01 to 9999-12-31 | No conversion — stores and returns the literal value |
TIMESTAMP | 4 bytes | 1970-01-01 to 2038-01-19 | Stores UTC; converts to session time_zone on retrieval |
TIMESTAMP automatically converts values:
- On INSERT/UPDATE: converts from session
time_zoneto UTC for storage - On SELECT: converts from UTC back to session
time_zonefor display
DATETIME makes no conversions. The value you write in is the value you get back.
The 2038 Problem
TIMESTAMP is stored as a 32-bit Unix timestamp. It can’t represent dates after 2038-01-19 03:14:07 UTC. Any application that stores future dates — reservations, subscriptions, scheduled events — should use DATETIME to avoid hitting this limit.
Server and Session Time Zones
MySQL has two independent time zone settings:time_zone shows SYSTEM, MySQL is using the operating system’s time zone. This works until the OS changes (server migration, DST transition) — then your timestamps shift.
Best practice: set the server to UTC explicitly, and handle display conversion in the application.
Named Time Zones
'+00:00' and 'UTC' are both valid, but named zones like 'America/New_York' require the timezone tables to be loaded. If CONVERT_TZ() returns NULL or named zones fail, the tables are missing.
AUTO_UPDATE Columns
TIMESTAMP columns support automatic population on insert and update:
CURRENT_TIMESTAMP is evaluated in UTC if the session timezone is UTC, so created_at and updated_at are stored consistently regardless of who connects and from what timezone.
You can do the same with DATETIME and NOW():
NOW() returns the current date and time in the session’s timezone — another reason to standardize on UTC for the session.
Frequently Asked Questions
Should I store timestamps as UTC in DATETIME or use TIMESTAMP?
If your dates stay within the 2038 ceiling,TIMESTAMP is cleaner — it handles UTC conversion automatically. For future dates beyond 2038 or for applications that need to store historical dates before 1970, use DATETIME and enforce UTC at the application layer by always converting to UTC before inserting.
Why do timestamps shift after a server migration?
The old server usedtime_zone = SYSTEM pointing to one OS timezone; the new server points to a different one (or UTC). TIMESTAMP values stored as UTC display correctly in both timezones, but if any values were stored assuming the system timezone rather than UTC, they’ll shift. Audit with SELECT @@global.time_zone on both servers before migrating.
How do I store just a date with no time component?
UseDATE. It stores 3 bytes and has no time-of-day or timezone ambiguity. Operations like WHERE date_col = '2024-03-15' are unambiguous regardless of the session timezone.
Troubleshooting
| Problem | Solution |
|---|---|
| Timestamps shift by hours after deployment | Session or server time_zone differs between environments — standardize on UTC |
CONVERT_TZ() returns NULL | Named timezone tables not loaded — run mysql_tzinfo_to_sql |
| Dates after 2038 fail to store | Use DATETIME instead of TIMESTAMP |
TIMESTAMP shows wrong time after DST change | Set time_zone = '+00:00' instead of SYSTEM to avoid OS DST dependence |
updated_at not updating automatically | Column needs ON UPDATE CURRENT_TIMESTAMP clause |

