The DATE
, DATETIME
, and
TIMESTAMP
types are related. This section
describes their characteristics, how they are similar, and how
they differ. MySQL recognizes DATE
,
DATETIME
, and TIMESTAMP
values in several formats, described in
Section 9.1.3, “Date and Time Literals”. For the
DATE
and DATETIME
range
descriptions, “supported” means that although
earlier values might work, there is no guarantee.
The DATE
type is used for values with a date
part but no time part. MySQL retrieves and displays
DATE
values in
'YYYY-MM-DD'
format. The supported range is
'1000-01-01'
to
'9999-12-31'
.
The DATETIME
type is used for values that
contain both date and time parts. MySQL retrieves and displays
DATETIME
values in 'YYYY-MM-DD
hh:mm:ss'
format. The supported range is
'1000-01-01 00:00:00'
to '9999-12-31
23:59:59'
.
The TIMESTAMP
data type is used for values
that contain both date and time parts.
TIMESTAMP
has a range of '1970-01-01
00:00:01'
UTC to '2038-01-19
03:14:07'
UTC.
A DATETIME
or TIMESTAMP
value can include a trailing fractional seconds part in up to
microseconds (6 digits) precision. In particular, any fractional
part in a value inserted into a DATETIME
or
TIMESTAMP
column is stored rather than
discarded. With the fractional part included, the format for
these values is '
,
the range for YYYY-MM-DD
hh:mm:ss
[.fraction
]'DATETIME
values is
'1000-01-01 00:00:00.000000'
to
'9999-12-31 23:59:59.999999'
, and the range
for TIMESTAMP
values is '1970-01-01
00:00:01.000000'
to '2038-01-19
03:14:07.999999'
. The fractional part should always be
separated from the rest of the time by a decimal point; no other
fractional seconds delimiter is recognized. For information
about fractional seconds support in MySQL, see
Section 11.3.5, “Fractional Seconds in Time Values”.
The TIMESTAMP
and DATETIME
data types offer automatic initialization and updating to the
current date and time. For more information, see
Section 11.3.4, “Automatic Initialization and Updating for TIMESTAMP and DATETIME”.
MySQL converts TIMESTAMP
values from the
current time zone to UTC for storage, and back from UTC to the
current time zone for retrieval. (This does not occur for other
types such as DATETIME
.) By default, the
current time zone for each connection is the server's time. The
time zone can be set on a per-connection basis. As long as the
time zone setting remains constant, you get back the same value
you store. If you store a TIMESTAMP
value,
and then change the time zone and retrieve the value, the
retrieved value is different from the value you stored. This
occurs because the same time zone was not used for conversion in
both directions. The current time zone is available as the value
of the time_zone
system
variable. For more information, see
Section 5.1.13, “MySQL Server Time Zone Support”.
Invalid DATE
, DATETIME
, or
TIMESTAMP
values are converted to the
“zero” value of the appropriate type
('0000-00-00'
or '0000-00-00
00:00:00'
).
Be aware of certain properties of date value interpretation in MySQL:
MySQL permits a “relaxed” format for values specified as strings, in which any punctuation character may be used as the delimiter between date parts or time parts. In some cases, this syntax can be deceiving. For example, a value such as
'10:11:12'
might look like a time value because of the:
, but is interpreted as the year'2010-11-12'
if used in a date context. The value'10:45:15'
is converted to'0000-00-00'
because'45'
is not a valid month.The only delimiter recognized between a date and time part and a fractional seconds part is the decimal point.
The server requires that month and day values be valid, and not merely in the range 1 to 12 and 1 to 31, respectively. With strict mode disabled, invalid dates such as
'2004-04-31'
are converted to'0000-00-00'
and a warning is generated. With strict mode enabled, invalid dates generate an error. To permit such dates, enableALLOW_INVALID_DATES
. See Section 5.1.11, “Server SQL Modes”, for more information.MySQL does not accept
TIMESTAMP
values that include a zero in the day or month column or values that are not a valid date. The sole exception to this rule is the special “zero” value'0000-00-00 00:00:00'
.Dates containing two-digit year values are ambiguous because the century is unknown. MySQL interprets two-digit year values using these rules:
Year values in the range
00-69
are converted to2000-2069
.Year values in the range
70-99
are converted to1970-1999
.
* Storing data in the column will be ambiguous at the end of daylight savings, because the local-time hour gets repeated twice.
* At the start of daylight savings, when clocks are turned forwards, there's a "missing hour". Technically times in this hour aren't valid, but if you do try to store a time from this missing hour in a TIMESTAMP column and then read it back, you WON'T get out what you put in! (Contrary to what the page states).
* If you ORDER BY your timezone column, it will order by the internal UTC representation; *displayed* times can therefore be out of order when daylight savings ends.
* HOWEVER, any computation will use the derived local time, even in the ORDER BY clause, so if ts is a TIMESTAMP column, "ORDER BY ts" "ORDER BY <column number>", or "ORDER BY UNIX_TIMESTAMP(ts)" give UTC-time ordering, but "ORDER BY ts+0", "ORDER BY ts + INTERVAL 1 SECOND", "ORDER BY CAST(ts AS datetime)" will be ordered by *local* time, which may result in a different row ordering!
* Similarly, comparisons between timestamp values, i.e. ts1<ts2 etc are always carried out using local-time values. So the result of this comparison can CHANGE depending on the timezone you set, if the values are on different sides of the turn-the-clocks-back point: IT IS NOT A COMPARISON BETWEEN UTC TIMESTAMPS!!! To do that, compare UNIX_TIMESTAMP(ts1)<UNIX_TIMESTAMP(ts2)
These problems can be obviated by setting the server timezone to UTC (temporarily, if you prefer), i.e.:
SET @oldTZ := @@time_zone; SET @@time_zone := '+00:00';
#
# Do something with timestamps here; it'll work reliably[*] as everything is in UTC
#
SET @@time_zone := @oldTZ;
[*] Actually, not entirely true, as there may also occasionally be leap-seconds, represented by hh:59:59 repeating twice. So events with these timestamps may be more than one second apart, and there isn't a 1-1 correspondence between actual seconds and displayed seconds; and when storing decimal seconds (milliseconds, etc), once again all the local-times-are-out-of order issues can arise. See https://dev.mysql.com/doc/refman/8.0/en/time-zone-leap-seconds.html
CREATE TABLE t (ts TIMESTAMP);
and you insert a value like so:
INSERT INTO t (ts) VALUES (FROM_UNIXTIME(1522510200));
The value will undergo a roundtrip conversion, from a number to a date, and then back again, before insertion!
Due to daylight savings time and/or leap-seconds, the inserted value may not be what you started with!
SELECT UNIX_TIMESTAMP(ts) FROM t;
For example, in the Australian Eastern Daylight Time timezone, the result of the above query is 1522513800 rather than 1522510200, as the latter value falls within the hour that gets repeated at the end of daylight savings.
You can mitigate this problem by switching to a non-daylight-savings timezone (temporarily if you prefer), but I believe it also arises due to leap-seconds, which cannot be mitigated this way. i.e.
SET @oldTZ := @@time_zone;
SET @@time_zone := '+00:00';
# Do something with timestamps; they'll work reliably here unless you hit a leap-second
SET @@time_zone := @oldTZ;
I'm not aware of any way to insert a "raw" UNIX time-value into a TIMESTAMP column exactly, without conversion to a datetime and back again.
(Though bug report #83852 includes a feature request for exactly this, plus some helper functions).
You can just do this:
SET @@timestamp := 1522510200;
INSERT INTO t (ts) VALUES (NOW());
SET @@timestamp := DEFAULT;
It's rather clumsy, though!
(See the documentation page for Server System Variables: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_timestamp)