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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
# test conversion from date tuple to timestamp and back
try:
import time
time.localtime
except (ImportError, AttributeError):
print("SKIP")
raise SystemExit
# Range of date expected to work on all MicroPython platforms
MIN_YEAR = 1970
MAX_YEAR = 2099
# CPython properly supported date range:
# - on Windows: year 1970 to 3000+
# - on Unix: year 1583 to 3000+
# Start test from Jan 1, 2001 13:00 (Feb 2000 might already be broken)
SAFE_DATE = (2001, 1, 1, 13, 0, 0, 0, 0, -1)
# mktime function that checks that the result is reversible
def safe_mktime(date_tuple):
try:
res = time.mktime(date_tuple)
chk = time.localtime(res)
except OverflowError:
print("safe_mktime:", date_tuple, "overflow error")
return None
if chk[0:5] != date_tuple[0:5]:
print("safe_mktime:", date_tuple[0:5], " -> ", res, " -> ", chk[0:5])
return None
return res
# localtime function that checks that the result is reversible
def safe_localtime(timestamp):
try:
res = time.localtime(timestamp)
chk = time.mktime(res)
except OverflowError:
print("safe_localtime:", timestamp, "overflow error")
return None
if chk != timestamp:
print("safe_localtime:", timestamp, " -> ", res, " -> ", chk)
return None
return res
# look for smallest valid timestamps by iterating backwards on tuple
def test_bwd(date_tuple):
curr_stamp = safe_mktime(date_tuple)
year = date_tuple[0]
month = date_tuple[1] - 1
if month < 1:
year -= 1
month = 12
while year >= MIN_YEAR:
while month >= 1:
next_tuple = (year, month) + date_tuple[2:]
next_stamp = safe_mktime(next_tuple)
# at this stage, only test consistency and monotonicity
if next_stamp is None or next_stamp >= curr_stamp:
return date_tuple
date_tuple = next_tuple
curr_stamp = next_stamp
month -= 1
year -= 1
month = 12
return date_tuple
# test day-by-day to ensure that every date is properly converted
def test_fwd(start_date):
DAYS_PER_MONTH = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
curr_stamp = safe_mktime(start_date)
curr_date = safe_localtime(curr_stamp)
while curr_date[0] <= MAX_YEAR:
if curr_date[2] < 15:
skip_days = 13
else:
skip_days = 1
next_stamp = curr_stamp + skip_days * 86400
next_date = safe_localtime(next_stamp)
if next_date is None:
return curr_date
if next_date[2] != curr_date[2] + skip_days:
# next month
if next_date[2] != 1:
print("wrong day of month:", next_date)
return curr_date
# check the number of days in previous month
month_days = DAYS_PER_MONTH[curr_date[1]]
if month_days == 28 and curr_date[0] % 4 == 0:
if curr_date[0] % 100 != 0 or curr_date[0] % 400 == 0:
month_days += 1
if curr_date[2] != month_days:
print("wrong day count in prev month:", curr_date[2], "vs", month_days)
return curr_date
if next_date[1] != curr_date[1] + 1:
# next year
if curr_date[1] != 12:
print("wrong month count in prev year:", curr_date[1])
return curr_date
if next_date[1] != 1:
print("wrong month:", next_date)
return curr_date
if next_date[0] != curr_date[0] + 1:
print("wrong year:", next_date)
return curr_date
curr_stamp = next_stamp
curr_date = next_date
return curr_date
small_date = test_bwd(SAFE_DATE)
large_date = test_fwd(small_date)
print("tested from", small_date[0:3], "to", large_date[0:3])
print(small_date[0:3], "wday is", small_date[6])
print(large_date[0:3], "wday is", large_date[6])
|