datetime strftime methods require year >= 1900

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • John Hunter

    #1

    datetime strftime methods require year >= 1900

    [color=blue][color=green][color=darkred]
    >>> from datetime import date
    >>> dt = date(1005,1,1)
    >>> print dt.strftime('%Y ')[/color][/color][/color]
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    ValueError: year=1005 is before 1900; the datetime strftime() methods require year >= 1900

    Does anyone know of a datetime string formatter that can handles
    strftime format strings over the full range that datetime objects
    support?

    Thanks,
    John Hunter
  • Anna Martelli Ravenscroft

    #2
    Re: datetime strftime methods require year &gt;= 1900

    John Hunter wrote:[color=blue][color=green][color=darkred]
    >>>>from datetime import date
    >>>>dt = date(1005,1,1)
    >>>>print dt.strftime('%Y ')[/color][/color]
    >
    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > ValueError: year=1005 is before 1900; the datetime strftime() methods require year >= 1900
    >
    > Does anyone know of a datetime string formatter that can handles
    > strftime format strings over the full range that datetime objects
    > support?
    >
    > Thanks,
    > John Hunter[/color]

    Why would you need to use strftime, if you've already got a year?[color=blue][color=green][color=darkred]
    >>> print dt.year[/color][/color][/color]
    1005

    HTH
    Anna

    Comment

    • John Hunter

      #3
      Re: datetime strftime methods require year &gt;= 1900

      >>>>> "Anna" == Anna Martelli Ravenscroft <anna@aleax.i t> writes:


      Anna> Why would you need to use strftime, if you've already got a
      Anna> year?[color=blue][color=green][color=darkred]
      >>>> print dt.year[/color][/color][/color]
      Anna> 1005

      That was just a simple-minded example. I need the ability to handle
      general format strings over the range of datetime dates.

      JDH

      Comment

      • Andrew Dalke

        #4
        Re: datetime strftime methods require year &gt;= 1900

        John Hunter wrote:[color=blue]
        > Does anyone know of a datetime string formatter that can handles
        > strftime format strings over the full range that datetime objects
        > support?[/color]

        Here's what the Python source says

        /* Give up if the year is before 1900.
        * Python strftime() plays games with the year, and different
        * games depending on whether envar PYTHON2K is set. This makes
        * years before 1900 a nightmare, even if the platform strftime
        * supports them (and not all do).
        * We could get a lot farther here by avoiding Python's strftime
        * wrapper and calling the C strftime() directly, but that isn't
        * an option in the Python implementation of this module.
        */

        The underlying time.strftime module supports special
        behaviour for dates < 1900.
        [color=blue][color=green][color=darkred]
        >>> time.accept2dye ar[/color][/color][/color]
        1[color=blue][color=green][color=darkred]
        >>> time.strftime(" %Y", datetime.date(2 0, 1, 1).timetuple())[/color][/color][/color]
        '2020'[color=blue][color=green][color=darkred]
        >>> time.accept2dye ar = 0
        >>> time.strftime(" %Y", datetime.date(2 0, 1, 1).timetuple())[/color][/color][/color]
        Traceback (most recent call last):
        File "<stdin>", line 1, in ?
        ValueError: year >= 1900 required[color=blue][color=green][color=darkred]
        >>>[/color][/color][/color]

        One concern about your request is, what a date mean
        when you get before 1900? I assume you want the proleptic
        Gregorian calendar, that is, to apply it even when and
        where it wasn't in use.

        One way to fake it is to move the date to a date in the
        supported time range which starts on the same day, then
        use strftime on that new date.

        It's not enough to find the fake year number in the
        resulting string and convert it into the real year
        number. After all, the format string might be
        "1980 %Y" and if the %Y expands to 1980 in your shifted
        time frame then you don't know which to change.

        To figure that out, move the date forward by 28 years
        (which is the repeat cycle except for the non-leap
        centuries) and do it again. The parts of the two
        strings that differ indicate where to put the change.

        I tried to write this function but I wasn't sure
        how to handle the non-leap year centuries. It seems
        to be that those are the same as 6 years later, so
        that Jan. 1900's calendar looks like 1906's.

        Here's what I came up with. Seems to work.


        # Format a datetime.date using the proleptic Gregorian calendar

        import time, datetime

        def _findall(text, substr):
        # Also finds overlaps
        sites = []
        i = 0
        while 1:
        j = text.find(subst r, i)
        if j == -1:
        break
        sites.append(j)
        i=j+1
        return sites

        # I hope I did this math right. Every 28 years the
        # calendar repeats, except through century leap years
        # excepting the 400 year leap years. But only if
        # you're using the Gregorian calendar.

        def strftime(dt, fmt):
        # WARNING: known bug with "%s", which is the number
        # of seconds since the epoch. This is too harsh
        # of a check. It should allow "%%s".
        fmt = fmt.replace("%s ", "s")
        if dt.year > 1900:
        return time.strftime(f mt, dt.timetuple())

        year = dt.year
        # For every non-leap year century, advance by
        # 6 years to get into the 28-year repeat cycle
        delta = 2000 - year
        off = 6*(delta // 100 + delta // 400)
        year = year + off

        # Move to around the year 2000
        year = year + ((2000 - year)//28)*28
        timetuple = dt.timetuple()
        s1 = time.strftime(f mt, (year,) + timetuple[1:])
        sites1 = _findall(s1, str(year))

        s2 = time.strftime(f mt, (year+28,) + timetuple[1:])
        sites2 = _findall(s2, str(year+28))

        sites = []
        for site in sites1:
        if site in sites2:
        sites.append(si te)

        s = s1
        syear = "%4d" % (dt.year,)
        for site in sites:
        s = s[:site] + syear + s[site+4:]
        return s

        # Make sure that the day names are in order
        # from 1/1/1 until August 2000
        def test():
        s = strftime(dateti me.date(1800, 9, 23),
        "%Y has the same days as 1980 and 2008")
        if s != "1800 has the same days as 1980 and 2008":
        raise AssertionError( s)

        print "Testing all day names from 0001/01/01 until 2000/08/01"
        days = []
        for i in range(1, 10):
        days.append(dat etime.date(2000 , 1, i).strftime("%A "))
        nextday = {}
        for i in range(8):
        nextday[days[i]] = days[i+1]

        startdate = datetime.date(1 , 1, 1)
        enddate = datetime.date(2 000, 8, 1)
        prevday = strftime(startd ate, "%A")
        one_day = datetime.timede lta(1)

        testdate = startdate + one_day
        while testdate < enddate:
        if (testdate.day == 1 and testdate.month == 1 and
        (testdate.year % 100 == 0)):
        print testdate.year
        day = strftime(testda te, "%A")
        if nextday[prevday] != day:
        raise AssertionError( str(testdate))
        prevday = day
        testdate = testdate + one_day

        if __name__ == "__main__":
        test()

        [color=blue][color=green][color=darkred]
        >>> strftime(dateti me.date(1850, 8, 2), "%Y/%M/%d was a %A")[/color][/color][/color]
        '1850/00/02 was a Friday'[color=blue][color=green][color=darkred]
        >>>[/color][/color][/color]

        % cal 8 1850
        August 1850
        S M Tu W Th F S
        1 2 3
        4 5 6 7 8 9 10
        11 12 13 14 15 16 17
        18 19 20 21 22 23 24
        25 26 27 28 29 30 31

        Andrew
        dalke@dalkescie ntific.com

        Comment

        • Andrew Dalke

          #5
          Re: datetime strftime methods require year &gt;= 1900

          Andrew Dalke wrote:[color=blue][color=green][color=darkred]
          > >>> strftime(dateti me.date(1850, 8, 2), "%Y/%M/%d was a %A")[/color][/color]
          > '1850/00/02 was a Friday'[/color]

          While "%Y/%m/%d was a %A" gives

          1850/08/02 was a Friday
          [color=blue]
          >
          > % cal 8 1850
          > August 1850
          > S M Tu W Th F S
          > 1 2 3
          > 4 5 6 7 8 9 10
          > 11 12 13 14 15 16 17
          > 18 19 20 21 22 23 24
          > 25 26 27 28 29 30 31[/color]

          I once played around with an alternate time
          format string that allowed

          %(2000)/%(12)/%(31) to mean %Y, %m, %d.
          %(Jan)/%(January)/%(Mon)/%(Monday) to mean %b/%B/%a/%A

          because I forget which name is which.

          Andrew
          dalke@dalkescie ntific.com

          Comment

          Working...