[JAVA] WTF IS UP WITH DATES AND GREGORIAN CALENDAR ON SEP 1, 1905?

Posts

Pages: 1
So I ran into a vexing problem whose root cause (beyond deprecated Date classes and methods) in Java seems weird.

The gist behind it is that we are writing a new web app that interfaces with a legacy app that uses Dates, and we use XMLGregorianCalendar, so we do some date conversions.

But a weird thing happens for dates before September 1, 1905...

I wrote a snippet of code just to play around with it

private static DatatypeFactory dataFactory = null;
	static {
		try {
			dataFactory = DatatypeFactory.newInstance();
		} catch (DatatypeConfigurationException e) {
			throw new IllegalStateException(
					"Error while trying to obtain a new instance of DatatypeFactory",
					e);
		}
	}

	protected static String LONG_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
	protected static String SHORT_DATE_PATTERN = "yyyy-MM-dd";
		
	public static void main(String[] args) throws IOException {

		SimpleMain sm = new SimpleMain();
		// TODO Auto-generated method stub
		System.out.println("Hello World!");
		sm.datestuff();
	}


	private void datestuff() {
		Date myDate = new Date(1,0,1,0,0,0);
		Date my1Sep1905 = new Date(5,8,1,0,0,0);
		Date my31Aug1905 = new Date(5,7,31,0,0,0);
		Date my1997 = new Date(97,0,1,0,0,0);
		System.out.println("MD:" + myDate);
		System.out.println("MDSep1905:" + my1Sep1905);
		System.out.println("MDAug31:" + my31Aug1905);
		System.out.println("MD97:" + my1997);
		System.out.println("^^^^ INIT ^^^^^--------------------------");
		System.out.println("vvvv Business Effective Date vvvvvvvv--------------------------");
		XMLGregorianCalendar businessEffectiveDate = convertDateToXMLGregorianCalendar(myDate);
		XMLGregorianCalendar bed31Aug1905 = convertDateToXMLGregorianCalendar(my31Aug1905);
		XMLGregorianCalendar bed1905 = convertDateToXMLGregorianCalendar(my1Sep1905);
		XMLGregorianCalendar bed97 = convertDateToXMLGregorianCalendar(my1997);
		System.out.println("BED:" + businessEffectiveDate);
		System.out.println("BED1Sep1905:" + bed1905);
		System.out.println("BED31Aug1905:" + bed31Aug1905);
		System.out.println("BED97:" + bed97);
		
		String myString = businessEffectiveDate.toString();
		System.out.println("myStr:" + myString);
		

		System.out.println("--------------------------------");
		Calendar myCal = Calendar.getInstance();
	
		System.out.println("myCal: " + myCal);
		Date newD = businessEffectiveDate.toGregorianCalendar().getTime();
		System.out.println("newDate from myDate:" + newD);
		Date newD2 = bed1905.toGregorianCalendar().getTime();
		System.out.println("newDate2 from 1Sep1905:" + newD2);
		Date newD3 = bed31Aug1905.toGregorianCalendar().getTime();
		System.out.println("newDate3 from 31Aug1905:" + newD3);
		Date newD4 = bed97.toGregorianCalendar().getTime();
		System.out.println("newDate4 from 1997:" + newD4);
		
		System.out.println("--------------------------------millis");
		System.out.println("millis 1901:" + businessEffectiveDate.toGregorianCalendar().getTimeInMillis());
		System.out.println("millis 1Sep1905:" + bed1905.toGregorianCalendar().getTimeInMillis());
		System.out.println("millis 31Aug1905:" + bed31Aug1905.toGregorianCalendar().getTimeInMillis());
		System.out.println("millis 1997:" + bed97.toGregorianCalendar().getTimeInMillis());


		System.out.println("--------------------------------parsing");
		String parsed = parseDate(myDate, SHORT_DATE_PATTERN);
		System.out.println("parsed:" + parsed);
		parsed = parseDate(newD, SHORT_DATE_PATTERN);
		System.out.println("parsed newDate:" + parsed);
		parsed = parseDate(newD2, SHORT_DATE_PATTERN);
		System.out.println("parsed newDate2:" + parsed);
		parsed = parseDate(newD3, SHORT_DATE_PATTERN);
		System.out.println("parsed newDate3:" + parsed);
		parsed = parseDate(newD4, SHORT_DATE_PATTERN);
		System.out.println("parsed newDate4:" + parsed);
		
	}
	
	protected static String parseDate(Calendar cal, String aDatePattern) {
		Date date = cal != null ? cal.getTime() : null;
		return parseDate(date, aDatePattern);
	}
	protected static String parseDate(Date aDate,String aDatePattern)
	{
		if (aDate != null)
		{							
			SimpleDateFormat formatter = new SimpleDateFormat(aDatePattern);
			return formatter.format(aDate);							
		}	
		
		return null;
	}
	
	public static XMLGregorianCalendar convertDateToXMLGregorianCalendar(Date date) {
		XMLGregorianCalendar cal = null;
		if (date != null) {
			GregorianCalendar gCal = new GregorianCalendar();
			gCal.setTime(date);
			//gCal.set(date.getYear()+1900, date.getMonth(), date.getDay(), date.getHours(), date.getMinutes(), date.getSeconds());
			
			cal = dataFactory.newXMLGregorianCalendar(gCal);
		}
		return cal;
	}


I just made a simple Main class and ran this in the console right in Eclipse. Obligatory Hello World! included.

OUTPUT:

Hello World!
MD:Tue Jan 01 00:00:00 CST 1901
MDSep1905:Fri Sep 01 00:00:00 CST 1905
MDAug31:Thu Aug 31 00:00:00 CST 1905
MD97:Wed Jan 01 00:00:00 CST 1997
^^^^ INIT ^^^^^--------------------------
vvvv Business Effective Date vvvvvvvv--------------------------
BED:1901-01-01T00:00:00.000-06:58
BED1Sep1905:1905-09-01T00:00:00.000-07:00
BED31Aug1905:1905-08-31T00:00:00.000-06:58
BED97:1997-01-01T00:00:00.000-06:00
myStr:1901-01-01T00:00:00.000-06:58
--------------------------------
myCal: java.util.GregorianCalendar[time=1458853089219,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Regina",offset=-21600000,dstSavings=0,useDaylight=false,transitions=54,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=2,WEEK_OF_YEAR=13,WEEK_OF_MONTH=4,DAY_OF_MONTH=24,DAY_OF_YEAR=84,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=2,HOUR_OF_DAY=14,MINUTE=58,SECOND=9,MILLISECOND=219,ZONE_OFFSET=-21600000,DST_OFFSET=0]
newDate from myDate:Mon Dec 31 23:59:24 CST 1900
newDate2 from 1Sep1905:Fri Sep 01 00:00:00 CST 1905
newDate3 from 31Aug1905:Wed Aug 30 23:59:24 CST 1905
newDate4 from 1997:Wed Jan 01 00:00:00 CST 1997
--------------------------------millis
millis 1901:-2177427720000
millis 1Sep1905:-2030202000000
millis 31Aug1905:-2030288520000
millis 1997:852098400000
--------------------------------parsing
parsed:1901-01-01
parsed newDate:1900-12-31
parsed newDate2:1905-09-01
parsed newDate3:1905-08-30
parsed newDate4:1997-01-01


Pertinent information is Zone of "America/Regina" - which may be significant since Saskatchewan is retarded with regards to timezones and UTC offset (we don't observe DST... or we ALWAYS observe DST... year round, depending on how you look at it. https://en.wikipedia.org/wiki/Time_in_Saskatchewan )

Anywho, we are UTC -06:00

So take a look at my 31Aug1905
31Aug1905:Wed Aug 30 23:59:24 CST 1905
where the fuck did 36 seconds go??

Same with my 1901 date:
newDate from myDate:Mon Dec 31 23:59:24 CST 1900
???!

but the 1Sep1905 date is fine(??)
newDate2 from 1Sep1905:Fri Sep 01 00:00:00 CST 1905

this was noticed because in the legacy system the date is further parsed, and now because of that loss of 36 seconds, we seem to lose an entire day!
parsed newDate:1900-12-31
parsed newDate2:1905-09-01
parsed newDate3:1905-08-30
(in fact, that's how it all was discovered. Some records were showing a day earlier than it should in our testing. Trial and error and repeated tests is what lead us to the Sep 1, 1905 date "cutoff")

We have a bit of a time buff in the group, and he thought it might have something to do with Leap Seconds:

https://en.wikipedia.org/wiki/Leap_second

"In 1972, the leap-second system was introduced so that the broadcast UTC seconds could be made exactly equal to the standard SI second, while still maintaining the UTC time of day and changes of UTC date synchronized with those of UT1 (the solar time standard that superseded GMT). By then, the UTC clock was already 10 seconds behind TAI, which had been synchronized with UT1 in 1958, but had been counting true SI seconds since then. After 1972, both clocks have been ticking in SI seconds, so the difference between their readouts at any time is 10 seconds plus the total number of leap seconds that have been applied to UTC (36 seconds in July 2015)."

Holy shit. But is it relevant?? And why did it still work fine after Sep 1, 1905?

https://en.wikipedia.org/wiki/Saskatchewan

"Confederation September 1, 1905 (split from NWT) (10th)"

Saskatchewan became a province officially on September 1, 1905. Coincidence?? I HAVE NO IDEA.


If there are any Java gurus out there, can you try running this snippet of code (or similar tests)? Or do you know what the fuck is going on? This is so absurd.

(btw the workaround solution we implemented was to manually set the year month day when converting dates, instead of using setTime(Date) - but if you check the TimeInMillis compared to setting it all manually vs setTime the milliseconds are different)

what is going on??
InfectionFiles
the world ends in whatever my makerscore currently is
4622
Global warming, thanks obama

(Seriously though this is creepy and interesting)

Edit: also, this page is messed up on my phone. WHAT'S GOING ON?!
pianotm
The TM is for Totally Magical.
32388
Well, I'm no programmer, but I know calendars. There is not 24 hours in a day; there is 23.9344699 hours in a day. Time measurements aren't just spaced out to make it look like 24 hours: in the design of a calendar, specific days and times are chosen to to make minor adjustments to make sure dates and times remain consistent year after year. Sometimes, the adjustments are major. If you think 36 seconds missing from a day in 1905 is bad, in 1752, 11 whole days were deleted from Gregorian September in order to adjust a discrepancy that was missed in the Julian calendar, and that adjustment was planned two hundred years in advance. So yeah, if you work with precise dating measurements like that, you're going to find little weird things like that. Years often have a few seconds shaved off here and there. If these adjustments aren't represented in a calendar, you run the potential of creating a dating system that is days, weeks, or even months out of adjustment. So, yeah, it makes perfect sense to me that a program language would have to do that, because you can't just lay out a calendar on a yard stick that follows concise, consistent unit and expect everything to line up.
Look at the IANA tz data for Regina, in the file northamerica, these lines
# Zone NAME GMTOFF RULES FORMAT
Zone America/Regina -6:58:36 - LMT 1905 Sep
-7:00 Regina M%sT 1960 Apr lastSun 2:00
-6:00 - CST
this says that as of 1905 September the city of Regina is deemed to have switched from using the mean solar time of its meridian of longitude to using Mountain Time.
author=sla29970
Look at the IANA tz data for Regina, in the file northamerica, these lines
# Zone NAME GMTOFF RULES FORMAT
Zone America/Regina -6:58:36 - LMT 1905 Sep
-7:00 Regina M%sT 1960 Apr lastSun 2:00
-6:00 - CST
this says that as of 1905 September the city of Regina is deemed to have switched from using the mean solar time of its meridian of longitude to using Mountain Time.

amazing!
author=kentona
this was noticed because in the legacy system the date is further parsed, and now because of that loss of 36 seconds, we seem to lose an entire day!

The day that was lost. Could make for a compelling story. Why was this day deleted? Was it on purpose? What happened on this day? When exactly was this day lost? Who knows about this day that never existed?

O-oh and the other things about this are interesting - I'm just a nut who wants to think about how everything would be a cool story haha...
WIP
I'm not comfortable with any idea that can't be expressed in the form of men's jewelry
11363
I want to chime in that timezones and Dates in general are bullshit. Easily the most frustrating area I've had to work with in my programming career.
Pages: 1