importmath
# return whether a certain year is a leap year or not# e.g. 2020 -> True; 2019 -> Falsedefis_leap_year(year):
if0== year % 4and0!= year % 100or0== year % 400:
return Trueelse:
return False# parse the date str ('yyyymmdd' or 'yyyy-mm-dd') to yyyy, mm, dd, 3 ints# if the bits of yyyy is less than 4, or the bits of mm or dd less than 2, prefix them with 0(s)defparse(date):
date = date.replace('-', '').replace('_', '')
iflen(date) != 8:
print('Invalid date %s.'% date)
return-1, -1, -1yyyy = int(date[:4])
mm = int(date[4:6])
dd = int(date[-2:])
ifis_leap_year(yyyy):
month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
else:
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
ifmm <= 12anddd <= month_days[mm - 1]:
returnyyyy, mm, dd
else:
print('Invalid date %s.'% date)
return-1, -1, -1# return the index of a certain date in that year# e.g. '20200401' -> 92, which means it is the 92nd date in that yeardefdate2index(date):
yyyy, mm, dd = parse(date)
ifmm == -1:
return-1ifis_leap_year(yyyy):
month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
else:
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
date_in_year = dd
fori inrange(mm - 1):
date_in_year += month_days[i]
returndate_in_year
# return the date str and parsed ints of the nth date in year yyyy# e.g. 2020, 1 -> '20200101', 2020, 1, 1; 2019, 365 -> '20191331', 2019, 12, 31# 645, 192 -> '06450711', 645, 7, 11defindex2date(yyyy, idx):
ifis_leap_year(yyyy):
month_days_cum = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
else:
month_days_cum = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
ifidx <= 0oridx > month_days_cum[-1]:
return'invalid index %s.'% idx, -1, -1, -1fori inrange(1, 13):
ifidx <= month_days_cum[i]:
mm = i
dd = idx - month_days_cum[i - 1]
return'04ddd'% (yyyy, mm, dd), yyyy, mm, dd
# return the day of a certain date# e.g. '20200401' -> 3 (Wed)defdate2day(date):
yyyy, mm, dd = parse(date)
ifmm == -1:
return-1ifyyyy < 1582oryyyy == 1582andmm < 10oryyyy == 1582andmm == 10anddd < 15:
return'Can NOT calculate dates before Oct 15th 1582.'day = 0rela_hundreds = [0, 5, 3, 1]
rela_tens = [0, 3, 6, 2, 5, 1, 4, 0, 3, 6]
rela_month = [6, 2, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]
ifis_leap_year(yyyy) andmm <= 2:
day += 6yyyy %= 400day += rela_hundreds[int(yyyy / 100) % 10]
day += int(yyyy % 100/ 4)
day += rela_tens[int(yyyy / 10) % 10]
day += yyyy % 10day += rela_month[mm - 1]
day += dd
returnday % 7# return the date index of the nth Sun-day in year yyyy# target = {'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'}# e.g. 2020, 1, 'Tue' -> 7; 2020, 2, 'Thu' -> 9;# 2020, 5, 'Sun' -> 33 which means the 33rd date (Feb 2nd) in 2020# 2020, 9, 'Sat' -> 60 which means the 60th date (Feb 29th) in 2020defday2index(yyyy, n, target='Sun'):
ifn <= 0orn > 53:
return'Invalid num %s.'% n
new_year_day = date2day(str(yyyy) + '0101') # the day of the New Year's Dayday_dict = {'Sun': 0, 'Mon': 1, 'Tue': 2, 'Wed': 3, 'Thu': 4, 'Fri': 5, 'Sat': 6}
target_day = day_dict[target]
return(target_day - new_year_day + 7) % 7+ (n - 1) * 7+ 1# return the week index of a certain date in that year# start = {'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'} denotes the start day of a week# e.g. start = 'Sun': '20200101' -> 1; '20200104' -> 1; '20200105' -> 2; '20200106' -> 2;# start = 'Mon': '20200101' -> 1; '20200104' -> 1; '20200105' -> 1; 20200106' -> 2defdate2week(date, start='Sun'):
yyyy, mm, dd = parse(date)
ifmm == -1:
return-1date_in_year = date2index(date)
index_1st_day = day2index(yyyy, 1, start) # the index of the 1st Start-dayweek_in_year = math.ceil((date_in_year - index_1st_day + 1) / 7)
ifindex_1st_day != 1:
week_in_year += 1returnweek_in_year
# return the number of leap years between two different years (borders are NOT counted)# e.g 2020, 2021 -> 0; 2020, 2030 -> 2; 2016, 2020 -> 0; 2021, 2019 -> 1defn_leaps(yyyy1, yyyy2):
ifyyyy1 > yyyy2:
yyyy1, yyyy2 = yyyy2, yyyy1
n = 0fory inrange(yyyy1 + 1, yyyy2):
ifis_leap_year(y):
n = math.ceil((yyyy2 - y) / 4)
breakreturnn
# return the interval between 2 dates# e.g. '20190227', 20200403' -> 401; 20200403', '20200101' -> -91defdates_interval(date1, date2):
yyyy1, mm1, dd1 = parse(date1)
yyyy2, mm2, dd2 = parse(date2)
if-1in(yyyy1, mm1, dd1, yyyy2, mm2, dd2):
returnfloat('inf')
date_in_year1 = date2index(date1)
date_in_year2 = date2index(date2)
ifyyyy1 == yyyy2:
returndate_in_year2 - date_in_year1
elifyyyy1 < yyyy2:
ifis_leap_year(yyyy1):
interval1 = 366- date_in_year1
else:
interval1 = 365- date_in_year1
interval12 = (yyyy2 - yyyy1 - 1) * 365+ n_leaps(yyyy1, yyyy2)
interval2 = date_in_year2
returninterval1 + interval12 + interval2
else:
ifis_leap_year(yyyy2):
interval2 = 366- date_in_year2
else:
interval2 = 365- date_in_year2
interval21 = (yyyy1 - yyyy2 - 1) * 365+ n_leaps(yyyy2, yyyy1)
interval1 = date_in_year1
return-interval2 - interval21 - interval1