Why the date generated with below code offset the given time to 11:00:00.00?
test("shouldReturnGivenMockedDateTime") {
val mockedDateTime = "2020-01-01T10:00:00.00Z"
val clock: Clock = Clock.fixed(Instant.parse(mockedDateTime), TimeZone.getDefault.toZoneId);
val result = LocalDateTime.ofInstant(clock.instant, TimeZone.getDefault.toZoneId)
assert(result.toString == "2020-01-01T10:00") // FALSE!!!
assert(result.toString == "2020-01-01T11:00") // TRUE
}
Why the date generated with below code offset the given time to
11:00:00.00?
Your mock date is 1st January, 2020. According to your link, Belgrade was at offset UTC+1h on this date. From 27th October 2019 until 29th March 2020, more precisely. The mocked date and time is also in UTC, denoted by the trailing Z. When querying the time in your local time zone, Europe/Belgrade, 1 hour is added to the UTC time, so 10:00 becomes 11:00.
You are correct, of course, that Belgrade is at offset +02:00 here in May (because of summer time/DST). Only when converting a date and time in January, the offset that was valid back then is used, not the offset for May.
Repeating your link: 2020 Time Zones - Belgrade
Related
I have a problem with the dates and their passage from Date to LocalDate, mainly the error is with the dates that have the year 1700.
I have tried two different dates and whenever the year is 1700, it puts me one day less.
Date dto = ...;
Instant inst = dto.toInstant();
LocalDate localDate = LocalDate.ofInstant(inst, ZoneId.systemDefault());
Date dto It is a variable that retrieves the date of a query. But the method only gets dto variable as input parameter.
Tue Dec 14 00:00:00 CET 1700 -> Date
1700-12-13T23:00:00Z -> Instant
1700-12-13 -> LocalDate
Sat Jan 01 00:00:00 CET 1994 -> Date
1993-12-31T23:00:00Z -> Instant
1994-01-01 -> LocalDate
I use:
jackson.time-zone: ${TZ:Europe/Madrid}
What is the problem with this year?
This is because whoever produced that Date and ZoneId.systemDefault() have different opinions on what Madrid's offset is from UTC, back in 1700.
Whoever produced that Date mistakenly thinks that Madrid is UTC+1 back in 1700-12-14, so it produced a Date that represents the midnight of 1700-12-14 in the zone UTC+1 ("CET"):
Tue Dec 14 00:00:00 CET 1700
This has a corresponding Instant, because we can pinpoint a certain point in time using this information. This is what toInstant gives you. Instant.toString always displays in UTC (more specifically ISO 8601), so you see:
1700-12-13T23:00:00Z
1700-12-14T00:00:00+01:00 is indeed the same point in time as 1700-12-13T23:00:00Z.
When you do LocalDate.ofInstant, you use ZoneId.systemDefault(). ZoneId.systemDefault() thinks that Madrid had the offset of UTC-0:14:44. This is because Madrid had been observing Local Mean Time, and did not standardise timezones until the 1900s.
Therefore, LocalDate.ofInstant subtracts 14 minutes and 44 seconds from 1700-12-13T23:00:00 to compute what the date would be at offset -0:14:44, and lo and behold, it is still 1700-12-13.
I would recommend that you not to use Date if possible. If what you are doing is related to time at all, you should work with LocalDates directly.
I have a problem with converting dates between Instant and LocalDate and back. I need to change my date to monday in its current week (if it's wednesday then I'm changing to monday):
public static Instant getStartDateAsMonday(Instant startTime) {
LocalDate monday = startTime.atZone(ZoneId.systemDefault()).toLocalDate()
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
startTime = monday.atStartOfDay(ZoneId.systemDefault()).toInstant();
return startTime;
}
When I test it ...
#Test
public void testGetStartDateAsMonday() {
Instant instant = DateHelperService.getStartDateAsBeginningOnMonday(
Instant.parse("2020-05-27T00:00:00Z"));
assertThat(instant).isEqualTo("2020-05-25T00:00:00Z");
}
... the test doesn't pass and gives the output:
Expected : 2020-05-25T00:00:00Z
Actual : 2020-05-24T22:00:00Z
My system default timezone is GMT+2. The test passes when I do atStartOfDay(ZoneId.of("UTC")), but I don't understand why I can't use my system default in that conversion.
Firstly, calling ZoneId.systemDefault() twice is bad practice. The JVM’s current default time zone may have changed between calls. Any code in any thread of any app within that JVM can change the current default time zone with immediate effect.
So, capture the current default.
ZoneId z = ZoneId.systemDefault() ;
You said:
My system default timezone is GMT+2
GMT+2 represents an offset not a time zone. An offset is merely a number of hours-minutes-seconds, positive or negative. A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region. A time zone has a name in the format of Continent/Region.
You said:
Expected : 2020-05-25T00:00:00Z
Actual : 2020-05-24T22:00:00Z
Your expectation is incorrect. If you have a moment representing the first moment of the day in a zone such as Europe/Brussels or Africa/Cairo for a time-of-day of 00:00, and you know that zone runs two hours ahead of UTC on that date, then intuitively you know UTC is two hours earlier. If the clock strikes midnight in Brussels or Cairo, you know the clock cannot yet be striking midnight in UTC, at the Greenwich Royal Observatory, or in Iceland. Midnight won’t strike there for two more hours. So the date is still “yesterday” in UTC. And two hours earlier than 00:00 is 22:00. So your actual result is correct.
Let's go through it method by method:
LocalDate monday = startTime // startTime is 2020-05-27 00:00:00 as an Instant.
// 2020-05-27 02:00:00, but with GMT+2 information as it is a ZonedDateTime now.
.atZone(ZoneId.systemDefault())
// (1) 2020-05-27 02:00:00 without any zone information.
// (2) 2020-05-27 00:00:00 because hour is dropped as it's a LocalDate here.
.toLocalDate()
// Changed to 2020-05-25 00:00:00, which is what you want.
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
startTime = monday // monday is 2020-05-25 00:00:00 as LocalDate.
// You say that this is at GMT+2 and you want the start of day.
// You get a ZonedDateTime 2020-05-25 00:00:00 with information about GMT+2.
.atStartOfDay(ZoneId.systemDefault())
// Instant has no zone offset, so this is what the ZonedDateTime needs to consider.
// 2020-05-25 00:00:00 becomes 2020-05-24 22:00:00 as the +2 offset is subtracted.
.toInstant();
When you are using ZoneId.of("UTC") no problems will happen as the offset of ZoneId.of("UTC") is zero (daylight saving stuff ignored for simplicity). A datetime minus zero hours is the same datetime.
Edit: It turns out the problem is not about Jackson, but about time adjustment in Thailand on 1 April 1920.
How does com.fasterxml.jackson.databind.ObjectMapper works? I thought it used Unix timestamp.
I tried converting a java.util.Date with mapper.writeValueAsString().
When I convert the string back to Date with mapper.readerFor(Date.class).readValue(), the result is correct.
However, when I trying removing the last 3 digits and and put the same string into some converter websites, the result is off for some minutes and seconds.
Please see the code below.
Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");
System.out.println(wayBack); // Wed Jan 31 00:00:00 ICT 1900
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(wayBack)); // -2206420924000
Date deserialised = mapper.readerFor(Date.class).readValue(mapper.writeValueAsString(wayBack));
System.out.println(deserialised); // Wed Jan 31 00:00:00 ICT 1900
Below is a screenshot from http://www.onlineconversion.com/unix_time.htm
Please note that 7-hour off is expected because of my timezone but I don't understand the 17:56 minutes different.
EDIT - Here is my attempt to provide a better answer than my first one.
Background
Before looking at the code in the question, some background notes:
The epoch value (in seconds) at midnight 31st Jan 1900 in Bangkok is -2206420924:
LocalDateTime localDateTime = LocalDateTime.parse("1900-01-31T00:00:00");
ZoneId z = ZoneId.of("Asia/Bangkok");
ZonedDateTime ict_1 = ZonedDateTime.of(localDateTime, z);
System.out.println("Epoch seconds: " + ict_1.toEpochSecond());
System.out.println("ICT datetime : " + ict_1);
The above prints this:
Epoch seconds: -2206420924
ICT datetime : 1900-01-31T00:00+06:42:04[Asia/Bangkok]
The epoch value (in seconds) for UTC midnight on the same date is -1570060800:
ZonedDateTime utcDateTime = ZonedDateTime.parse("1900-01-31T00:00:00Z");
System.out.println("Epoch seconds: " + utcDateTime.toEpochSecond());
System.out.println("UTC datetime : " + utcDateTime);
The above prints this:
Epoch seconds: -2206396800
UTC datetime : 1900-01-31T00:00Z
The time at midnight in Bangkok on 31st January 1900 was 24,124 seconds further into the past than the time at midnight in Greenwich, UK (the prime meridian - or UTC).
That is to say, on that date Bangkok was 6 hours, 42 minutes and 4 seconds ahead of UTC time (or GMT as I believe it was then called - as UTC had not been established at that time).
The Specific Code in the Question
First, I changed my default time zone to match the one used in the question:
System.setProperty("user.timezone", "Asia/Bangkok");
The below line from the question does the following:
(1) The SimpleDateFormat constructor, in which the date format string does not specify a locale, uses the default locale.
(2) Then the parse() method creates the Date object:
Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");
At this point we can check the date object:
System.out.println(wayBack);
System.out.println(wayBack.getTime());
This prints the following:
Wed Jan 31 00:00:00 ICT 1900
-2206420924000 // epoch milliseconds
This matches what we saw earlier, in the background section.
When you use an online tool such as the one mentioned in the question, you will see the above milliseconds value reported as the following GMT (UTC) datetime:
GMT: Tuesday, January 30, 1900 5:17:56 PM
For the above output I used this tool.
Again, this is what we expect - when it's midnight in Bangkok, it's still the afternoon of the day before in Greenwich, UK.
The remainder of the code (including the Jackson object mapper transformations) are all subject to this initial set-up of your Date object.
For the question: "How does com.fasterxml.jackson.databind.ObjectMapper works? I thought it used Unix timestamp." It shows the same behavior as the core Java date object. I believe your assumption is correct.
Regarding the Unusual Offset
Regarding the ICT offset of +06:42:04 shown above:
On April 1st 1920, an adjustment was made to the local ICT (Indochina Time), to align it with UTC time (with an offset of +7 hours, as you note). The local clocks were set forward by 17 minutes and 56 seconds, to round up the UTC (GMT) offset to 7 hours.
See this link for a specific reference to the 17 minutes & 56 seconds change.
This is why you will not see that unusual offset from April 1920 onwards.
Further Links
See this answer regarding the newer java.time classes which should be used instead of java.util.Date.
See this question and its answers for a related deep-dive into the topic of historic time zone adjustments.
For some date in the past, GregorianCalendar.toZonedDateTime() returns a date that is 1 day off.
For 2nd April 1893, toZonedDateTime() returns the same date, for 1st April 1893, ZonedDateTime shows me the 31st March 1893 and there is also a difference in the "day of the year" values. There is always an offset for dates before this "magic" date.
Here is some sample code:
final GregorianCalendar gc = new GregorianCalendar(1893, 0, 1); // Set to 1st January 1893
for(int i = 1; i < 365; i++) {
gc.set(Calendar.DAY_OF_YEAR, i); // Update day of year
final ZonedDateTime zdt = gc.toZonedDateTime();
System.out.println(String.format(
"GC: %02d.%02d.%d (%d) -> ZDT: %02d.%02d.%d (%d)",
gc.get(Calendar.DAY_OF_MONTH),
gc.get(Calendar.MONTH) + 1, // "+1" is needed, because GregorianCalendar encodes January as 0.
gc.get(Calendar.YEAR),
gc.get(Calendar.DAY_OF_YEAR),
zdt.getDayOfMonth(),
zdt.getMonthValue(),
zdt.getYear(),
zdt.getDayOfYear()
));
}
When running the code, you will get the output
[...]
GC: 31.03.1893 (90) -> ZDT: 30.03.1893 (89)
GC: 01.04.1893 (91) -> ZDT: 31.03.1893 (90)
GC: 02.04.1893 (92) -> ZDT: 02.04.1893 (92)
GC: 03.04.1893 (93) -> ZDT: 03.04.1893 (93)
[...]
What am I doing wrong here?
Thanks in advance for your answers!
Best regards,
Markus
What is your system timezone?
I suspect you may be in a locale which observes, or observed, a DST type change on the 1st April 1893. Try printing out the offset value of the ZonedDateTime at each iteration of your loop.
Or a little more info can be gleaned by removing the timezone factor LocalDateTime.ofInstant(zdt.toInstant(), ZoneOffset.UTC)
Seems this is related to Berlin choosing to adopt CET on that date
The tzdata file europe contains only one zone Europe/Berlin for all of Germany.
It is not the best possible choice for several reasons:
- Berlin started CET only in 1893, later than several southern states.
https://mm.icann.org/pipermail/tz/2011-August/008736.html
From the JavaDocs of GregorianCalendar:
public ZonedDateTime toZonedDateTime()
Converts this object to a ZonedDateTime that represents the same point on the time-line as this GregorianCalendar.
Since this object supports a Julian-Gregorian cutover date and ZonedDateTime does not, it is possible that the resulting year, month and day will have different values. The result will represent the correct date in the ISO calendar system, which will also be the same value for Modified Julian Days.
I think it is some kind of non-desired but expected behaviour.
I suspect it’s a bug in GregorianCalendar. I have before seen bugs in Date for dates before 1900 in certain time zones. I consider ZonedDateTime and the other classes from java.time solider and more trustworthy.
Berlin was at offset +0:53:28 until April 1, 1893 00:00. At day of year 89 (for the sake of an example) your code gives a ZonedDateTime of 1893-03-29T23:53:28+00:53:28[Europe/Berlin] giving the correct offset. But both your GregorianCalendar and your ZonedDateTime represent a moment of 1893-03-29T23:00:00Z (UTC), so the GregorianCalendar seems to have assumed an offset of +01:00 instead, which is wrong. The conversion converts the moment correctly (and also the time zone correctly) and therefore the individual date and time fields incorrectly.
Source: For the offset for Berlin up until 1893 go to Time Zone in Berlin, Germany on timeanddate.com. Under “Time Changes in Berlin Over the Years”, in the dropdown on the top right select “1850 — 1899”. For the years 1850–92 you will see “No changes, UTC +0:53:28 hours all of the period”, and a change on April 1, 1893 at 00:00 to CET/UTC +1.
Here’s what I tried within your loop:
System.out.println(gc.getTimeZone().getID());
System.out.println(gc.getTime());
System.out.println(Instant.ofEpochMilli(gc.getTimeInMillis()));
System.out.println();
final ZonedDateTime zdt = gc.toZonedDateTime();
System.out.println(zdt);
System.out.println(zdt.toInstant());
Output for day of year (i) 89 was:
Europe/Berlin
Thu Mar 30 00:00:00 CET 1893
1893-03-29T23:00:00Z
1893-03-29T23:53:28+00:53:28[Europe/Berlin]
1893-03-29T23:00:00Z
I have the following problem using Joda-Time for parsing and producing date and time around Daylight Saving Time (DST) hours. Here is an example (please, note that March 30th 2008 is Daylight Saving change in Italy):
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
DateTime x = dtf.parseDateTime("30/03/2008 03:00:00");
int h = x.getHourOfDay();
System.out.println(h);
System.out.println(x.toString("dd/MM/yyyy HH:mm:ss"));
DateTime y = x.toDateMidnight().toDateTime().plusHours(h);
System.out.println(y.getHourOfDay());
System.out.println(y.toString("dd/MM/yyyy HH:mm:ss"));
I get the following output:
3
30/03/2008 03:00:00
4
30/03/2008 04:00:00
When i parse hour I get hour is 3. In my data structure I save the day storing midnight time, and then I have some value for each hour of the day (0-23). Then, when I write out the date, I re-compute the full date time making midnight plus hour. When I sum 3 hours to my midnight I get 04:00:00! And if I parse it again, I get hour 4!
Where is my mistake? Is there some way to get hour 2 when I parse or get hour three when I print out?
I have also tried to build output by hand:
String.format("%s %02d:00:00", date.toString("dd/MM/yyyy"), h);
but in this case for hour 2, I produce 30/03/2008 02:00:00 which is not a valid date (since hour 2 does not exist) and cannot be parsed any more.
Thank you in advance for your help.
Filippo
When I sum 3 hours to my midnight I get 04:00:00! And if I parse it again, I get hour 4! Where is my mistake?
You mentioned already that this date is exactly when the time changes. So there is no mistake. March 30, 2010 00:00 CEST (the timezone in Italy) is precisely speaking March 29, 2010 23:00 UTC. When you add 3 hours, you will get March 30, 2010 02:00 UTC. But this is post the moment, that we switch times (which happens on 01:00 UTC), so when you convert time to local timezone you get March 30, 04:00. That's correct behavior.
Is there some way to get hour 2 when I parse or get hour three when I print out?
No, because March 30, 2010 02:00 CEST does not exist. Precisely at March 30, 2010 01:00 UTC we switch time from +1 hour to +2 hours versus UTC, so March 30, 2010 00:59 UTC is March 30, 2010: 01:59 CEST, but March 30, 2010 01:00 UTC become March 30, 2010 03:00 CEST. No 02:xx hour exist on that particular date.
BTW. In a week you can expect another "fun". Can you tell what date in UTC this refers to:
October 31, 2010 02:15 CEST ?
Well, the funny part is, we do not know. It could be either 0ctober 31, 2010 00:15 UTC (before actual time switch) or October 31, 2010 01:15 UTC (after the switch).
That's exactly why you should always store date and times in relation to UTC and convert them to local time zone before displaying, otherwise you risk an ambiguity.
HTH.
The data structure you are saving your data is not very optimal for the days with daylight saving time. Your day in this particular day should only have 23 hours.
If you do:
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss").withLocale(Locale.US);
DateTime x = dtf.parseDateTime("30/03/2008 00:00:00");
DateTimeFormatter parser = DateTimeFormat.fullDateTime();
System.out.println("Start:"+parser.print(x));
DateTime y = x.plusHours(4);
System.out.println("After add of 4:"+parser.print(y));
You get the expected result, that the time is 05:00.
I recommend that you change the way you store your day and use a date. If not, you must handle daylight saving time when storing the hour of day.
You might do something like this:
In the case where we move the time forward one hour, as this case, you must store 4 and not 5 as the time for 5. And when you calculate the time, you should use the plusHours() method to get the actual time. I think you might get away with something like:
public class DateTest {
private static final int HOUR_TO_TEST = 2;
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
DateTime startOfDay = dtf.parseDateTime("30/03/2008 00:00:00");
/* Obtained from new DateTime() in code in practice */
DateTime actualTimeWhenStoring = startOfDay.plusHours(HOUR_TO_TEST);
int hourOfDay = actualTimeWhenStoring.getHourOfDay();
int hourOffset = startOfDay.plusHours(hourOfDay).getHourOfDay();
System.out.println("Hour of day:" + hourOfDay);
System.out.println("Offset hour:" + hourOffset);
int timeToSave = hourOfDay;
if (hourOffset != hourOfDay) {
timeToSave = (hourOfDay + (hourOfDay - hourOffset));
}
System.out.println("Time to save:" + timeToSave);
/* When obtaining from db: */
DateTime recalculatedTime = startOfDay.plusHours(timeToSave);
System.out.println("Hour of time 'read' from db:" + recalculatedTime.getHourOfDay());
}
}
...or basicly something like that. I'd write a test for it if you choose for going down this route. You can change the HOUR_TO_TEST to see that it moves passed the daylight saving time.
Building on the correct answers by Paweł Dyda & Knubo…
ISO 8601 For String Format
You should never store (serialize) a date-time as a string in the format you mentioned: "30/03/2008 03:00:00". Problems:
Omitted time zone.
Day, Month, Year order is ambiguous.
Should have been translated to UTC time.
If you must serialize a date-time value to text, use a reliable format. The obvious choice is the ISO 8601 standard format. Even better is converting the local time to UTC (Zulu) time zone and then out to ISO 8601 format. Like this: 2013-11-01T04:48:53.044Z
No Midnight
The midnight methods in Joda-Time are deprecated in favor of the Joda-Time method withTimeAtStartOfDay() (see doc). Some days do not have a midnight.
Example Code in Joda-Time 2.3
Some comments about this source code:
// © 2013 Basil Bourque. This source code may be used freely forevery by anyone taking full responsibility for doing so.
// Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 7 and earlier.
// http://www.joda.org/joda-time/
// Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
// JSR 310 was inspired by Joda-Time but is not directly based on it.
// http://jcp.org/en/jsr/detail?id=310
// By default, Joda-Time produces strings in the standard ISO 8601 format.
// https://en.wikipedia.org/wiki/ISO_8601
Example showing 23 hours in the day of DST (Daylight Saving Time) in Rome Italy, while the day after has 24 hours. Note that the time zone (for Rome) is specified.
// Time Zone list: http://joda-time.sourceforge.net/timezones.html
org.joda.time.DateTimeZone romeTimeZone = org.joda.time.DateTimeZone.forID("Europe/Rome");
org.joda.time.DateTime dayOfDstChange = new org.joda.time.DateTime( 2008, 3, 30, 0, 0, romeTimeZone ) ; // Day when DST
org.joda.time.DateTime dayAfter = dayOfDstChange.plusDays(1);
// How many hours in this day? Should be 23 rather than 24 on day of Daylight Saving Time "springing ahead" to lose one hour.
org.joda.time.Hours hoursObjectForDay = org.joda.time.Hours.hoursBetween(dayOfDstChange.withTimeAtStartOfDay(), dayAfter.withTimeAtStartOfDay());
System.out.println( "Expect 23 hours, got: " + hoursObjectForDay.getHours() ); // Extract an int from object.
// What time is 3 hours after midnight on day of DST change?
org.joda.time.DateTime threeHoursAfterMidnightOnDayOfDst = dayOfDstChange.withTimeAtStartOfDay().plusHours(3);
System.out.println( "Expect 4 AM (04:00) for threeHoursAfterMidnightOnDayOfDst: " + threeHoursAfterMidnightOnDayOfDst );
// What time is 3 hours after midnight on day _after_ DST change?
org.joda.time.DateTime threeHoursAfterMidnightOnDayAfterDst = dayAfter.withTimeAtStartOfDay().plusHours(3);
System.out.println( "Expect 3 AM (03:00) for threeHoursAfterMidnightOnDayAfterDst: " + threeHoursAfterMidnightOnDayAfterDst );
Example of storing a date-time by first translating to UTC. Then upon restoring the date-time object, adjust to the desired time zone.
// Serialize DateTime object to text.
org.joda.time.DateTimeZone romeTimeZone = org.joda.time.DateTimeZone.forID("Europe/Rome");
org.joda.time.DateTime dayOfDstChangeAtThreeHoursAfterMidnight = new org.joda.time.DateTime( 2008, 3, 30, 0, 0, romeTimeZone ).withTimeAtStartOfDay().plusHours(3);
System.out.println("dayOfDstChangeAtThreeHoursAfterMidnight: " + dayOfDstChangeAtThreeHoursAfterMidnight);
// Usually best to first change to UTC (Zulu) time when serializing.
String dateTimeSerialized = dayOfDstChangeAtThreeHoursAfterMidnight.toDateTime( org.joda.time.DateTimeZone.UTC ).toString();
System.out.println( "dateTimeBeingSerialized: " + dateTimeSerialized );
// Restore
org.joda.time.DateTime restoredDateTime = org.joda.time.DateTime.parse( dateTimeSerialized );
System.out.println( "restoredDateTime: " + restoredDateTime );
// Adjust to Rome Italy time zone.
org.joda.time.DateTime restoredDateTimeAdjustedToRomeItaly = restoredDateTime.toDateTime(romeTimeZone);
System.out.println( "restoredDateTimeAdjustedToRomeItaly: " + restoredDateTimeAdjustedToRomeItaly );
When run:
dayOfDstChangeAtThreeHoursAfterMidnight: 2008-03-30T04:00:00.000+02:00
dateTimeBeingSerialized: 2008-03-30T02:00:00.000Z
restoredDateTime: 2008-03-30T02:00:00.000Z
restoredDateTimeAdjustedToRomeItaly: 2008-03-30T04:00:00.000+02:00