London time changed with Daylight Saving Time (DST) ending on 2013-10-27 when 2 AM became 1 AM (again).
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.
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.
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".
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 );
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 );
Thank you. Your examples are using the Joda time library, right? I am pretty sure but just making sure.
@peter.petrov Yep, Joda-Time 2.3, that's what it says under "Example Code" heading.