I'm writing some integration of legacy code that expects date/time as a GregorianCalendar.
So, I'm creating the GregorianCalendar using from(ZonedDateTime) method.
However, the result is showing some inconsistent behaviour when the GregorianCalendar is created from the ZonedDateTime versus when the GregorianCalendar is created, let's say, from a string parsing or even from epoch millis. For example, when setting the Calendar day_of_week. To demonstrate this (https://onecompiler.com/java/3yssz4q86):
import java.util.*;
import java.time.*;
public class Main {
public static void main(String[] args) {
final GregorianCalendar calendarPure = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"));
printInfo(calendarPure);
ZoneId zone = ZoneId.of("America/Los_Angeles");
final ZonedDateTime zdtNow = ZonedDateTime.now(zone);
final GregorianCalendar calendarFromZdt = GregorianCalendar.from(zdtNow);
printInfo(calendarFromZdt);
final GregorianCalendar calendarFromZdtEpoch = new GregorianCalendar();
calendarFromZdtEpoch.setTimeInMillis(zdtNow.toInstant().toEpochMilli());
printInfo(calendarFromZdtEpoch);
}
private static void printInfo(GregorianCalendar cal) {
System.out.printf("Now: %tc%n", cal);
System.out.printf("Last Sunday: %tc%n", getLastSunday(cal));
System.out.printf("Week of Year: %d%n", cal.get(Calendar.WEEK_OF_YEAR));
System.out.printf("Time in Millis: %d%n", cal.getTimeInMillis());
System.out.printf("toString(): %s%n", cal);
System.out.println();
}
public static GregorianCalendar getLastSunday(GregorianCalendar referenceDate) {
final GregorianCalendar lastSundayMidNight = (GregorianCalendar)referenceDate.clone();
lastSundayMidNight.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
return lastSundayMidNight;
}
}
The output is:
Now: Wed Dec 28 16:45:36 PST 2022
Last Sunday: Sun Dec 25 16:45:36 PST 2022
Week of Year: 53
Time in Millis: 1672274736631
toString(): java.util.GregorianCalendar[time=1672274736631,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=11,WEEK_OF_YEAR=53,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=362,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=45,SECOND=36,MILLISECOND=631,ZONE_OFFSET=-28800000,DST_OFFSET=0]
Now: Wed Dec 28 16:45:36 PST 2022
Last Sunday: Sun Jan 01 16:45:36 PST 2023
Week of Year: 52
Time in Millis: 1672274736668
toString(): java.util.GregorianCalendar[time=1672274736668,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2022,MONTH=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=362,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=45,SECOND=36,MILLISECOND=668,ZONE_OFFSET=-28800000,DST_OFFSET=0]
Now: Thu Dec 29 00:45:36 UTC 2022
Last Sunday: Sun Dec 25 00:45:36 UTC 2022
Week of Year: 53
Time in Millis: 1672274736668
toString(): java.util.GregorianCalendar[time=1672274736668,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Etc/UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=11,WEEK_OF_YEAR=53,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=363,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=45,SECOND=36,MILLISECOND=668,ZONE_OFFSET=0,DST_OFFSET=0]
I noticed that, from GregorianCalendar.toString() that prints the internal fields a different in firstDayOfWeek and minimalDaysInFirstWeek. That could be the cause for the divergence in the after results. I wonder where the GregorianCalendar is getting these values from. Maybe there is a way to set something on ZonedDateTime object that will ensure the results will be the same? Couldn't find anything on javadocs.
Note: Please, don't tell me not to use GregorianCalendar. I know, I know. I'm just writing an integration for two pieces of code that I'm not allowed to modify, one uses GregorianCalendar (sigh), the other uses ZonedDateTime.
This question already has answers here:
How to handle dates when the input dates are in the transition period from PDT to PST?
(2 answers)
Closed 3 years ago.
In short:
I have a time that is in epoch and I want to make it time since January 1, 1970, 00:00:00 GMT as java.util.Date would expect to be given.
This code helps demonstrate my issue:
import java.util.Date;
import java.util.Locale;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class TimeHelp {
private String format = "EEE MMM dd HH:mm:ss yyyy";
public SimpleDateFormat asSimpleDateFormat() {
return new SimpleDateFormat(format, Locale.ENGLISH);
}
public DateTimeFormatter asDateTimeFormatter() {
return DateTimeFormatter.ofPattern(format, Locale.ENGLISH);
}
#Test
public void test() throws Exception {
System.out.println(ZoneId.systemDefault());
String s = "Sun Apr 04 02:00:01 2010";
long t1 = asSimpleDateFormat().parse(s).getTime();
ZonedDateTime zoned = LocalDateTime.parse(s, asDateTimeFormatter())
.atZone(ZoneId.systemDefault());
long t2 = zoned.toEpochSecond() * 1000;
long t3 = Date.from(zoned.toInstant()).getTime();
long t4 = zoned.toInstant().toEpochMilli();
System.out.println("t1 " + t1);
System.out.println("t2 " + t2);
System.out.println("t3 " + t3);
System.out.println("t4 " + t4);
System.out.println("Difference in minutes " + Math.abs(t1 - t2)/1000/60);
}
}
And that outputs:
Australia/Sydney
t1 1270310401000
t2 1270306801000
t3 1270306801000
t4 1270306801000
Difference in minutes 60
Note that t1 is different from all the others, I think because t1 is GMT while the others are all UTC.
If I use the SimpleDateFormat the value of the long is different to if I use the DateTimeFormatter to get a ZonedDateTime after which I call toEpochSecond().
For reasons I would like to be given a ZonedDateTime and I want to convert that to a Date but it looks like such a thing wont work because the Date works in GMT not UTC.
Quoting timeanddate.com:
When local daylight time was about to reach Sunday, April 4, 2010, 3:00:00 am clocks were turned backward 1 hour to Sunday, April 4, 2010, 2:00:00 am local standard time instead.
Which means that Sun Apr 04 02:00:01 2010 happened twice that day. So which of those 2 do you get?
With SimpleDateFormat you get the later one, although that is undocumented.
With ZonedDateTime you get the earlier one:
For Overlaps, the general strategy is that if the local date-time falls in the middle of an Overlap, then the previous offset will be retained. If there is no previous offset, or the previous offset is invalid, then the earlier offset is used, typically "summer" time.. Two additional methods, withEarlierOffsetAtOverlap() and withLaterOffsetAtOverlap(), help manage the case of an overlap.
I have the following code:
import java.util.Date;
import java.util.Calendar;
import java.text.SimpleDateFormat;
import org.junit.Test;
public class DateCalendarTest {
#Test
public void test1() {
private final DateFormat df1 = new SimpleDateFormat("MM/dd/yyyy");
String batchdt = "2015/09/23";
System.out.println("Date & Calendar Test: " + batchdt);
Calendar cal = Calendar.getInstance();
Date date1 = df1.parse(batchdt);
cal.setTime(date1);
System.out.println("Date & Calendar Test: " + cal.getTime());
}
}
Output:
Date Calendar Test: 2015/09/23
Date Calendar Test: Mon Nov 09 00:00:00 EST 190
Can someone please explain why this behaves in this manner?
Your intended data doesn't match the format specified in your SimpleDateFormat. However, by default, it is lenient in how it parses the data. For example, September 31st would be interpreted as October 1st.
Here, it's interpreted as day 9 of month 2,015 of year 23. 2,015 months is 167 years, 11 months, which when added to the year 23 yields the year 190. In this case, it is very lenient.
The output format is what is expected when printing out a Date directly.
Yes, I know this question has been posed many times, but I am still unable to get a consistent result between .NET and Java.
There are a number of posts and google search results that refer to the constants:
long UNIX_EPOCH_IN_100NS_INTERVALS = 621355968000000000l;
long DOTNET_EPOCH_IN_MSEC_INTERVALS = -62135596800000l;
The first purports to be the number of 100ns intervals or 'tics' between UTC 0001-01-01 00:00:00 (z yyyy-MM-dd HH:mm:ss) and UTC 1970-01-02 00:00:00, and the second is the difference between the .NET epoch and the Java epoch in milliseconds.
A post here reports a similar issue in the .NET world.
In the code below, I calculate the offset between the epochs and then compare to the published numbers:
SimpleDateFormat sdf = new SimpleDateFormat("z yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date;
try {
date = sdf.parse("UTC 1970-01-01 00:00:00");
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("Stupid java dates !");
}
System.out.println("Unix Epoch Date: " + sdf.format(date));
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(date);
System.out.println("Unix Epoch Milliseconds: " + cal.getTimeInMillis());
try {
date = sdf.parse("UTC 0001-01-01 00:00:00");
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("Stupid java dates !");
}
System.out.println("Calculated DotNet Epoch Date: " + sdf.format(date));
cal.setTime(date);
System.out.println("Calculated DotNet Epoch Milliseconds: " + cal.getTimeInMillis());
date = new Date(-62135596800000l);
System.out.println("Published DotNet Epoch Date: " + sdf.format(date));
cal.setTime(date);
System.out.println("Published DotNet Epoch Milliseconds: " + cal.getTimeInMillis());
which generates the output:
Unix Epoch Date: UTC 1970-01-01 00:00:00
Unix Epoch Milliseconds: 0
DotNet Epoch Date: UTC 0001-01-01 00:00:00
DotNet Epoch Milliseconds: -62135769600000
Published DotNet Epoch Date: UTC 0001-01-03 00:00:00
Published DotNet Epoch Milliseconds: -62135596800000
Any help appreciated.
I am looking at the SimpleDateFormat in Java.
I want to format a date string and turn it into a Date/Calendar, then format the resulting date/calendar back to the string. This is for a date time over the October clock change where you have two 03:00's in the day. The times over the October clock change period should be:
** UPDATE I have changed my times from 01:00 to 03:00 where there should be 2 times in the day **
2012-10-28 02:00:00 CEST
2012-10-28 02:30:00 CEST
2012-10-28 02:00:00 CET
2012-10-28 02:30:00 CET
2012-10-28 03:00:00 CET
Converting 2012-10-28 01:00:00 CET to date/calendar then back to string is causing the issue. The converted string is 2012-10-28 02:00:00 CEST - which does not exist.
The following code will replicate this problem:
public class DatesAndTimesStackOverflow {
final static SimpleDateFormat sdf;
final static TimeZone tz;
static {
tz = TimeZone.getTimeZone( "Europe/Paris" );
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz");
sdf.setTimeZone(tz);
}
public static void main(String[] args) {
// october clock change should be the following:
outputDateInfo("2012-10-28 02:00:00 CEST");
outputDateInfo("2012-10-28 02:30:00 CEST");
outputDateInfo("2012-10-28 02:00:00 CET");
outputDateInfo("2012-10-28 02:30:00 CET");
outputDateInfo("2012-10-28 03:00:00 CET");
outputDateInfo("2012-10-28 03:30:00 CET");
outputDateInfo("2012-10-28 04:00:00 CET");
}
private static void outputDateInfo(String theDate) {
try {
output("------------------------------------------------------------------------------");
Date d = sdf.parse(theDate);
Calendar c = GregorianCalendar.getInstance(tz);
c.setTimeInMillis(d.getTime());
TimeZone tzCal = c.getTimeZone();
output("String: " + theDate);
output("");
output("Date: " + d); // toString uses current system TimeZone
output("Date Millis: " + d.getTime());
output("Cal Millis: " + c.getTimeInMillis());
output("Cal To Date Millis: " + c.getTime().getTime());
output("Cal TimeZone Name: " + tzCal.getDisplayName());
output("Cal TimeZone ID: " + tzCal.getID());
output("Cal TimeZone DST Name: " + tzCal.getDisplayName(true, TimeZone.SHORT));
output("Cal TimeZone Standard Name: " + tzCal.getDisplayName(false, TimeZone.SHORT));
output("In DayLight: " + tzCal.inDaylightTime(d));
output("");
output("Day Of Month: " + c.get(Calendar.DAY_OF_MONTH));
output("Month Of Year: " + c.get(Calendar.MONTH));
output("Year: " + c.get(Calendar.YEAR));
output("Hour Of Day: " + c.get(Calendar.HOUR_OF_DAY));
output("Minute: " + c.get(Calendar.MINUTE));
output("Second: " + c.get(Calendar.SECOND));
// check to see if this converts back to correct string
String reformat = sdf.format(c.getTime());
if( reformat.equals(theDate) ) {
output("ReConvert: " + reformat + " OK");
} else {
output("ReConvert: " + reformat + " <-------- Error. The converted date is different");
}
} catch (ParseException ex) {
output("Cannot parse this date");
}
}
private static void output(String message) {
System.out.println(message);
}
}
I don't think it is a problem with my example. Hopefully it is and not a problem in Java. Is there a rational explanation if it is not the way I have coded this?
It is correct. There is such a time as 2:00 (or 3) CEST. Europe/Paris contains two timezones, CET and CEST. They are always an hour apart and are both contiguous (the timezone's UTC offset never changes, the calendar switches timezones).
Also note, fall backward happens at 3 in the morning, not at 1 in the morning! If you change your times to 3, you should see it flip the other way, where parsing 3:00CET input yields 3:00CET output, but parsing 3:00CEST input, yields 2:00CET output, as expected.
At 3am in Paris, the current time changes from 3:00 CEST to 2:00 CET. When you specify Oct 28, 2012 3:00 CEST as your input, it will end up outputting 2:00am CET, which is the same actual point in time, because that is what clocks on the wall in Paris say!
--
I think there is some confusion about the 'human' meaning of CET vs the machine meaning. To a person "CET" for Paris means "A location that is +1 hour UTC in the winter and +2 hours UTC in the summer." However, when you see output printed by the parser, CET means +1 hours, always. So when you print Paris times that happen before the "first" 3:00 am on 10/28, the parser is going to output it as CEST. When you print Paris times that happen after the "first" 3am on Oct 28, it will say CET.
At the time of the daylight saving switch Parisians will experience both 02:59 CET (machine meaning) and 02:59 CEST, rather than "02:59 CET twice" as might be a more human understanding. They're both valid times that exist.