Fractaliste
Fractaliste

Reputation: 5957

How to keep original timezone with JodaTime

I have date in String format I need to parse. The format is as following with timezone from all over the world :

String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";

It seems that java.util.Date doesn't accept timezone with : separator. So I'm trying with Jodatime library :

DateTime formattedDate = DateTimeFormat.forPattern(pattern).parseDateTime(stringDate);
LocalDateTime formattedDate2 = DateTimeFormat.forPattern(pattern).parseLocalDateTime(stringDate);
MutableDateTime formattedDate3 = DateTimeFormat.forPattern(pattern).parseMutableDateTime(stringDate);

System.out.println(formattedDate);
System.out.println(formattedDate2);
System.out.println(formattedDate3);

These lines output :

2016-04-29T09:46:49.222+02:00
2016-04-29T12:16:49.222
2016-04-29T09:46:49.222+02:00

As far as I understand the formatter modify output timezone to comply on mine (I'm in Paris, UTC+2), but I want the output keep its original timezone. Is it possible to do it with Jodatime library? Or should I change for another?

Edit : Actually I need to get a Date object on which the timezone offset would be 270 (the timezone offset of the stringDate : 4 hour and 30 minutes) in place of 120 (my local timezone offset):

System.out.println(formattedDate.toDate().getTimezoneOffset()); // I expect 270 but I get 120

Upvotes: 0

Views: 1759

Answers (2)

Adam Michalik
Adam Michalik

Reputation: 9945

What you missed is DateTimeFormatter#withOffsetParsed:

Returns a new formatter that will create a datetime with a time zone equal to that of the offset of the parsed string.

Otherwise the formatter will parse it into your local time zone (surprising, I know).

@Test
public void preserveTimeZone() {
    String stringDate = "2016-04-29 12:16:49.222+04:30";
    String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";

    DateTime dt = DateTimeFormat.forPattern(pattern).withOffsetParsed().parseDateTime(stringDate);

    System.out.println(dt); // prints "2016-04-29T12:16:49.222+04:30"
}

As for your edit - java.util.Date does not hold time zone information and the deprecated getTimezoneOffset() method only

Returns the offset, measured in minutes, for the local time zone relative to UTC that is appropriate for the time represented by this Date object.

So you'd better use Joda Time or java.time classes to handle time zones properly.

Upvotes: 4

Scott Fines
Scott Fines

Reputation: 779

When I run the same code that you have posted, I end up with

2016-04-29T02:46:49.222-05:00
2016-04-29T12:16:49.222
2016-04-29T02:46:49.222-05:00

which if you will notice, has different hour values AND time-zone values. However, if you look at their millis:

System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());

you'll see the output

1461916009222
1461950209222
1461916009222

So they have the same epoch time, but are printed out differently. This is due to the mechanism of toString() on DateTime objects, and how they are to be interpreted.

DateTime and LocalDateTime(MutableDateTime is just a mutable version of DateTime) deal with the same epoch time in different ways. LocalDateTime will always assume that epoch time is UTC time(per the javadoc for LocalDateTime), while DateTime will assume that epoch is represented in the time zone of the Chronology which it holds(per the javadoc again). If the TimeZone is not specified at construction time, then the Chronology will assume that you want the timezone of your default Locale, which is set by the JVM. In your case, the default Locale is Paris France, while mine is St. Louis USA. Paris currently holds a +2:00 time zone offset, while St. Louis has -5:00, leading to the different time zone representations when we print it.

To get even more annoying, those offsets can change over time. If I come back in 6 months and try to answer this again, my values will show -6:00 (stupid Daylight savings time!)

The important thing to remember is that these two dates have the same epoch time: we are talking about the same instant in time, we are just representing that time differently when we print it out.

If you want to use a different time zone for representing the output of the parse result, then you can set the DateTimeZone during formatting using DateTimeFormat.withZone() or DateTimeFormat.withLocale:

DateTimeFormatter sdf = DateTimeFormat.forPattern(pattern).withZone(DateTimeZone.forOffsetHoursMinutes(4,30));
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());

which will print

2016-04-29 12:16:49.222+0430
2016-04-29 12:16:49.222
2016-04-29 12:16:49.222+0430

notice that the LocalDateTime version still prints out without the TimeZone. That's kind of the feature of LocalDateTime: it is represented without having to deal with all this business.

So that is why your printing values look weird. To further your question about getting a java.util.Date object from the parsed DateTime object: toDate will give you a java.util.Date which represents the same epoch time. However, java.util.Date behaves similarly to DateTime, in that unless otherwise stated, it will use the TimeZone of the default Locale. If you know the Locale ahead of time, then you can use the toDate(Locale) method to ensure you use that Locale's TimeZone offset.

It gets a lot harder if you don't know the TimeZone ahead of time; in the past, I've had to hand-parse the TimeZone hour and minute offsets to determine the proper TimeZone to use. In this exact case that's not too difficult, since the last 6 characters are extremely well-formed and regular(unless, of course, they aren't :)).

Upvotes: 0

Related Questions