ZonedDateTime to java.util.Date -> EST to UTC - Off by 1 hour - java

I'm trying to convert ZonedDateTime (EST) to Date (UTC) i see 1 hour off for the month of march for 13 and 14th calendar date
SystemDefault - UTC
ZonedDateTime zonedDateTime1 = ZonedDateTime.of(2021, 3, 13, 19, 0, 0, 0, ZoneId.of("America/New_York"));
Date date1 = Date.from(zonedDateTime1.withZoneSameInstant(ZoneId.systemDefault()).toInstant();
System.out.println("EST -> ", zonedDateTime1);
System.out.println("UTC -> ", date1);
ZonedDateTime zonedDateTime2 = ZonedDateTime.of(2021, 3, 14, 19, 0, 0, 0, ZoneId.of("America/New_York"));
Date date2 = Date.from(zonedDateTime2.withZoneSameInstant(ZoneId.systemDefault()).toInstant();
System.out.println("EST -> ", zonedDateTime2);
System.out.println("UTC -> ", date2);
Actual Result:
EST -> 2021-03-13T19:00-05:00[America/New_York]
UTC -> Sun Mar 14 00:00:00 UTC 2021
EST -> 2021-03-14T19:00-04:00[America/New_York]
UTC -> Sun Mar 14 23:00:00 UTC 2021
Expected Result:
EST -> 2021-03-13T19:00-05:00[America/New_York]
UTC -> Sun Mar 14 00:00:00 UTC 2021
EST -> 2021-03-14T19:00-04:00[America/New_York]
UTC -> Mon Mar 15 00:00:00 UTC 2021
Here is business use case
-> Client specific holidays 2021/1/14, 2021/2/14, 2021/3/14 (These are in UTC)
-> user selects a specific time eg: 2021/2/14 19:00, 2021/3/14 19:00 EST (These two days are actual working days)
Now system has say user selected date is a holiday or working day for
client
For this I converted user selected date (EST) to UTC and checking against client specific calendar (it works for feb but fails for march)

On Sun March 14th daylight saving time started in America/New_York. I suppose this is the reason for the observed difference.
Daylight saving time for America/New_York started on 2021-03-14T02:00:00 EST. That is any date time instance after this (until winter time is effective again) for time zone America/New_York will be EDT and not EST. And EDT has a UTC offset of -4 hours.

Using EST all year
It seems that your client insists on reporting times in EST, Eastern Standard Time, all year, not EDT, Eastern Daylight Time, in summer. There are time zones that do that, so a solution is: instead of using America/New_York time zone, use a time zone that uses Eastern Time and doesn’t use summer time/DST. For example:
ZoneId clientTimeZone = ZoneId.of("America/Atikokan");
ZonedDateTime zonedDateTime1 = ZonedDateTime.of(2021, 3, 13, 19, 0, 0, 0, clientTimeZone);
Date date1 = Date.from(zonedDateTime1.toInstant());
System.out.println("EST -> " + zonedDateTime1);
System.out.println("UTC -> " + date1);
ZonedDateTime zonedDateTime2 = ZonedDateTime.of(2021, 3, 14, 19, 0, 0, 0, clientTimeZone);
Date date2 = Date.from(zonedDateTime2.toInstant());
System.out.println("EST -> " + zonedDateTime2);
System.out.println("UTC -> " + date2);
Output is (when running in UTC time zone):
EST -> 2021-03-13T19:00-05:00[America/Atikokan]
UTC -> Sun Mar 14 00:00:00 UTC 2021
EST -> 2021-03-14T19:00-05:00[America/Atikokan]
UTC -> Mon Mar 15 00:00:00 UTC 2021
Compared to what you expected, the differences are:
The time zone ID is now America/Atikokan.
The UTC offset for 2021-03-14T19:00 is now not -04:00 but -05:00. Which is what causes the time to be converted to the expected UTC time of 00:00.
Compared to your code I have made one other change: I have left the calls to withZoneSameInstant() out. They make no difference here. Since you are doing toInstant() afterward, as the method names say, you get the same instant in both cases.
Edit: North and Central American time zones using Eastern Standard Time all year include:
In Canada: America/Atikokan
In the US: None that I have found
In Central America and the Caribbeans: America/Cancun, America/Jamaica, America/Panama

Related

Java: ZonedDateTime - get the offset of a reference timestamp

I have a timestamp in UTC. I convert it into localtime. My timezone is CET/CEST.
2018-10-03 12:00 UTC => 14:00 CEST
2018-10-30 12:00 UTC => 13:00 CET
Because of my timezone the system automatically applies the correct offset: If I convert timestamp in summer it automatically adds 2hrs (no matter what time it is when I convert), if it is in winter it adds 1hr.
So far - so good.
Now I would like to convert a UTC timestamp based on another referenced timestamp.
If the reference is in summer it should always add 2hrs - no matter if the timestamp to convert is summer or winter - and if reference is in winter it should always add 1hr.
Ref = 01.01.2018 = CET
2018-10-03 12:00 UTC => 13:00 CET
2018-10-30 12:00 UTC => 13:00 CET
Ref = 01.10.2018 = CEST
2018-10-03 12:00 UTC => 14:00 CEST
2018-10-30 12:00 UTC => 14:00 CEST
So how can I find out what timezone (or what offset to UTC) a reference timestamp (in UTC) has if my systems runs normal CEST/CET??
I use ZonedDateTime normally.
You can get the ZoneId from the reference ZonedDateTime that you have and use it to adjust the timestamp that you have in UTC to that zone:
Set up
ZoneId cet = ZoneId.of("CET");
// The reference timestamps, these could be any ZonedDateTime
// but I specifically make one for winter and one for summer
ZonedDateTime refWinter = ZonedDateTime.of(LocalDateTime.parse("2018-01-01T12:00"), cet);
ZonedDateTime refSummer = ZonedDateTime.of(LocalDateTime.parse("2018-10-01T12:00"), cet);
// The UTC timestamp that you want to convert to the reference zone
ZonedDateTime utc = ZonedDateTime.of(LocalDateTime.parse("2018-10-03T12:00"), ZoneOffset.UTC);
Conversion
// The converted timestamps
ZonedDateTime convertedWinter = utc.withZoneSameInstant(refWinter.getOffset());
ZonedDateTime convertedSummer = utc.withZoneSameInstant(refSummer.getOffset());
System.out.println(convertedWinter); // 2018-01-03T13:00+01:00
System.out.println(convertedSummer); // 2018-10-03T14:00+02:00
You can parse your date from string to Date object if they are in UTC and then you can convert them back to the string in your local timezone. Please check below code example:
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2018-10-03 12:00");
isoFormat.setTimeZone(TimeZone.getDefault());
System.out.println("Date in my local timezone is "+isoFormat.format(date));

java.sql.Timestamp wrong time parsing

Can someone explain why is it so? Why there is a 24 minutes offset for that time and how to deal with it?
Scala 2.12 and Java 8.
scala> java.sql.Timestamp.valueOf("1900-01-01 00:59:00")
res22: java.sql.Timestamp = 1900-01-01 00:59:00.0
scala> java.sql.Timestamp.valueOf("1900-01-01 01:00:00")
res23: java.sql.Timestamp = 1900-01-01 01:24:00.0
scala> java.sql.Timestamp.valueOf("1900-01-01 01:14:00")
res24: java.sql.Timestamp = 1900-01-01 01:38:00.0
scala> java.sql.Timestamp.valueOf("1900-01-01 01:20:00")
res25: java.sql.Timestamp = 1900-01-01 01:44:00.0
scala> java.sql.Timestamp.valueOf("1900-01-01 01:23:00")
res26: java.sql.Timestamp = 1900-01-01 01:47:00.0
scala> java.sql.Timestamp.valueOf("1900-01-01 01:24:00")
res27: java.sql.Timestamp = 1900-01-01 01:24:00.0
scala> java.sql.Timestamp.valueOf("1900-01-01 01:30:00")
res28: java.sql.Timestamp = 1900-01-01 01:30:00.0
Look at the time zone definition in the IANA time zone database:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Europe/Warsaw 1:24:00 - LMT 1880
1:24:00 - WMT 1915 Aug 5 # Warsaw Mean Time
1:00 C-Eur CE%sT 1918 Sep 16 3:00
2:00 Poland EE%sT 1922 Jun
1:00 Poland CE%sT 1940 Jun 23 2:00
1:00 C-Eur CE%sT 1944 Oct
1:00 Poland CE%sT 1977
1:00 W-Eur CE%sT 1988
1:00 EU CE%sT
In 1900, Poland had a time zone offset of one hour and 24 minutes from UTC, i.e., they were using local mean solar time. That was before standard time zones were introduced on August 5, 1915.
It must be that you feed PostgreSQL a timestamp without time zone, which is interpreted at your local time zone (with an offset of 1:24).
Somebody (scala?) then converts this timestamp back to a timestamp in your local time zone, but erroneously uses an offset of one hour.
I don't know how exactly to fix that, but either use timestamp without time zone throughout or fix the component that thinks the Polish time was offset 1 hour from UTC in 1900.
As far as I can tell there are two bugs involved here. Both are (if I am correct) in the java.util.Date class, the superclass of java.sql.Timestamp.
First, there is no time offset transition in Warsaw in year 1900. The earliest transition that my Java 8 knows of is in 1915. So Warsaw was at offset 1:24 from GMT during all of the time we’re concerned with.
I tried:
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Warsaw"));
ZoneOffset offset0124 = ZoneOffset.ofHoursMinutes(1, 24);
System.out.println("" + new Date(0, 0, 1, 0, 59)
+ " -> " + new Date(0, 0, 1, 0, 59).toInstant().atOffset(offset0124));
System.out.println("" + new Date(0, 0, 1, 1, 14)
+ " -> " + new Date(0, 0, 1, 1, 14).toInstant().atOffset(offset0124));
System.out.println("" + new Date(0, 0, 1, 1, 24)
+ " -> " + new Date(0, 0, 1, 1, 24).toInstant().atOffset(offset0124));
This prints:
Mon Jan 01 00:59:00 CET 1900 -> 1900-01-01T01:23+01:24
Mon Jan 01 01:38:00 CET 1900 -> 1900-01-01T01:38+01:24
Mon Jan 01 01:24:00 CET 1900 -> 1900-01-01T01:24+01:24
The method Timestamp.valueOf method that you use indirectly uses a deprecated Date constructor, so so am I (not the exact same constructor, I am using the one without seconds, trusting it makes no difference). I will comment on the above three cases backward:
1:24 is handled correctly, we get the expected time both from Date.toString() and from the OffsetDateTime.
1:14 is perceived as 1:38, 24 minutes later. This looks like a bug to me.
0:59 is perceived as 1:23, also 24 minutes later. We can see this from the OffsetDateTime. The same bug. However, Date.toString() produces 00:59 as expected. This seems to me to be a second bug that somehow compensates for the first one. I haven’t checked, but I suspect that the source of this bug also causes Timestamp.toString() to behave incorrectly.
As a check I calculated the difference between your Timestamp objects of 0:59 and 1:24. The desired result is 25 minutes or 1 500 000 milliseconds. The code is:
System.out.println(java.sql.Timestamp.valueOf("1900-01-01 01:24:00").getTime()
- java.sql.Timestamp.valueOf("1900-01-01 00:59:00").getTime());
This prints
60000
60 seconds, the same as 1 minute. So even though both of those timestamps printed the way we had expected, there still is a bug involved.

Inconsistency in handling date and time in Java

I have noticed strange behavior of date and time in java. I have the following code:
public class TestDateTime {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Helsinki"));
Calendar calendar = GregorianCalendar.getInstance();
assert(calendar.getTimeZone().equals(TimeZone.getDefault()));
//Set 1899-12-30T23:00:00
calendar.set(1899,11,30,23,0,0);
calendar.set(Calendar.MILLISECOND,0);
long timeInMillis = calendar.getTimeInMillis();
java.util.Date calendarDateTime = new java.util.Date(timeInMillis);
LocalDateTime localDateTime = LocalDateTime.ofInstant(ofEpochMilli(timeInMillis), ZoneId.systemDefault());
System.out.println("Time in millis: " + timeInMillis);
System.out.println("Date: " + calendarDateTime.toString());
System.out.println("Local DateTime: " + localDateTime.toString());
}
}
The output is:
Time in millis: -2209086000000
Date: Sat Dec 30 23:00:00 EET 1899
Local DateTime: 1899-12-30T22:39:49
timeInMillis must contain the number of milliseconds passed from 1970-01-01T00:00:00Z.
The instance of Date class stores number of milliseconds passed from 1970-01-01T00:00:00Z.
Date.toString() method returns local date and time for the default timezone.
So the Date.toString() and LocalDateTime.toString() must return the same date and time, but we see the difference (more than 20 minutes).
Is this a bug of java, or I use date and time incorrectly in Java?
This is a weirdness caused by Finland time change, see Clock Changes in Helsinki, Finland (Helsingfors) in 1921:
May 1, 1921 - Time Zone Change (HMT → EET)
When local standard time was about to reach
Sunday, May 1, 1921, 12:00:00 midnight clocks were turned forward 0:20:11 hours to
Sunday, May 1, 1921, 12:20:11 am local standard time instead
Those 20 minutes 11 seconds seem to be what you're observing.
As Jim Garrison said in his answer, LocalDateTime is correctly handling that, while Calendar is not.
In reality, it seems that the old TimeZone is getting the offset wrong, while the new ZoneId is getting it right, as can be seen in the following test code:
public static void main(String[] args) {
compare(1800, 1, 1, 0, 0, 0);
compare(1899,12,31, 23,59,59);
compare(1900, 1, 1, 0, 0, 0);
compare(1900,12,30, 23, 0, 0);
compare(1921, 4,30, 0, 0, 0);
compare(1921, 5, 1, 0, 0, 0);
compare(1921, 5, 2, 0, 0, 0);
}
private static void compare(int year, int month, int day, int hour, int minute, int second) {
Calendar calendar = new GregorianCalendar();
calendar.clear();
calendar.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
calendar.set(year, month-1, day, hour, minute, second);
Date date = calendar.getTime();
ZonedDateTime zdt = ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.of("Europe/Helsinki"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z XXX");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss z XXX");
System.out.printf("%04d-%02d-%02d %02d:%02d:%02d %s = %d %s = %d %d%n",
year, month, day, hour, minute, second,
sdf.format(date), date.getTime(),
dtf.format(zdt), zdt.toInstant().toEpochMilli(),
date.getTime() - zdt.toInstant().toEpochMilli());
}
Output
1800-01-01 00:00:00 1800-01-01 00:00:00 EET +02:00 = -5364669600000 1800-01-01 00:00:00 EET +01:39 = -5364668389000 -1211000
1899-12-31 23:59:59 1899-12-31 23:59:59 EET +02:00 = -2208996001000 1899-12-31 23:59:59 EET +01:39 = -2208994790000 -1211000
1900-01-01 00:00:00 1900-01-01 00:00:00 EET +02:00 = -2208996000000 1900-01-01 00:00:00 EET +01:39 = -2208994789000 -1211000
1900-12-30 23:00:00 1900-12-30 23:00:00 EET +01:39 = -2177548789000 1900-12-30 23:00:00 EET +01:39 = -2177548789000 0
1921-04-30 00:00:00 1921-04-30 00:00:00 EET +01:39 = -1536025189000 1921-04-30 00:00:00 EET +01:39 = -1536025189000 0
1921-05-01 00:00:00 1921-05-01 00:20:11 EET +02:00 = -1535938789000 1921-05-01 00:20:11 EET +02:00 = -1535938789000 0
1921-05-02 00:00:00 1921-05-02 00:00:00 EET +02:00 = -1535853600000 1921-05-02 00:00:00 EET +02:00 = -1535853600000 0
LocalDateTime is CORRECT. According to the TZ database, the GMT offset at that date was 1:39:49:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Europe/Helsinki 1:39:49 - LMT 1878 May 31
1:39:49 - HMT 1921 May # Helsinki Mean Time
2:00 Finland EE%sT 1983
2:00 EU EE%sT
Historical timezones are incredibly complex, and prior to standardization offsets were inherited from settings based on things like mean solar noon. When going back that far just about any offset is possible, and the IANA TZ database is the master reference for historical data.
From what I can see in the database, the weird offset did not get standardized to 2:00:00 until 1921 when HMT was replaced with EE(S)T.
As others pointed out, the difference is because the LMT (local mean time) value is not being taken into account by the Date object. This has been discussed before here, with regard to Joda-Time - the precursor to Java 8's time package.
Additionally, the Joda-Time FAQ says the following:
Why is the offset for a time-zone different to the JDK?
There are two main reasons for this.
The first reason is that both the JDK and Joda-Time have time-zone
data files. It is important to keep both up to date and in sync if you
want to compare the offset between the two.
The second reason affects date-times before the modern time-zone
system was introduced. The time-zone data is obtained from the
time-zone database. The database contains information on "Local Mean
Time" (LMT) which is the local time that would have been observed at
the location following the Sun's movements.
Joda-Time uses the LMT information for all times prior to the first
time-zone offset being chosen in a location. By contrast, the JDK
ignores the LMT information. As such, the time-zone offset returned by
the JDK and Joda-Time are different for date-times before the modern
time-zone system.
The last part (which I bolded) is relavent to both Joda-Time and Java 8, even though Java 8 has one set of time zone data files (unlike Joda-Time).
To be more precise about API-inconsistency:
While the new java.time-API always uses the LMT-informations of TZDB, we have also to state that the old JDK-class java.util.TimeZone makes a cut in year 1900 with the consequence that LMT-informations are not taken into account before the year 1900, but after 1900, yes, it is still taken into account! Just make your experiments with an appropriate zone... (Asia/Kamchatka for example)
We cannot really say that either the LMT-strategy of java.time-API is correct or the traditional 1900-strategy. Keep also in mind that there is an open JDK-issue to abolish the LMT-strategy. Citation:
The current TimeZone code does not use LMT. Joda-Time does, as does
JSR-310. This is wrong.
Recent discussion on the tzdb mailing list has indicated that the data
is not properly maintained or reliably linked to the city of the zone
ID. It is also relatively meaningless, being a notional value for a
single city within a large region.
Removing LMT is a good thing.
And Xueming Shen from Oracle says as comment in this issue:
The current j.u.TimeZone implementation DOES use LMT. If the LMT is
defined/used cross the 1900.1.1 j.u.TimeZone cutoff date (by the tzdb
data). For example the offset for Asia/Kamchatka from 1900.1.1 to the
1922.11.10 will be the LMT 10.34.36. Yes, if the LMT end date is before 1900.1.1, the LMT will not be used by the j.u.TZ.
As additional historical note, the JDK-issue was originally suggested by the main author of java.time-API S. Colebourne, see also the ancestor on threeten-issue-tracker.

java 1.6 daylight saving

I'm working on a critical application which cares about daylight saving time change.
I'm trying to simulate manually what could happen at runtime by comparing thow date which cross the daylight saving change, so I made the below test.
My current location is Italy so the change from CEST (Central European Summer Time) to CET (Central European Time) this year happens on 25/10.
I used the full time zone names, my timezone is Europe/Rome.
And here it is the test I did:
Calendar before = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome DST")); //CEST
before.set(Calendar.DAY_OF_MONTH, 25);
before.set(Calendar.MONTH, Calendar.OCTOBER);
before.set(Calendar.HOUR_OF_DAY, 2);
before.set(Calendar.MINUTE, 30);
before.set(Calendar.SECOND, 0);
before.set(Calendar.MILLISECOND, 0);
System.out.println(before.getTime());
Calendar after = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome")); //CET
after.set(Calendar.DAY_OF_MONTH, 25);
after.set(Calendar.MONTH, Calendar.OCTOBER);
after.set(Calendar.HOUR_OF_DAY, 2);
after.set(Calendar.MINUTE, 30);
after.set(Calendar.SECOND, 0);
after.set(Calendar.MILLISECOND, 0);
System.out.println(after.getTime());
System.out.println(before.compareTo(after));
The output is:
BEFORE DST CHANGE: Sun Oct 25 03:30:00 CET 2015
AFTER DST CHANGE: Sun Oct 25 02:30:00 CET 2015
before.compareTo(after): 1
The comparison result is wrong, i.e. 2:30 CEST is after 2:30 CET, but its the opposite.
I don't know if it's a real test.
Is it Is there any way to fix this?
I tried also with joda time but the result is the same.
Thanks in advance.
Your problem is that "Europe/Rome DST" is not recognized by getTimeZone(timeZoneId).
When it doesn't understand your input, it returns the GMT timezone by default. You can see the list of available TimeZone ids with getAvailableIDs (the method below getTimeZone at the above link).
It should be noted that CEST is also not on the list. To simulate the CEST timezone you could choose one of the following solutions:
I would recommend using TimeZone.setRawOffset(int offsetInMs) to set the offests for CET and CEST yourself.
Use one of the timzones that is defined relative to GMT (ex, with id "Etc/GMT+1"). This will ensure that you are using valid timezone offsets the the TimeZone api will understand.
Set the DST offset on the calendar instance Calendar.DST_OFFSET.
By using the last solution the correct test code is:
Calendar before = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome"));
before.set(Calendar.DST_OFFSET, 3600000);
before.set(Calendar.DAY_OF_MONTH, 25);
before.set(Calendar.MONTH, Calendar.OCTOBER);
before.set(Calendar.HOUR_OF_DAY, 2);
before.set(Calendar.MINUTE, 30);
before.set(Calendar.SECOND, 0);
before.set(Calendar.MILLISECOND, 0);
System.out.println("BEFORE DST CHANGE: " + before.getTime());
Calendar after = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome"));
after.set(Calendar.DAY_OF_MONTH, 25);
after.set(Calendar.MONTH, Calendar.OCTOBER);
after.set(Calendar.HOUR_OF_DAY, 2);
after.set(Calendar.MINUTE, 30);
after.set(Calendar.SECOND, 0);
after.set(Calendar.MILLISECOND, 0);
System.out.println("AFTER DST CHANGE: " + after.getTime());
System.out.println("before.compareTo(after): " + before.compareTo(after));
And the output:
BEFORE DST CHANGE: Sun Oct 25 02:30:00 CEST 2015
AFTER DST CHANGE: Sun Oct 25 02:30:00 CET 2015
before.compareTo(after): -1
The answer by augray is correct and should be accepted (click that large empty check mark icon to make it turn green). I'll add some thoughts and code.
Use A Good Date-Time Library
Avoid the mess that is the java.util.Date/.Calendard classes. They are notoriously troublesome, flawed in both design and implementation.
These classes have been supplanted by the new java.time package in Java 8 and later (Tutorial). That package was inspired by the Joda-Time library. While similar, java.time and Joda-Time are not identical. Each has features the other lacks. You can use either or both.
Avoid 3-4 Letter Time Zone Codes
Codes such as CET & CEST are neither standardized nor unique. Avoid them.
Use full time zone names. Most of these are "continent/city" or "continent/region".
You seem to be using this codes in an effort to manage the problem of DST, Daylight Saving Time. Leave such heavy-lifting to the date-time library, such as java.time or Joda-Time. A time zone combines an offset from UTC with the set of past, present, and future rules for DST and other anomalies. So you specify the time zone name and let the library do the work of figuring out when DST is in effect.
DST
Daylight Saving Time (DST) for Rome ends this year on October 25, 2015 at 3 AM. Clocks are rolled back to repeat the 2 AM hour. So there are two 2:30 times that day. You can see both of those 2:30 times in example code below.
2015-10-25T02:30+02:00[Europe/Rome]
2015-10-25T02:30+01:00[Europe/Rome]
Example Code
Here is some example code in java.time of Java 8 to see how DST is handled. First we take some "local" date-time, where "local" means without any particular time zone attached. We then assign the Rome time zone. After that we take the zoned values (the Rome values) and either add or subtract hours.
Ambiguity
Note how the notion of "2:30 AM in Rome" is meaningless on the 25th of October. You must know the offset of 01:00 or 02:00 to correctly interpret.
ZoneId zone = ZoneId.of( "Europe/Rome" );
System.out.println( "-----| Local |-----------------------------------------\n" );
LocalDateTime local_0130 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 1 , 30 );
ZonedDateTime zoned_0130 = ZonedDateTime.of( local_0130 , zone );
LocalDateTime local_0230 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 2 , 30 );
ZonedDateTime zoned_0230 = ZonedDateTime.of( local_0230 , zone );
LocalDateTime local_0330 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 3 , 30 );
ZonedDateTime zoned_0330 = ZonedDateTime.of( local_0330 , zone );
System.out.println( "local_0130: " + local_0130 + " in zone: " + zone + " is " + zoned_0130 );
System.out.println( "local_0230: " + local_0230 + " in zone: " + zone + " is " + zoned_0230 );
System.out.println( "local_0330: " + local_0330 + " in zone: " + zone + " is " + zoned_0330 + "\n" );
System.out.println( "-----| Add Hours |-----------------------------------------\n" );
ZonedDateTime zoned_0130_plus_1H = zoned_0130.plusHours( 1 );
ZonedDateTime zoned_0130_plus_2H = zoned_0130.plusHours( 2 );
System.out.println( "zoned_0130_plus_1H: " + zoned_0130_plus_1H );
System.out.println( "zoned_0130_plus_2H: " + zoned_0130_plus_2H + "\n" );
System.out.println( "-----| Subtract Hours |-----------------------------------------\n" );
ZonedDateTime zoned_0330_minus_1H = zoned_0330.minusHours( 1 );
ZonedDateTime zoned_0330_minus_2H = zoned_0330.minusHours( 2 );
System.out.println( "zoned_0330_minus_1H: " + zoned_0330_minus_1H );
System.out.println( "zoned_0330_minus_2H: " + zoned_0330_minus_2H + "\n" );
When run.
-----| Local |-----------------------------------------
local_0130: 2015-10-25T01:30 in zone: Europe/Rome is 2015-10-25T01:30+02:00[Europe/Rome]
local_0230: 2015-10-25T02:30 in zone: Europe/Rome is 2015-10-25T02:30+02:00[Europe/Rome]
local_0330: 2015-10-25T03:30 in zone: Europe/Rome is 2015-10-25T03:30+01:00[Europe/Rome]
-----| Add Hours |-----------------------------------------
zoned_0130_plus_1H: 2015-10-25T02:30+02:00[Europe/Rome]
zoned_0130_plus_2H: 2015-10-25T02:30+01:00[Europe/Rome]
-----| Subtract Hours |-----------------------------------------
zoned_0330_minus_1H: 2015-10-25T02:30+01:00[Europe/Rome]
zoned_0330_minus_2H: 2015-10-25T02:30+02:00[Europe/Rome]
My advice is to use the full identifiers from the IANA time zone database. In your case you should be using "Europe/Rome":
before.setTimeZone(TimeZone.getTimeZone("Europe/Rome"));
This will make sure that proper time zone info is used for the region, including when daylight saving mode needs to be triggered, and possible regional exceptions.
Do not add DST.
"Europe/Rome" is not CET, and "Europe/Rome DST" is not CEST.
"Europe/Rome" is the timezone of Italy. It includes not only DST changes, but any other change to the offset that Rome/Italy has had. So "Europe/Rome" is CET when Rome is in CET and it's CEST when Rome is on CET. The whole point of timezones like "Europe/Rome" is that you don't have to care about daylight savings, the timezone will handle it for you.

Unparseable Date In America/Mazatlan timezone

I'm on America/Los_Angeles TZ, and when I try to render midnight in the America/Mazatlan TZ, I get the following exception:
Exception in thread "main" java.text.ParseException: Unparseable date: "12:00 AM"
Here is my code to reproduce this:
DateFormat dateFormat = new SimpleDateFormat("h:mm a");
TimeZone timeZone = TimeZone.getTimeZone("America/Mazatlan");
dateFormat.setTimeZone(timeZone);
dateFormat.setLenient(false);
Date parse = dateFormat.parse("12:00 AM");
I'm aware the commenting out the setLenient(false) will fix the issue, I'm just not sure why that is a fix as other timezones in the same offset, such as America/Inuvik do not cause issues like this.
Any assistance would be great.
When you don't specify a date, 1970-01-01 is used.
The time zone definition for Mazatlan shows that the base offset switched from -08:00 to -07:00 in 1970. This creates a discontinuity in local time, similar to the kind usually found during a spring-forward daylight saving time transition.
There is an hour of missing local time, from midnight to just before 1:00. Times in this range are invalid. Assuming the zone definition is correct, that means the clocks ticked forward like this:
======== UTC ======= ==== America/Mazatlan ===
1970-01-01T07:59:57Z 1969-12-31T23:59:57-08:00
1970-01-01T07:59:58Z 1969-12-31T23:59:58-08:00
1970-01-01T07:59:59Z 1969-12-31T23:59:59-08:00
1970-01-01T08:00:00Z 1970-01-01T01:00:00-07:00 (transition!)
1970-01-01T08:00:01Z 1970-01-01T01:00:01-07:00
1970-01-01T08:00:02Z 1970-01-01T01:00:02-07:00
Therefore, if you are using SimpleDateFormat - you should include a date, not just a time.
If you remove the line,
dateFormat.setLenient(false);
Your parse object value is becoming
Thu Jan 01 10:00:00 EET 1970
I don't know why but for America/Mazatlan TZ this line is creating exception.
For America/Los_Angeles TZ and America/Inuvik TZ, usage of dateFormat.setLenient(false) line not giving any error and results are the same with America/Mazatlan TZ.
Thu Jan 01 10:00:00 EET 1970
That because you dateFormat.setLenient(false); and 12:00 should be 'PM' not 'AM'

Categories