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.