Leap Years&Co.
-
Continuing the discussion from Practical ValiDATEion:
>attempted leap year handling which makes February 29, 2000 a perfectly valid date
Oh fuck, it is a perfectly valid date.
Looks like the right time I pull out the date handling code we have in our codebase. It was written by the
βprogrammerβ with a really bad case of NIH syndrome
int64_t Atime::privToI64(SYSTEMTIME * pst, int32_t * pms/*=NULL*/) { uint32_t i,f; // starting from zero int64_t itime=0; // flag for actual leap year i=pst->wYear; f=(((i&3)||(((i%400)==0)&&(i!=2000)))?0:1); // non-leap 400 years while(i>400) { itime+=12622953600000; i-=400; } // stupid exception from exceptions (year 2000 was leap, so everything after February 2000 must be shifted by one day) if((pst->wYear>2000) || ((pst->wYear==2000) && (pst->wMonth>=3))) itime+=86400000; // we are done with non-leap 400-years, now leap centuries will follow while(i>100) { itime+=3155760000000; i-=100; } // we are at the begining of century, leap 4-years follow while(i>4) { itime+=126230400000; i-=4; } // we are at the begining of next 4-year cycle, adding non-leap years while(i>1) { itime+=31536000000; i-=1; } // we are in current year, adding time for complete months (that's why subtracting 1, in March only January and February are complete) switch(pst->wMonth-1) { default: break; case 11: itime+=2592000000ll; case 10: itime+=2678400000ll; case 9: itime+=2592000000ll; case 8: itime+=2678400000ll; case 7: itime+=2678400000ll; case 6: itime+=2592000000ll; case 5: itime+=2678400000ll; case 4: itime+=2592000000ll; case 3: itime+=2678400000ll; case 2: itime+=(f?2505600000ll:2419200000ll); // based on actual leapness case 1: itime+=2678400000ll; } // add time for whole days i=pst->wDay; while(i>1) { itime+=86400000; i--; } // add whole hours itime+=pst->wHour*3600000; // add whole minutes itime+=pst->wMinute*60000; // add seconds itime+=pst->wSecond*1000; // and finally miliseconds itime+=pst->wMilliseconds; m_itime = itime; return m_itime ; }
The idea is that time is internally represented by number of (non-leap) seconds since beginning of 1 Jan 0.
The code survived 7 years in good health and is not about to be replaced any time soon, because the code only ever deals with times from it's creation until current time, so we never gave a damn about not being able to represent dates before 20th century correctly and we don't give a damn about 22nd century just yet either.
-
Paging @RaceProUK...
-
*responds to summons*
*reads OP*
*drop*
*sods off again*
-
// ... so everything after February 200 ....
Was that a transcription error, or is that what's actually in the code?...
-
The missing 0 is transcription error. The rest is original
-
I actually looked into this years ago when I thought writing a date library was a good idea (as opposed to using one off the shelf...). What can I say - I was younger and stupider.
Anyway, from memory, the rules are:
We have a leap year if the year is divisible by 4.
Unless it's also divisible by 100, in which case it's not a leap year.
Unless it's also divisible by 400, in which case it IS a leap year.See - you learn something every day...
-
Yes, correct. Unlike the guy who wrote the code above and mixed them up.
-
if year % 4 == 0: if year % 100 == 0: if year % 400 == 0: return True return False return True return False
-
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
-
or that if you want toabuse the ternary operatormust, get, more, caffeine..... reading fail.
-
-
-
The True Python Wayβ’ is:
return True if year % 400 == 0 else False if year % 100 == 0 else True if year % 4 == 0 else False
-
right.... forgot that True and False are capitalized for rasins in python.
-
TRWTF is a ternary operator that takes its arguments in the wrong order.
-
right.... forgot that True and False are capitalized for rasins in python.
Yes,
True
,False
andNone
are special snowflakes in Python.TRWTF is a ternary operator that takes its arguments in the wrong order.
Like
True
,False
andNone
, the BFDL is also a special snowflake and must have something special.
-
Benevolent For Dictator Life?
... Oh. My irony detector is broken. Huh.
-
TRWTF is a ternary operator that takes its arguments in the wrong order.
Because right-to-left and left-to-right evaluation orders are too conventional!
-
a special snowflake and must have something special
$ flame to /dev/null flame: /dev/null: No space left on device $
-
Did you check if there was space right on your device?
-
there's no space in there. the nearest space is roughly a hundred klicks straight up from here.
-
The idea is that time is internally represented by number of (non-leap) seconds since beginning of 1 Jan 0.
Really? The epoch is a date/time that doesn't exist? (Don't forget, boys and girls, that the year before 1 was -1.)
-
And none of that matters, since the Gregorian calendar wasn't adopted until, what, AD 1753 or something like that? (In case you were wondering why SQL Server's epoch date was 1753, now you know.)
Before that date, it's nonsensical to give any dates following the Gregorian rules-- you either need to switch to the Julian rules or (much more sanely) use Julian Day Count. The Gregorian calendar can't be used to count dates that occurred before its existence.
Making things even more complicated is that not every country adopted Gregorian rules at the same time. So you have things like Russia's October Revolution that took place on November 7th. And those insane Asian calendars where you don't know the date unless you know the emperor's line of succession.
-
And none of that matters, since the Gregorian calendar wasn't adopted until, what, AD 1753 or something like that? (In case you were wondering why SQL Server's epoch date was 1753, now you know.)
Before that date, it's nonsensical to give any dates following the Gregorian rules-- you either need to switch to the Julian rules or (much more sanely) use Julian Day Count. The Gregorian calendar can't be used to count dates that occurred before its existence.
Making things even more complicated is that not every country adopted Gregorian rules at the same time. So you have things like Russia's October Revolution that took place on November 7th. And those insane Asian calendars where you don't know the date unless you know the emperor's line of succession.
Never heard of a proleptic calendar eh, Blakey?
I will agree that Julian Day Count is a good idea, though.
-
Dafuq? That article is so wrong.
Mayan scholars (as well as ALL scholars) use Julian Day Count. I've never seen a Proleptic Gregorian date.
I guess it's "only" 2 days off if you go back 2,000 years, but. Still. Ugh. Just sounds like a terrible idea.
-
You should join the ISO 8601 committee then
-
proleptic calendar
Is that where the inside parts of the calendar end up on the outside?
-
-
Some countries (Catholic ones) adopted the Gregorian calendar as early as 1582 (the year it was proposed). Protestant countries adopted it later and Orthodox countries only in the 20th century.
You also get weird stuff like Miguel Cervantes and William Shakespeare dying on the same date, but not the same day.
-
That doesn't count leap seconds.
And the other points where we are like
"Hell, it got off again!"
-
You should join the ISO 8601 committee then
I'm guessing very few people who communicate in ISO 8601 are using dates older than, say, 1900.
-
Really? The epoch is a date/time that doesn't exist? (Don't forget, boys and girls, that the year before 1 was -1.)
No. Before year AD 1 (or CE 1) was a year 1 BC. That is not -1 however. It is 1 BC. Year -1 only exists in astronomical year numbering and ISO 8601 and both have 0 between -1 and 1.
-
@Steve_The_Cynic said:
Really? The epoch is a date/time that doesn't exist? (Don't forget, boys and girls, that the year before 1 was -1.)
No. Before year AD 1 (or CE 1) was a year 1 BC. That is not -1 however. It is 1 BC. Year -1 only exists in astronomical year numbering and ISO 8601 and both have 0 between -1 and 1.
OK, yes, pendantically you're right, but it's reasonably safe to argue that the "AD" and "BC" (how very quaintly politically incorrect of you, I approve) are just strange ways to spell "+" and "-", like "CR" and blank in accounting. (Remember the System 370 instruction "EDIT" that had the ability to do something like this. In EBCDIC, of course.) (Yes, on System 370,Int32.ToString(n)
was an assembly-level instruction. How's that for scary!)Oh, and if you call 1AD by the new name 1 CE, the year before it should be 1 BCE.
-
@accalia said:
ternary operator
Which I didn't useWhich was the abuse of the ternary operator. It feels abandoned. You should use it. Always.
-
"AD" and "BC" are just strange ways to spell "+" and "-"
Well, except they are not, because between +1 and -1 there is 0, but there is nothing between AD 1 and 1 BC.
-
Well, except they are not, because between +1 and -1 there is 0, but there is nothing between AD 1 and 1 BC.
Fortunately, 0 is nothing. :p