0

I have following 2 input strings:

String string1 = "2017-01-30T13:00:00+0000"
String string2 = "2018-06-23T16:00:00+0000"

For string1 I am getting following as a date when I do:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date startDate = formatter.parse(string1);

result - Mon Jan 30 13:00:00 GMT 2017

But when I do the same for string2 I get the following in my debugger:

Sat Jun 23 17:00:00 BST 2018

Why am I getting different timezones here?

java123999
  • 6,974
  • 36
  • 77
  • 121
  • 1
    Probably as it's using your default Locale and the Uk switches between timezones in accordance with the date. – d.j.brown Dec 05 '17 at 15:05
  • yes same formatter, @d.j.brown how can I solve this so both dates are always in GMT? – java123999 Dec 05 '17 at 15:07
  • 1
    `formatter.setTimeZone(TimeZone.getTimeZone("GMT"));` – d.j.brown Dec 05 '17 at 15:08
  • 6
    Better to use UTC than GMT - the intent is much clearer. Note that it's not that the UK "switches between time zones" - it's that the Europe/London time zone switches between GMT and BST over time. – Jon Skeet Dec 05 '17 at 15:08
  • thanks @JonSkeet is UTC effectively always the same as +0000? – java123999 Dec 05 '17 at 15:10
  • that has worked @d.j.brown , if you answer I can accept it – java123999 Dec 05 '17 at 15:11
  • 1
    @JonSkeet thank you for being more technically correct – d.j.brown Dec 05 '17 at 15:12
  • 1
    You are using the long outdated and notoriously troublesome `SimpleDateFormat` class. Any particular reason? I recommend you throw it out and use [`java.time`, the modern Java date and time API also known as JSR-310](https://docs.oracle.com/javase/tutorial/datetime/), instead. It is much nicer to work with. And will not show the surprising behaviour you have experienced. – Ole V.V. Dec 05 '17 at 17:03
  • 1
    In other words, BST is not a time zone. I suppose in this context it means British Summer Time, which can be considered half a time zone (BST may also mean Bangladesh Standard Time and Bougainville Standard Time and more). – Ole V.V. Dec 05 '17 at 17:38

1 Answers1

2

tl;dr

OffsetDateTime.parse( "2017-01-30T13:00:00+0000" )

java.time

Among the many design flaws of the troublesome old legacy classes you are using is that the Date::toString method applies your JVM’s current default time zone while generating the string. So while the value internally is actually in UTC, it confusingly appears to have another time zone. Add to that that your current default time zone experiences a Daylight Saving Time cutover between your two dates.

Instead use modern java.time classes. Parse your input strings as OffsetDateTime objects.

OffsetDateTime odt = OffsetDateTime.parse( "2017-01-30T13:00:00+0000" ) ; 

A bug in Java 8 and Java 9 might bite on parsing your offset-from-UTC lacking a colon between hours and minutes. As a workaround, add the colon.

OffsetDateTime odt = OffsetDateTime.parse( "2017-01-30T13:00:00+0000".replace( "+0000" , "+00:00" ) ; 

Call toString to generate a String in standard ISO 8601 format. Result is not adulterated by any time zone dynamically applied, unlike Date.

If desired, assign a ZoneId to get a ZonedDateTime.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as BST or EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "Europe/London" ) ; 
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

See complete code for both inputs run live at IdeOne.com.

String input2017 = "2017-01-30T13:00:00+0000".replace( "+0000" , "+00:00" ) ;  // Workaround Java 8 bug where omitted colon in offset-from-UTC fails to parse. Fixed in Java 9.
OffsetDateTime odt2017 = OffsetDateTime.parse( input2017 ) ; 

String input2018 = "2018-06-23T16:00:00+0000".replace( "+0000" , "+00:00" ) ;  // Workaround Java 8 bug where omitted colon in offset-from-UTC fails to parse. Fixed in Java 9.
OffsetDateTime odt2018 = OffsetDateTime.parse( input2018 ) ; 

ZoneId z = ZoneId.of( "Europe/London" ) ; 
ZonedDateTime zdt2017 = odt2017.atZoneSameInstant( z ) ;
ZonedDateTime zdt2018 = odt2018.atZoneSameInstant( z ) ;

Dump to console.

System.out.println( "input2017: " + input2017 ) ;
System.out.println( "odt2017: " + odt2017 ) ;
System.out.println( "zdt2017: " + zdt2017 ) ;
System.out.println( "" ) ;  // Blank line.

System.out.println( "input2018: " + input2018 ) ;
System.out.println( "odt2018: " + odt2018 ) ;
System.out.println( "zdt2018: " + zdt2018 ) ;

Note how in June 2018, Europe/London time jumps forward an hour because of Daylight Saving Time (DST). In winter, London zone is on UTC but in summer, one hour ahead of UTC.

2017

input2017: 2017-01-30T13:00:00+00:00

odt2017: 2017-01-30T13:00Z

zdt2017: 2017-01-30T13:00Z[Europe/London]

2018

input2018: 2018-06-23T16:00:00+00:00

odt2018: 2018-06-23T16:00Z

zdt2018: 2018-06-23T17:00+01:00[Europe/London]

Search Stack Overflow for much more info as this topic has been handled many many times already.


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.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

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.

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