1

Hi I have a following code below:

public Date convertFromGMTToLocal(Date date) {
     return new Date(date.getTimezoneOffset() + newOffset*60*1000);  
}

public Date convertFromLocalToGMT(Date date) {
    return new Date(date.getTime() - date.getTimezoneOffset()*60*1000);
}

convertFromLocalToGMT is supposed to strip off timezone information and convertFromGMTToLocal is supposed to put the timezone information back. I understand java.util.Date is representing the time in epoch and always in GMT however when displaying the date it is defaulted to JVM's default timezone.

Explanation I got was that if your timezone is CST and it is 10:00AM CST you are changing timezone to GMT with convertFromLocalToGMT so you're essentially adding the offset to get GMT so you get 4:00AM CST (offset is -6) and using convertFromGMTToLocal will convert this Date object back to 10:00AM regardless of your timezones (the most confusing part how?). How does above work? I am confused...

Thanks.

user_1357
  • 7,766
  • 13
  • 63
  • 106

3 Answers3

8

You should pretty much never add an offset to a Date to create another Date - you'd only ever do that if you received broken data to start with.

You should not use this code. It is bad code which tries to treat java.util.Date in a way it was not designed for. If you want to represent a date in a particular time zone, either use Calendar (urgh) or the far better Joda Time API.

In particular, the code you've got will not work around time zone transitions - because the offset at date.getTimeZoneOffset() still considers date to be UTC (because that's what it's defined as) even you're treating it as a local date/time.

Ignore the value that's displayed by Date.toString() - avoid using that method. Either display using SimpleDateFormat with appropriate settings for the time zone you're interested in, or (better, again) use Joda's DateTimeFormat class.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • The problem we had was we wanted to get rid of notion of "timezone" form the date. So if we get 10:00AM today no matter which timezone you're at we should display 10:00AM. In order to achieve this above transition was necessary, treating them all in GMT will allow us to display this Date as 10:00AM regardless of the timezone. – user_1357 Aug 12 '11 at 22:33
  • @Mayumi: In that case you should use `LocalDateTime` or `LocalTime` from Joda. If you *must* use `Date`/`Calendar`, then simply use UTC for *all* operations, and remember that you've done so. But you don't need to *convert* from a local time zone to do that - simply parse it in UTC to start with. (I'm assuming you received this value in text form somehow. If you could be clearer on that point, it would really help.) – Jon Skeet Aug 12 '11 at 22:35
  • +1 - The OP's code should be redesigned / rewritten to use a better date representation class. – Stephen C Aug 12 '11 at 22:36
  • +1 I really think Joda Time should just be packaged with the next major version of java – Liviu T. Aug 12 '11 at 22:43
  • "simply use UTC for all operations" isn't Date already in UTC? What I am most confused about is the behavior of java.util.Date. When you (date.getTime() - 300 (CST)*60*1000) what is the behavior? Date does come with timezone offset information which is defaulted to machine time, for example if you save (new Date()) into datastore and retrieve it in machine with different timezone would this offset update? – user_1357 Aug 13 '11 at 00:52
  • @Mayumi: Yes, `Date` is always in UTC, but if you *parse* some text that doesn't specify a time zone, you have to give information about which time zone to consider it as part of. Date itself does *not* include any time zone offset information - it's used when *displaying* the information, but it's not part of the information within the `Date` itself. If you use `date.getTime() - 300*60*1000` that gets you something I call `LocalInstant` in Noda Time, but which doesn't really have a fixed terminology in Java. What do you mean by "what is the behaviour"? (Continued.) – Jon Skeet Aug 13 '11 at 05:57
  • Fundamentally a `Date` with a value of `date.getTime() - 300*60*1000` represents the *UTC instant* 5 hours before the original one. That's *not* the same thing as representing a local time. Again, just don't do it - it's using `Date` in a way which it was not intended. – Jon Skeet Aug 13 '11 at 05:58
  • @Jon: We are using GWT which doesn't work with Joda or Calendar. This is for a scheduling application. When someone books an appointment for 10:00AM, we want it to display as 10:00AM no matter where the user (or server) is. It will be implied that the time is in the vendor's time zone. By the same token, we don't want the vendor to create a bunch of appointments, then realize that they set the timezone wrong, correct the mistake, and have all the appointments shift. (This is different behaviour from Google Calendar, for example, where the appointments all shift if you change the time zone.) – user_1357 Aug 15 '11 at 16:28
  • This is why we don't store in the vendor's timezone either but instead, convert all appointments to a common timezone (in this case, GMT). – user_1357 Aug 15 '11 at 16:28
  • @Mayumi: Okay, that sounds like the right approach - but how do you receive the information to start with? Basically, if you receive it as text in the form "10:00AM" then you should be parsing that *in UTC*. It's not really clear how your data is flowing at the moment or where conversions are taking place. – Jon Skeet Aug 15 '11 at 16:39
  • @Mayumi: Btw, it looks like there are various ports of Joda Time for GWT. I don't know how complete or valid any of them are. – Jon Skeet Aug 15 '11 at 16:41
4

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Also, quoted below is a notice from the home page of Joda-Time:

Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

You can solve it using the following functions in java.time, the modern Date-Time API :

  1. LocalDateTime#atZone(ZoneId)
  2. ZonedDateTime#withZoneSameInstant(ZoneId)

Demo:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

/*
 * Change the JVM's ZoneId, ZoneId.systemDefault() to the required ZoneId e.g. ZoneId.of("America/New_York")
 */
public class Main {
    public static void main(String[] args) {
        // Test
        ZoneId zoneId = ZoneId.of("Etc/UTC");
        ZonedDateTime zdt = ZonedDateTime.of(LocalDate.of(2021, 5, 10), LocalTime.of(15, 20), zoneId);
        System.out.println(zdt + " is " + convertFromZdtToLdt(zdt) + " at " + ZoneId.systemDefault().getId());

        LocalDateTime ldt = LocalDateTime.of(LocalDate.of(2021, 5, 10), LocalTime.of(15, 20));
        System.out
                .println(ldt + " at " + ZoneId.systemDefault().getId() + " is " + convertFromLdtZdtToZdt(ldt, zoneId));
    }

    public static LocalDateTime convertFromZdtToLdt(ZonedDateTime zdt) {
        return zdt.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static ZonedDateTime convertFromLdtZdtToZdt(LocalDateTime ldt, ZoneId targetZoneId) {
        return ldt.atZone(ZoneId.systemDefault()).withZoneSameInstant(targetZoneId);
    }
}

Output on my system in Europe/London timezone:

2021-05-10T15:20Z[Etc/UTC] is 2021-05-10T16:20 at Europe/London
2021-05-10T15:20 at Europe/London is 2021-05-10T14:20Z[Etc/UTC]

The Z in the output is the timezone designator for zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).

ONLINE DEMO

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

As you said, the Date object doesn't care about timezone. It stores an instant in time. The timezone is only meaningful when you want to display a date. Use a DateFormat with the specific timezone you want to use to display a date (using setTimeZone)

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255