Leap Years&Co.



  • Continuing the discussion from Practical ValiDATEion:

    @Maciejasjmj said:

    >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

    @Bulb said:

    “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.


  • Discourse touched me in a no-no place

    Paging @RaceProUK...


  • sockdevs

    *responds to summons*
    *reads OP*
    *drop*
    *sods off again*


  • Discourse touched me in a no-no place

    @Bulb said:

    // ... 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.


  • sockdevs

    if year % 4 == 0:
      if year % 100 == 0:
        if year % 400 == 0:
          return True
        return False
      return True
    return False
    

  • sockdevs

    return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
    

  • sockdevs

    or that if you want to abuse the ternary operator

    must, get, more, caffeine..... reading fail.


  • sockdevs

    @accalia said:

    ternary operator

    Which I didn't use :stuck_out_tongue:


  • sockdevs

    @RaceProUK said:

    Which I didn't use

    appologies. i have not yet consumed my morning coffee.



  • 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
    

    :trolleybus:


  • sockdevs

    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.



  • @accalia said:

    right.... forgot that True and False are capitalized for rasins in python.

    Yes, True, False and None are special snowflakes in Python.

    @Planar said:

    TRWTF is a ternary operator that takes its arguments in the wrong order.

    Like True, False and None, the BFDL is also a special snowflake and must have something special.



  • Benevolent For Dictator Life?

    ... Oh. My irony detector is broken. Huh.


  • Discourse touched me in a no-no place

    @Planar said:

    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!


  • sockdevs

    @Bulb said:

    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?


  • sockdevs

    there's no space in there. the nearest space is roughly a hundred klicks straight up from here.



  • @Bulb said:

    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.



  • @blakeyrat said:

    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

    :passport_control:



  • @tarunik said:

    proleptic calendar

    Is that where the inside parts of the calendar end up on the outside?



  • @blakeyrat said:

    I've never seen a Proleptic Gregorian date.

    <joke>



  • 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!"



  • @tarunik said:

    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.



  • @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.



  • @Bulb said:

    @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.



  • @RaceProUK said:

    @accalia said:
    ternary operator

    Which I didn't use :stuck_out_tongue:

    Which was the abuse of the ternary operator. It feels abandoned. You should use it. Always.



  • @Steve_The_Cynic said:

    "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.


  • Discourse touched me in a no-no place

    @Bulb said:

    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 :trolleybus:


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.