0

I'm all clear on the fact that java.util.Date doesn't have timezone and why so.

I've an application where the user has set his TimeZone, and when he selects Date in a DateTime picker, the component returns a Date object to me. I then need to modify the date as to save the UTC equivalent in database.

It's pretty simple to get a Date object representing UTC date for a local timezone with org.joda.time :

public static final Date getTimeZoneDependantDate(Date pDateUtc, String pUserTimezoneValue) {
    // Build the DateTime Object
    DateTime originalDate = new DateTime(pDateUtc.getTime(), DateTimeZone.forID(PREF_TIMEZONE_DEF_VALUE));
    // Convert the Date
    DateTime convertedDate  = originalDate.withZone(DateTimeZone.forID(pUserTimezoneValue));
    // Return the localTime associated with the timeZone
    return convertedDate.toLocalDateTime().toDate();
}

But I'm stuck on how to do the opposite, change the Date picked by the user (from his timezone perspective) to UTC. Since LocalDateTime takes the instant and not the local Date as a parameter.

Is there any cleaner way than to parse String ?

TheBakker
  • 2,852
  • 2
  • 28
  • 49
  • 1
    I am not really convinced that you have understood that `java.util.Date` has no timezone. It has to be handled like an instant/moment. Therefore your whole code is useless. If your first parameter is `Date` and you want to return `Date` without time manipulation (i.e. changing the moment) then this is an identical operation (just `return pDateUtc;`). An instant is the same in any timezone! – Meno Hochschild Dec 19 '16 at 16:29
  • I must not be clear enough. I'm using a DatePicker where the user pick a date in a calendar and write a time in string. The component (I can't modify its code to take into account the user timezone) returns a java.util.Date matching the user selection to my controller. From then I need my controller to calculate the instant that would match the local date the user selected from his timezone point of view. In a better world, the component would be mindful of the timezone and would return the correct Date object. – TheBakker Dec 19 '16 at 16:33
  • Well, according to your description, the user selects a local calendar date and a local clock time. And then the `DatePicker`-component yields a `Date`-object which is an instant. That means, this component applies at least internally a timezone conversion. Maybe you have to set the timezone on the `DatePicker`-component instead. But manipulating the `java.util.Date`-object returned by `DatePicker` is definitely the wrong way. – Meno Hochschild Dec 19 '16 at 16:41
  • I know it's wrong, and as I said I cannot modify the datepicker. It takes the server timezone, but the user input the date thinking of his timezone. I'm looking for a way to do the opposite as what `.toLocalDateTime().toDate(); `from jodatime do. – TheBakker Dec 19 '16 at 16:44
  • Sorry for this terrible design. Is your server timezone UTC? – Meno Hochschild Dec 19 '16 at 16:51
  • Yep it sucks, and to make it worse, you guessed it, my server timezone is not even UTC, but the database is. Well meanwhile I'm doing dirty string manipulation to do it, it works but was hoping for something from Jodatime ... `public static final Date getUtcDate (Date pDateToUpdate, String pUserTimezoneValue) { SimpleDateFormat sdf = new SimpleDateFormat(); String timeZonedDate = sdf.format(pDateToUpdate); sdf.setTimeZone(TimeZone.getTimeZone(pUserTimezoneValue)); return sdf.parse(timeZonedDate); }` – TheBakker Dec 19 '16 at 16:55

2 Answers2

1

tl;dr

You do not provide enough info. Report:

  • The results of this: myJavaUtilDate.toInstant().toString()
  • The inputs to the component
  • The current default time zone of the app

Details

You do not really give enough information about your problem. Are you getting a correct date-time value in UTC via the java.util.Date object from your GUI component or not?

If the user in Québec time zone of America/Montreal entered 9 AM on December 1, 2016, and your component is correctly adjusting those values into UTC while producing the java.util.Date object, then you have no problem. The UTC value would be 2 PM in the afternoon for UTC, as America/Montreal is five hours behind UTC on that particular date. Just pass the Date object to the database after converting to a java.sql.Timestamp object.

FYI, both the old date-time classes classes (Date & Calendar etc.) and Joda-Time are now supplanted by the java.time classes. Here is some example code in java.time showing the kind of behavior your component is hopefully employing.

LocalDate ld = LocalDate.of ( 2016 , Month.DECEMBER , 1 );
LocalTime lt = LocalTime.of ( 9 , 0 );
ZoneId z = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.of ( ld , lt , z );
Instant instant = zdt.toInstant (); // UTC

System.out.println ( "zdt.toString(): " + zdt );
System.out.println ( "instant.toString(): " + instant );

zdt.toString(): 2016-12-01T09:00-05:00[America/Montreal]

instant.toString(): 2016-12-01T14:00:00Z

The Z on the end of the string is short for Zulu and means UTC.

Given the unfortunate behavior of java.util.Date::toString to apply your current default time zone while generating the string, I suggest you convert your Date to an Instant so you can get a clear reading of its value.

Instant instantConvertedFromDateOfComponent = myJavaUtilDate.toInstant();

If after this step you do indeed see 2 PM in the afternoon, then all is well and your component is performing well.

If your component is acting badly, ignoring the issue of time zone and reporting your user’s input as if the user intended UTC as their own zone, then you will see 2016-12-01T09:00:00Z. That is a problem. The workaround is to make the time zone adjustment yourself. Extract the “local” (zone-less) values, then apply the intended time zone.

To get the “local” date and time, first convert to OffsetDateTime object.

OffsetDateTime odt = instantConvertedFromDateOfComponent.atOffset( ZoneOffset.UTC );
LocalDateTime ldt = odt.toLocalDateTime();  // 2016-12-01T09:00:00Z
ZonedDateTime zdt = ldt.atZone( z );  // 2016-12-01T09:00-05:00[America/Montreal]

If your JDBC driver complies with JDBC 4.2 or later, you may be able to pass these java.time types directly. If not, convert to java.sql types via new methods added to the old classes. Discussed already many times on Stack Overflow, so search for more info.

The server’s current default time zone should be irrelevant to your programming. Always specify explicitly the desired/expected time zone in optional arguments rather than rely implicitly on default.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

  • Java SE 8 and SE 9 and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

The best way of transmitting and saving the date over network is in the form of timestamp (date in milliseconds) this timestamp does not need any timezone information. But after fetching the date in the form of timestamp use it create date object, If you want to show the date in the UTC format then only you need to convert it.

Kiran Kumar
  • 1,033
  • 7
  • 20
  • As I wrote, my issue is with a dateTimepicker object returning a Date Object to me that the user pick depending on his timezone. My controller needs to change this before pushing the info to the business layer. What I'm looking for is the easiest way to convert this local Date to the correct UTC date it's supposed to be. – TheBakker Dec 19 '16 at 16:00
  • How you are transmitting into the business layer? – Kiran Kumar Dec 19 '16 at 16:03
  • @TheBakker There is no "local" `Date`. It is already UTC in arithmetic sense (although its method `toString()` is implemented in a crazy way. – Meno Hochschild Dec 19 '16 at 16:33