Gregorian Date Conversions

Apropos

A Little Bit of Python Code

### Convert a year/month/date
### into a serial number

def day_number(*ymd):
    d = day_number_(*ymd)
    assert ymd == nth_date_(d), \
        f"Round trip failed {ymd}" \
        f" != nth_date({d})"
    return d

def day_number_(y, m, d):
    if m < 3:
        y -= 1
        m += 9
    else:
        m -= 3
    return \
        365*y +\
        y//4 - y//100 + y//400 +\
        30*m +\
        (6*m + 5)//10 +\
        d

def is_leap_year(y):
    return day_number_(y, 2, 29) \
        != day_number_(y, 3,  1)

### Convert a serial number
### into a year/month/date

# This should never fail its assertion
def nth_date(d):
    ymd = nth_date_(d)
    assert d == day_number_(*ymd), \
        f"Round trip failed {d}" \
        f" != day_number{ymd}"
    return ymd

def nth_date_(d):
    d -= 1
    (y, d) = date_divmod_365_2425(d)
    (m, d) = date_divmod_30_6(d)
    if m > 9:
        y += 1
        m -= 9
    else:
        m += 3
    d += 1
    return y, m, d

def date_divmod_365_2425(d):
    c, d = divmod(4*d + 3, 146097)
    d //= 4
    y, d = divmod(4*d + 3, 1461)
    d //= 4
    return c*100 + y, d

def date_divmod_30_6(d):
    m, d = divmod(10*d + 5, 306)
    d //= 10
    return m, d

### Pretty-print dates
### and serial numbers

days_of_week = (
    "Mon", "Tues", "Wednes", "Thurs",
    "Fri", "Satur", "Sun"
)

def print_header():
    print(
        "  yyyy/mm/dd"
        " #=>   in Week"
        "    0/3/1"
    )
    print(
        "  __________"
        "     _________"
        "  _______"
    )

def print_day(*ymd ,d=None):
    if d is None:
        d = day_number(*ymd)
    else:
        ymd = nth_date(d)
    print(
        f" {ymd[0]: 05}",
        f"/{ymd[1]:02}/{ymd[2]:02}"
            if ymd[1:] != (2, 29)
            else "*02*29",
        " #=> {:>6}day".format(
            days_of_week[(1 + d) % 7]
        ),
        f" {d:8}",
        sep=""
    )

### Example

print("Gregorian Proleptic Calendar")
print()

print("Day Numbers")
print_header()
print_day(-4713, 11, 24)
print_day(0, 2, 29)
print_day(1970, 1, 1)
print_day(2000, 1, 1)
print()

print("Julian Zero")
print_header()
print_day(-4713, 11, 24)
print()

print("Anno Domini")
print_header()
print_day(0, 1, 1)
print_day(0, 2, 28)
print_day(0, 2, 29)
print_day(0, 3, 1)
print_day(1, 1, 1)
print_day(1, 2, 28)
print_day(1, 3, 1)
print()

print("Computer Epochs")
print_header()
print_day(1970, 1, 1)
print_day(2000, 1, 1)
print()

print("Leap Years")
print_header()
for y in (1900, 2000, 2020, 2100):
    print_day(y, 2, 28)
    if is_leap_year(y):
        print_day(y, 2, 29)
    print_day(y, 3, 1)
print()

print("Today")
print_header()
def today():
    import time;
    return time.localtime()[:3]
print_day(*today())
print()

print("Random Days Test")
print_header()
def print_random_day():
    from random import randrange
    print_day(d=randrange(0, 1000000))
for _ in range(10):
    print_random_day()
print()

Code’s Output

Gregorian Proleptic Calendar

Day Numbers
  yyyy/mm/dd #=>   in Week    0/3/1
  __________     _________  _______
 -4713/11/24 #=>    Monday -1721119
  0000*02*29 #=>   Tuesday        0
  1970/01/01 #=>  Thursday   719469
  2000/01/01 #=>  Saturday   730426

Julian Zero
  yyyy/mm/dd #=>   in Week    0/3/1
  __________     _________  _______
 -4713/11/24 #=>    Monday -1721119

Anno Domini
  yyyy/mm/dd #=>   in Week    0/3/1
  __________     _________  _______
  0000/01/01 #=>  Saturday      -59
  0000/02/28 #=>    Monday       -1
  0000*02*29 #=>   Tuesday        0
  0000/03/01 #=> Wednesday        1
  0001/01/01 #=>    Monday      307
  0001/02/28 #=> Wednesday      365
  0001/03/01 #=>  Thursday      366

Computer Epochs
  yyyy/mm/dd #=>   in Week    0/3/1
  __________     _________  _______
  1970/01/01 #=>  Thursday   719469
  2000/01/01 #=>  Saturday   730426

Leap Years
  yyyy/mm/dd #=>   in Week    0/3/1
  __________     _________  _______
  1900/02/28 #=> Wednesday   693960
  1900/03/01 #=>  Thursday   693961
  2000/02/28 #=>    Monday   730484
  2000*02*29 #=>   Tuesday   730485
  2000/03/01 #=> Wednesday   730486
  2020/02/28 #=>    Friday   737789
  2020*02*29 #=>  Saturday   737790
  2020/03/01 #=>    Sunday   737791
  2100/02/28 #=>    Sunday   767009
  2100/03/01 #=>    Monday   767010

Today
  yyyy/mm/dd #=>   in Week    0/3/1
  __________     _________  _______
  2020/11/11 #=> Wednesday   738046

Random Days Test
  yyyy/mm/dd #=>   in Week    0/3/1
  __________     _________  _______
  0957/04/20 #=> Wednesday   349588
  1538/06/04 #=>  Saturday   561838
  2085/08/27 #=>    Monday   761711
  1798/03/19 #=>    Monday   656725
  1552/01/03 #=>  Thursday   566799
  0746/03/27 #=> Wednesday   272497
  0047/04/09 #=>   Tuesday    17206
  0068/04/14 #=>  Saturday    24882
  2136/05/26 #=>  Saturday   780245
  1785/08/31 #=> Wednesday   652142