Welcome, guest | Sign In | My Account | Store | Cart
# Format a datetime through its full proleptic Gregorian date range.
#
# >>> strftime(datetime.date(1850, 8, 2), "%Y/%M/%d was a %A")
# '1850/00/02 was a Friday'
# >>>

import re, datetime, time

# remove the unsupposed "%s" command.  But don't
# do it if there's an even number of %s before the s
# because those are all escaped.  Can't simply
# remove the s because the result of
#  %sY
# should be %Y if %s isn't supported, not the
# 4 digit year.
_illegal_s = re.compile(r"((^|[^%])(%%)*%s)")

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

# Every 28 years the calendar repeats, except through century leap
# years where it's 6 years.  But only if you're using the Gregorian
# calendar.  ;)

def strftime(dt, fmt):
    if _illegal_s.search(fmt):
        raise TypeError("This strftime implementation does not handle %s")
    if dt.year > 1900:
        return dt.strftime(fmt)

    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(fmt, (year,) + timetuple[1:])
    sites1 = _findall(s1, str(year))
    
    s2 = time.strftime(fmt, (year+28,) + timetuple[1:])
    sites2 = _findall(s2, str(year+28))

    sites = []
    for site in sites1:
        if site in sites2:
            sites.append(site)
            
    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(datetime.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"
    # Get the weekdays.  Can't hard code them; they could be
    # localized.
    days = []
    for i in range(1, 10):
        days.append(datetime.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(2000, 8, 1)
    prevday = strftime(startdate, "%A")
    one_day = datetime.timedelta(1)

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

if __name__ == "__main__":
    test()

History