129 lines
3.0 KiB
Python
129 lines
3.0 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
# This file is part of convertdate.
|
||
|
# http://github.com/fitnr/convertdate
|
||
|
|
||
|
# Licensed under the MIT license:
|
||
|
# http://opensource.org/licenses/MIT
|
||
|
# Copyright (c) 2016, fitnr <fitnr@fakeisthenewreal>
|
||
|
from calendar import isleap, monthrange
|
||
|
from .utils import floor, monthcalendarhelper, jwday
|
||
|
|
||
|
EPOCH = 1721425.5
|
||
|
|
||
|
INTERCALATION_CYCLE_YEARS = 400
|
||
|
INTERCALATION_CYCLE_DAYS = 146097
|
||
|
|
||
|
LEAP_SUPPRESSION_YEARS = 100
|
||
|
LEAP_SUPPRESSION_DAYS = 36524
|
||
|
|
||
|
LEAP_CYCLE_YEARS = 4
|
||
|
LEAP_CYCLE_DAYS = 1461
|
||
|
|
||
|
YEAR_DAYS = 365
|
||
|
|
||
|
HAVE_30_DAYS = (4, 6, 9, 11)
|
||
|
HAVE_31_DAYS = (1, 3, 5, 7, 8, 10, 12)
|
||
|
|
||
|
|
||
|
def legal_date(year, month, day):
|
||
|
'''Check if this is a legal date in the Gregorian calendar'''
|
||
|
if month == 2:
|
||
|
daysinmonth = 29 if isleap(year) else 28
|
||
|
else:
|
||
|
daysinmonth = 30 if month in HAVE_30_DAYS else 31
|
||
|
|
||
|
if not (0 < day <= daysinmonth):
|
||
|
raise ValueError("Month {} doesn't have a day {}".format(month, day))
|
||
|
|
||
|
return True
|
||
|
|
||
|
|
||
|
def to_jd2(year, month, day):
|
||
|
'''Gregorian to Julian Day Count for years between 1801-2099'''
|
||
|
# http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
|
||
|
|
||
|
legal_date(year, month, day)
|
||
|
|
||
|
if month <= 2:
|
||
|
year = year - 1
|
||
|
month = month + 12
|
||
|
|
||
|
a = floor(year / 100)
|
||
|
b = floor(a / 4)
|
||
|
c = 2 - a + b
|
||
|
e = floor(365.25 * (year + 4716))
|
||
|
f = floor(30.6001 * (month + 1))
|
||
|
return c + day + e + f - 1524.5
|
||
|
|
||
|
|
||
|
def to_jd(year, month, day):
|
||
|
legal_date(year, month, day)
|
||
|
|
||
|
if month <= 2:
|
||
|
leap_adj = 0
|
||
|
elif isleap(year):
|
||
|
leap_adj = -1
|
||
|
else:
|
||
|
leap_adj = -2
|
||
|
|
||
|
return (
|
||
|
EPOCH - 1 + (YEAR_DAYS * (year - 1)) +
|
||
|
floor((year - 1) / LEAP_CYCLE_YEARS) +
|
||
|
(-floor((year - 1) / LEAP_SUPPRESSION_YEARS)) +
|
||
|
floor((year - 1) / INTERCALATION_CYCLE_YEARS) +
|
||
|
floor((((367 * month) - 362) / 12) + leap_adj + day)
|
||
|
)
|
||
|
|
||
|
|
||
|
def from_jd(jd):
|
||
|
'''Return Gregorian date in a (Y, M, D) tuple'''
|
||
|
wjd = floor(jd - 0.5) + 0.5
|
||
|
depoch = wjd - EPOCH
|
||
|
|
||
|
quadricent = floor(depoch / INTERCALATION_CYCLE_DAYS)
|
||
|
dqc = depoch % INTERCALATION_CYCLE_DAYS
|
||
|
|
||
|
cent = floor(dqc / LEAP_SUPPRESSION_DAYS)
|
||
|
dcent = dqc % LEAP_SUPPRESSION_DAYS
|
||
|
|
||
|
quad = floor(dcent / LEAP_CYCLE_DAYS)
|
||
|
dquad = dcent % LEAP_CYCLE_DAYS
|
||
|
|
||
|
yindex = floor(dquad / YEAR_DAYS)
|
||
|
year = (
|
||
|
quadricent * INTERCALATION_CYCLE_YEARS +
|
||
|
cent * LEAP_SUPPRESSION_YEARS +
|
||
|
quad * LEAP_CYCLE_YEARS + yindex
|
||
|
)
|
||
|
|
||
|
if not (cent == 4 or yindex == 4):
|
||
|
year += 1
|
||
|
|
||
|
yearday = wjd - to_jd(year, 1, 1)
|
||
|
|
||
|
leap = isleap(year)
|
||
|
|
||
|
if yearday < 58 + leap:
|
||
|
leap_adj = 0
|
||
|
elif leap:
|
||
|
leap_adj = 1
|
||
|
else:
|
||
|
leap_adj = 2
|
||
|
|
||
|
month = floor((((yearday + leap_adj) * 12) + 373) / 367)
|
||
|
day = int(wjd - to_jd(year, month, 1)) + 1
|
||
|
|
||
|
return (year, month, day)
|
||
|
|
||
|
|
||
|
def month_length(year, month):
|
||
|
return monthrange(year, month)[1]
|
||
|
|
||
|
|
||
|
def monthcalendar(year, month):
|
||
|
start_weekday = jwday(to_jd(year, month, 1))
|
||
|
monthlen = month_length(year, month)
|
||
|
|
||
|
return monthcalendarhelper(start_weekday, monthlen)
|