London DST
London time changed with Daylight Saving Time (DST) ending on 2013-10-27 when 2 AM became 1 AM (again).
Features, Not Bugs
The documentation for java.util.Calendar in its discussion at top explains that unlike set(), add() forces an immediate recomputation of the calendar's milliseconds and all fields.
Furthermore, the documentation for java.util.GregorianCalendar on the add
method notes that smaller units of time are not adjusted. The doc specifically notes that HOUR
is a smaller field that DAY_OF_MONTH
, so it is not adjusted. Meaning that you started with an hour of 23 so you'll end up with an hour of 23, with milliseconds-since-epoch recomputed as needed.
The behaviors you see for the set
and add
methods are both correct. Features, not bugs.
Indirect Answer
The bundled java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat are notoriously troublesome, confusing, and tricky. They have flaws in both design and implementation.
I understand your interest in "to play with Java's built-in capabilities". While that interest is generally laudable, in this specific corner of Java, it is a waste of time. Even Sun and Oracle have given up on these classes. Java 8 brings a whole new java.time.* package, defined by JSR 310, inspired by Joda-Time, and supplanting the old bundled classes.
If you cannot yet move to Java 8, use Joda-Time. Joda-Time works in multiple versions of Java, and continues to work in Java 8 as it is actively maintained.
Example Code
Some example code using Joda-Time 2.3 to get you going.
Some Notes…
A Joda-Time DateTime actually knows its own time zone. In contrast, a java.util.Date has no time zone yet its toString
method applies the JVM's default time zone which creates no end of confusion.
Note in this example that dateTime_Boston
and dateTime_London
have the same number of milliseconds-since-epoch.
Joda-Time by default uses standard ISO 8601 format for string output, like this 2014-02-13T10:32:28.131+05:30
.
The +
or -
at the end marks a time zone offset from UTC/GMT. Do not read this as an operand in a formula. Read this as a label saying, for example, "India has a time zone offset of +05:30, so the date-time displayed is five and a half hours ahead of UTC/GMT".
- PLUS SIGN ("+") means ahead of UTC/GMT.
- HYPHEN-MINUS ("-") means behind UTC/GMT.
The Z
at the end is pronounced "Zulu" and is shorthand for +00:00
. That means UTC/GMT time zone, that is, no time zone offset.
// Specify a time zone rather than rely on default.
DateTimeZone timeZone_Boston = DateTimeZone.forID( "America/New_York" );
DateTimeZone timeZone_London = DateTimeZone.forID( "Europe/London" );
DateTime dateTime_Boston = new DateTime( 2013, 10, 27, 22, 51, 12, timeZone_Boston );
DateTime dateTime_London = dateTime_Boston.toDateTime( timeZone_London );
DateTime earlier_London = dateTime_London.minusDays( 2 ); // Use '2' to get us before DST change.
DateTime earlier_UtcGmt = earlier_London.toDateTime( DateTimeZone.UTC );
Dump to console…
System.out.println( "dateTime_Boston " + dateTime_Boston );
System.out.println( "dateTime_London " + dateTime_London );
System.out.println( "earlier_London " + earlier_London );
System.out.println( "earlier_UtcGmt " + earlier_UtcGmt );
When run…
dateTime_Boston 2013-10-27T22:51:12.000-04:00
dateTime_London 2013-10-28T02:51:12.000Z
earlier_London 2013-10-26T02:51:12.000+01:00
earlier_UtcGmt 2013-10-26T01:51:12.000Z