3

I'm using MySQL 5.0. I need to store date-time information in one column. I mean to use DATETIME or TIMESTAMP column type. But I have problem with summer-winter time change.

My timezone is CET. In summer we use summer-time (CEST, it is GMT+2) and in winter we use winter-time (CET, it is GMT+1). In the day when the summer time changes into winter time (in this year 2012 it was on 28th of October) we have time 2:00AM two-times. The sequence of time in this day is:

... -> 1:00 CEST -> 2:00 CEST -> 2:00 CET (= 3:00 CEST) -> 3:00 CET -> ...

So if I have timestamp/datetime '2012-10-28 02:00:00' I'm not able to say correctly if this time represents 2:00AM in summer time or in winter time.

I have two different Java dates:

Date d1 = new Date(1351382400000); // 2:00 CEST (summer-time)
Date d2 = new Date(1351386000000); // 2:00 CET (winter-time)

And when I store them in the database using standard timestamp/datetime format (ie. 'yyyy-MM-dd HH:mm:ss'), both of them store the same data '2012-10-28 02:00:00'. But when I get these values from database back into Date variables I get two same dates. So input dates were different but output dates are equal.
The same occurs if I use the FROM_UNIXTIME function to store date value: FROM_UNIXTIME(1351382400) and FROM_UNIXTIME(1351386000). Values are stored in the database column (DATETIME or TIMESTAMP type) as equal values. So when I get these values into Java's Date object I get two equal dates again (in winter time).

Is there any way to store the timezone in MySQL, or how to handle with timezone information within DATETIME/TIMESTAMP columns?
Of course I can store BIGINT values in the database with unix-timestamp. But I'm wondering if there's any way how to solve this problem with any MySQL date-time type.

Any help or trick is appreciated ... :)

Thanks a lot.

Honza (sporak)

EDIT #1:
If I tried to store timestamp values and then fetch this timestamp values into java's Date, I get faulty Date again. Let's say I have simple table with TIMESTAMP column. I store data in this table this way:

mysql> INSERT INTO `tab` (timestamp_column) VALUES 
          (FROM_UNIXTIME(1351382400)),  // 2:00 CEST (summer-time)
          (FROM_UNIXTIME(1351386000));  // 2:00 CET (winter-time)
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

When I fetch these rows into java Date object, I'm getting two same dates that shows 2:00 CET. And what's more - when I fetch these rows in MySQL I get wrong values again in MySQL:

mysql> SELECT UNIX_TIMESTAMP(timestamp_column) from tab;
+--------------------+
| UNIX_TIMESTAMP(ts) |
+--------------------+
|         1351386000 | 
|         1351386000 | 
+--------------------+
2 rows in set (0.00 sec)

So TIMESTAMP seems to me to be little useless.

Paulie-C
  • 1,674
  • 1
  • 13
  • 29
sporak
  • 496
  • 5
  • 10

3 Answers3

6

Your best bet, in my view, is to tell MySQL to use GMT and handle all local time issues in your application code, not your database. The values in the database would always be GMT, full stop, which is unambiguous. As you say, with daylight savings time (summer time) adjustments, you can end up with the same value in your database for what is, to us humans, two different times.

This also makes the database portable. If you move to North America and start using MySQL set to (say) Central time, all of a sudden the values in your database seem to have moved several hours. I had that issue with a database I inherited which was using the server's local time: When I moved it from the east coast of the U.S. to the west coast, not having thought to check whether MySQL was set to use the machine's zone...

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    You can, and probably should, hide the use of GMT from users. Convert according to an appropriate locale for display. "Appropriate" is a non-trivial issue. Location of server? Location of user? Location of business HQ? – Patricia Shanahan Nov 05 '12 at 15:40
  • 1
    @PatriciaShanahan: Right, as I said, handle it in application code, so the data in the database isn't ambiguous. – T.J. Crowder Nov 05 '12 at 15:42
  • @T.J.Crowder - I know us English love GMT (I am English but loves Scotland!) but GMT? Why not UTC - Keeps the Yanks happy!? – Ed Heal Nov 05 '12 at 16:04
  • @EdHeal: More than once writing the above I thought "I should probably say UTC, or GMT (UTC), or something," but ultimately thought it might be confusing. As the OP was using timezone indicators, and GMT is the usual timezone indicator for UTC... – T.J. Crowder Nov 05 '12 at 16:08
  • 1
    UTC is not the same as GMT: it is based on atomic time and includes leap seconds. – eggyal Nov 05 '12 at 16:09
  • @eggyal: I know. And yet, GMT is the timezone indicator that's relevant to the question. – T.J. Crowder Nov 05 '12 at 16:14
  • Yes, I manage date/timestamps in my Java application, this was my decision how to work around this problem. I'm only wondering if there's any way how to solve this problem directly in MySQL with some date/time type. – sporak Nov 09 '12 at 22:03
3

DATE, TIME, YEAR and DATETIME all store the respective date/time indications, just like taking a photograph of a calendar and/or clockface: they do not record the timezone in which the clock was set, so they don't represent any particular moment in time. They're useful for events that occur on a specific local date or at a specific local time (i.e. irrespective of timezone).

TIMESTAMP stores a UTC timestamp (as seconds since the UNIX epoch), performing conversion to/from your session's time_zone as necessary: it represents a precise, unambiguous moment in time. This is what you want; just be sure to set your session variable as appropriate (with SET SESSION time_zone = ...).

See MySQL Server Time Zone Support for more information.

eggyal
  • 122,705
  • 18
  • 212
  • 237
  • See my edit #1 in question. Timestamp seems to me to be useless. – sporak Nov 09 '12 at 21:28
  • 1
    @sporak: Your problem there is not with `TIMESTAMP`, but `FROM_UNIXTIME()` which converts into a `DATETIME` in the session's `time_zone`. Ensure that your session `time_zone` is set appropriately, as stated in my answer above. – eggyal Nov 09 '12 at 21:32
  • This I don't understand a little. 1351382400 is unique moment. When I insert this timestamp and immidiately I fetch different value 1351386000. But maybe it works in that way, that FROM_UNIXTIME() converts timestamp into string '2012-10-28 02:00:00' and this value is of course ambigous. If this works this way, how to store into TIMESTAMP column value of 1351382400 timestamp? – sporak Nov 09 '12 at 21:50
  • I think that `time_zone` affect only conversion of inserted date. But when I'm storing unique GMT moment (1351382400) I don't need any time-conversion. I'm giving unique timestamp, so I think `time_zone` won't take any affect. I have feeling that MySQL changes timestamp 1351382400 into 1351386000 and I have no possibility to affect it. – sporak Nov 09 '12 at 21:58
  • Your previous comment above is correct, "*FROM_UNIXTIME() converts timestamp into string '2012-10-28 02:00:00' and this value is of course ambiguous.*" If you already have a timestamp and want to insert without any conversion, just store it as an integer. If you want MySQL to handle timezone issues for you, **make sure you set `time_zone` appropriately**. – eggyal Nov 09 '12 at 22:01
  • OK, thanks for you replies. I was wondering if there's any date/time type in MySQL, that can do that job for me - it means to store timestamps but displays me human readable strings of timestamp, since in 99.99% of cases is format 'yyyy-mm-dd hh:mm:ss' unambiguous. – sporak Nov 09 '12 at 22:11
  • @sporak: How can it convert a timestamp to a human readable format without knowing the desired timezone? If you supply the timezone, that's precisely what `TIMESTAMP` does. – eggyal Nov 09 '12 at 22:23
  • Yes, you're right. The date/time type I'm looking for is type I can store timestamps into it and when the value is displaying it will be shown in `time_zone` that is set for current session/connection. But `TIMESTAMP` doesn't seems to me that can do this job for me, because I can't store timestamp 1351382400 into it so that I could fetch this value back. – sporak Nov 09 '12 at 22:39
  • @sporak: If you absolutely must give MySQL a timestamp to insert, then you must handle timezone conversion yourself (outside of MySQL). Therefore you don't want MySQL to treat the value as a timestamp, so use `INT` instead of `TIMESTAMP`. Then, when fetching the data, use `FROM_UNIXTIME()` on that integer having set the desired timezone of the session. However, IMHO, this is unecessarily painful! Why not just set `time_zone` and insert a date string: MySQL will handle the rest! – eggyal Nov 09 '12 at 22:57
  • But we DO have `time_zone` set properly. (At least I hope so.) The problem is, that I'm not able to set timestamp value 1351382400 into `TIMESTAMP` column, as is described in my question in edit #1. We have set `time_zone` on value 'Central Europe Time'. Timestamp 1351382400 is in our zone 2:00 in summer-time. But if I store this timestamp value, it is converted into different time 2:00 in winter-time (and that's different timestamp value 1351386000). OK, I can store timestamp as `INT` and handle it outside MySQL in application (which was according to question not requested solution). – sporak Nov 12 '12 at 16:25
  • Timestamp and timezone handling are broken in MySQL, it is impossible to correctly handle/insert ambiguous timestamps. I am afraid the easiest option is to set time_zone to UTC, insert the value and then change time_zone back. In this case, timestamp will be saved as it is. I know this is the old question, but I was suprised that no one gave you the right answer. – Eugene K Oct 25 '19 at 11:13
0

You could convert the date to UTC before storing in the database, then convert back to your own time zone when reading from the database.

long t = 1351382400000; // the timestamp in UTC
String insert = "INSERT INTO my_table (timestamp) VALUES (?)";
PreparedStatement stmt = db.prepareStatement(insert);
java.sql.Timestamp date = new Timestamp(t);
stmt.setTimestamp(1, date);
stmt.executeUpdate();

.....

TimeZone timezone = TimeZone.getTimeZone("MyTimeZoneId");
Calendar cal = java.util.Calendar.getInstance(timezone);
String select = "SELECT timestamp FROM my_table";
// some code omitted....
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
   java.sql.Timestamp ts = rs.getTimestamp(1);
   cal.setTimeInMillis(ts.getTime());
   System.out.println("date in db: " + cal.getTime());
}
mxns
  • 199
  • 1
  • 7
  • i am using this to download financial tick data from various time zones, then use them in trading simulations – mxns Nov 05 '12 at 16:28