Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate not providing date in UTC timezone

This seems like a stupid question, but I am not able to understand this creepy behavior. I am completely aware of the fact that java Date class does not store any TimeZone information in it. It just stores the number of milliseconds since January 1, 1970, 00:00:00 GMT

Thing is that, I am using MySql which is residing on a server with UTC timezone and I am also storing DateTime in UTC only. If I make a select query then I get this date 2014-01-17 16:15:49

By using http://www.epochconverter.com/ I get this:

Epoch timestamp: 1389975349
Timestamp in milliseconds: 1389975349000
Human time (GMT): Fri, 17 Jan 2014 16:15:49 GMT
Human time (your time zone): Friday, January 17, 2014 9:45:49 PM

Now comes the part of Hibernate. I am running my Java web app on a machine having IST as system timezone. I made a simple object fetch using Id and fetched createdDate property which is a Date object. I have wrote a simple code to understand its output, here is the code:

Date dt = c.getCreatedDate();
System.out.println(dt.getTime());
System.out.println(dt);
DateFormat df = new SimpleDateFormat("dd/MM/yyyy  hh:mm a z");
df.setTimeZone(TimeZone.getTimeZone("IST"));
System.out.println(df.format(dt));
df.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(df.format(dt));

And following is the output for this:

1389955549000
2014-01-17 16:15:49.0
17/01/2014  04:15 PM IST
17/01/2014  10:45 AM UTC

If you put this 1389955549000 in http://www.epochconverter.com/ then you get following output:

GMT: Fri, 17 Jan 2014 10:45:49 GMT
Your time zone: Friday, January 17, 2014 4:15:49 PM GMT+5.5

This is not the expected output, right. It is giving me time in millis which is -5:30 from the UTC time, so If I try to get time in IST timezone then it actually gives me time which is in UTC

Does anyone got idea where I am doing wrong?

--------------------------How I fixed it----------------------------

Based on suggestions from - Ako and Basil Bourque

Hibernate takes system timezone into consideration while fetching date/time fields from database. If you have stored DateTime in UTC in database but your system time or for least your java app timezone is in other timezone(e.g, IST - Indian Standard Time) then hibernate thinks that DateTime stored in database is also in IST and this is what causes whole problem.

For this as Basil suggested, use same timezones accross different servers. UTC should be preferred. Fix that I applied is that I added following code:

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

in ServletContextListener which made my app timezone in UTC and now hibernate is fetching and storing dates as expected.

like image 700
Abhi Avatar asked Mar 21 '23 22:03

Abhi


1 Answers

Confusing Question

Your question could use some rewriting.

If I make a select query – You should explain this and give the exact query statement.

Red Herring

Since dealing with two separate servers (database server, web app server), each with a different time zone setting, you should separate the question more cleanly. Indeed, the MySQL server seems to be just a red herring, a distraction.

Instead of talking about the irrelevant MySQL server, you should have tested and reported the actual time. Easy to do… Just google "current time in utc".

Thus the old sailors' adage: Use one compass or three, but never two.

Time Zones

Three-letter time zone codes are outmoded, being neither standardized nor unique. "IST" means "India Standard Time" and "Irish Standard Time", for example.

Use time zone names, as seen in this slightly outdated list. In your case, +05:30, you could use "Asia/Kolkata", also known as "Asia/Calcutta" in the older tables. That is 5.5 hours ahead of UTC/GMT.

Joda-Time

The java.util.Date & Calendar classes are notoriously bad and confusing, as you've come to see. Avoid them. Use either the open-source third-party Joda-Time or, in Java 8, the new java.time.* classes (inspired by Joda-Time).

The code below uses Joda-Time 2.3 and Java 8 on a Mac with US west coast time zone.

Baseline

Let's establish that 1389975349000L ≈ 16:15 UTC ≈ 21:45 India.

This agrees with EpochConverter.com, as the question stated.

// Specify a time zone rather than depend on defaults.
DateTimeZone timeZoneKolkata = DateTimeZone.forID( "Asia/Kolkata" );

long millis = 1389975349000L;
DateTime dateTimeUtc = new DateTime( millis, DateTimeZone.UTC );
DateTime dateTimeKolkata = dateTimeUtc.toDateTime( timeZoneKolkata );

Dump to console…

System.out.println( "millis: " + millis );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeKolkata: " + dateTimeKolkata );

When run…

millis: 1389975349000
dateTimeUtc: 2014-01-17T16:15:49.000Z
dateTimeKolkata: 2014-01-17T21:45:49.000+05:30

Mystery Number

The question mentions a second number: 1389955549000L.

That number turns out to be the same date as the first number, with different time.

Let's establish that 1389955549000L ≈ 10:45 UTC ≈ 16:15 India.

long mysteryMillis = 1389955549000L;
DateTime mysteryUtc = new DateTime( mysteryMillis, DateTimeZone.UTC );
DateTime mysteryKolkata = mysteryUtc.toDateTime( timeZoneKolkata );

Dump to console…

System.out.println( "mysteryMillis: " + mysteryMillis );
System.out.println( "mysteryUtc: " + mysteryUtc );
System.out.println( "mysteryKolkata: " + mysteryKolkata );

When run…

mysteryMillis: 1389955549000
mysteryUtc: 2014-01-17T10:45:49.000Z
mysteryKolkata: 2014-01-17T16:15:49.000+05:30

Conclusion

I'm not 100% sure, but…

→ Your web app server machine seems to have its clock set improperly, set to UTC time rather than India time.

The web app server is apparently let to 16:15 time in the India time zone, but apparently at that moment the true time in India was 21:45. In other words, the time did not match the time zone.

Mixing UTC time with non-UTC time zone = WRONG.

If you set an Indian time zone, then set an Indian time to match.

Details

Note that we have "16:15" in common in both sets of numbers.

The java.util.Date class has a very bad design where it has no time zone information itself BUT applies the Java Virtual Machine's default time zone in its implementation of toString. This is one of many reasons to avoid this class, but may be key to your problem.

Lessons Learned

Avoid java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat classes. Use only as required for exchanging values as needed with other classes.

Use Joda-Time or the new java.time.* classes (JSR 310).

Specify a time zone, never rely on default time zone.

Match the time to time zone on each server. Verify by googling "current time in utc".

Set host server’s operating system’ time zone to UTC or GMT where possible. No such choice on some OSes, so choose "Atlantic/Reykjavik" as a workaround, because Iceland stays on UTC/GMT year-round without any Daylight Savings Time nonsense.

Do not set the JVM’s default time zone with a call to TimeZone.setDefault (except as a last resort in the worst situations). Setting the default is rude, as it immediately affects all the code in all the threads in all the apps running in that JVM. And it is unreliable as any of that other code can change it on your app, during runtime. Instead, specify a time zone in all your date-time code. Never rely implicitly on the JVM’s current default. Both Joda-Time and java.time have methods that take an argument of time zone. So, while it is a good practice to set all your server computers’ host OS’ time zone to UTC, you should never depend on that in your Java code.

like image 163
Basil Bourque Avatar answered Apr 02 '23 17:04

Basil Bourque