3 # © Copyright 2021-2022, Scott Gasch
5 """dateparse_utils unittest."""
14 import pyutils.datetimez.dateparse_utils as du
15 import pyutils.unittest_utils as uu
17 parsable_expressions = [
18 ('today', datetime.datetime(2021, 7, 2)),
19 ('tomorrow', datetime.datetime(2021, 7, 3)),
20 ('yesterday', datetime.datetime(2021, 7, 1)),
21 ('21:30', datetime.datetime(2021, 7, 2, 21, 30, 0, 0)),
24 datetime.datetime(2021, 7, 2, 21, 30, 0, 0, tzinfo=pytz.timezone('EST')),
28 datetime.datetime(2021, 7, 2, 21, 30, 0, 0, tzinfo=pytz.timezone('EST')),
30 ('12:01am', datetime.datetime(2021, 7, 2, 0, 1, 0, 0)),
31 ('12:02p', datetime.datetime(2021, 7, 2, 12, 2, 0, 0)),
32 ('0:03', datetime.datetime(2021, 7, 2, 0, 3, 0, 0)),
33 ('last wednesday', datetime.datetime(2021, 6, 30)),
34 ('this wed', datetime.datetime(2021, 7, 7)),
35 ('next wed', datetime.datetime(2021, 7, 14)),
36 ('this coming tues', datetime.datetime(2021, 7, 6)),
37 ('this past monday', datetime.datetime(2021, 6, 28)),
38 ('4 days ago', datetime.datetime(2021, 6, 28)),
39 ('4 mondays ago', datetime.datetime(2021, 6, 7)),
40 ('4 months ago', datetime.datetime(2021, 3, 2)),
41 ('3 days back', datetime.datetime(2021, 6, 29)),
42 ('13 weeks from now', datetime.datetime(2021, 10, 1)),
43 ('1 year from now', datetime.datetime(2022, 7, 2)),
44 ('4 weeks from now', datetime.datetime(2021, 7, 30)),
45 ('3 saturdays ago', datetime.datetime(2021, 6, 12)),
46 ('4 months from today', datetime.datetime(2021, 11, 2)),
47 ('4 years from yesterday', datetime.datetime(2025, 7, 1)),
48 ('4 weeks from tomorrow', datetime.datetime(2021, 7, 31)),
49 ('april 15, 2005', datetime.datetime(2005, 4, 15)),
50 ('april 14', datetime.datetime(2021, 4, 14)),
51 ('9:30am on last wednesday', datetime.datetime(2021, 6, 30, 9, 30)),
52 ('2005/apr/15', datetime.datetime(2005, 4, 15)),
53 ('2005 apr 15', datetime.datetime(2005, 4, 15)),
54 ('the 1st wednesday in may', datetime.datetime(2021, 5, 5)),
55 ('last sun of june', datetime.datetime(2021, 6, 27)),
56 ('this Easter', datetime.datetime(2021, 4, 4)),
57 ('last christmas', datetime.datetime(2020, 12, 25)),
58 ('last Xmas', datetime.datetime(2020, 12, 25)),
59 ('xmas, 1999', datetime.datetime(1999, 12, 25)),
60 ('next mlk day', datetime.datetime(2022, 1, 17)),
61 ('Halloween, 2020', datetime.datetime(2020, 10, 31)),
62 ('5 work days after independence day', datetime.datetime(2021, 7, 12)),
63 ('50 working days from last wed', datetime.datetime(2021, 9, 10)),
64 ('25 working days before columbus day', datetime.datetime(2021, 9, 3)),
65 ('today +1 week', datetime.datetime(2021, 7, 9)),
66 ('sunday -3 weeks', datetime.datetime(2021, 6, 13)),
67 ('4 weeks before xmas, 1999', datetime.datetime(1999, 11, 27)),
68 ('3 days before new years eve, 2000', datetime.datetime(2000, 12, 28)),
69 ('july 4th', datetime.datetime(2021, 7, 4)),
70 ('the ides of march', datetime.datetime(2021, 3, 15)),
71 ('the nones of april', datetime.datetime(2021, 4, 5)),
72 ('the kalends of may', datetime.datetime(2021, 5, 1)),
73 ('9/11/2001', datetime.datetime(2001, 9, 11)),
74 ('4 sundays before veterans\' day', datetime.datetime(2021, 10, 17)),
75 ('xmas eve', datetime.datetime(2021, 12, 24)),
76 ('this friday at 5pm', datetime.datetime(2021, 7, 9, 17, 0, 0)),
77 ('presidents day', datetime.datetime(2021, 2, 15)),
78 ('memorial day, 1921', datetime.datetime(1921, 5, 30)),
79 ('today -4 wednesdays', datetime.datetime(2021, 6, 9)),
80 ('thanksgiving', datetime.datetime(2021, 11, 25)),
81 ('2 sun in jun', datetime.datetime(2021, 6, 13)),
82 ('easter -40 days', datetime.datetime(2021, 2, 23)),
83 ('easter +39 days', datetime.datetime(2021, 5, 13)),
84 ('2nd Sunday in May, 2022', datetime.datetime(2022, 5, 8)),
85 ('1st tuesday in nov, 2024', datetime.datetime(2024, 11, 5)),
87 '2 days before last xmas at 3:14:15.92a',
88 datetime.datetime(2020, 12, 23, 3, 14, 15, 92),
91 '3 weeks after xmas, 1995 at midday',
92 datetime.datetime(1996, 1, 15, 12, 0, 0),
95 '4 months before easter, 1992 at midnight',
96 datetime.datetime(1991, 12, 19),
99 '5 months before halloween, 1995 at noon',
100 datetime.datetime(1995, 5, 31, 12),
102 ('4 days before last wednesday', datetime.datetime(2021, 6, 26)),
103 ('44 months after today', datetime.datetime(2025, 3, 2)),
104 ('44 years before today', datetime.datetime(1977, 7, 2)),
105 ('44 weeks ago', datetime.datetime(2020, 8, 28)),
106 ('15 minutes to 3am', datetime.datetime(2021, 7, 2, 2, 45)),
107 ('quarter past 4pm', datetime.datetime(2021, 7, 2, 16, 15)),
108 ('half past 9', datetime.datetime(2021, 7, 2, 9, 30)),
109 ('4 seconds to midnight', datetime.datetime(2021, 7, 1, 23, 59, 56)),
111 '4 seconds to midnight, tomorrow',
112 datetime.datetime(2021, 7, 2, 23, 59, 56),
114 ('2021/apr/15T21:30:44.55', datetime.datetime(2021, 4, 15, 21, 30, 44, 55)),
116 '2021/apr/15 at 21:30:44.55',
117 datetime.datetime(2021, 4, 15, 21, 30, 44, 55),
120 '2021/4/15 at 21:30:44.55',
121 datetime.datetime(2021, 4, 15, 21, 30, 44, 55),
124 '2021/04/15 at 21:30:44.55',
125 datetime.datetime(2021, 4, 15, 21, 30, 44, 55),
128 '2021/04/15 at 21:30:44.55Z',
129 datetime.datetime(2021, 4, 15, 21, 30, 44, 55, tzinfo=pytz.timezone('UTC')),
132 '2021/04/15 at 21:30:44.55EST',
133 datetime.datetime(2021, 4, 15, 21, 30, 44, 55, tzinfo=pytz.timezone('EST')),
136 '13 days after last memorial day at 12 seconds before 4pm',
137 datetime.datetime(2020, 6, 7, 15, 59, 48),
140 ' 2 days before yesterday at 9am ',
141 datetime.datetime(2021, 6, 29, 9),
143 ('-3 days before today', datetime.datetime(2021, 7, 5)),
145 '3 days before yesterday at midnight EST',
146 datetime.datetime(2021, 6, 28, tzinfo=pytz.timezone('EST')),
151 class TestDateparseUtils(unittest.TestCase):
152 @uu.check_method_for_perf_regressions
153 def test_dateparsing(self):
154 dp = du.DateParser(override_now_for_test_purposes=datetime.datetime(2021, 7, 2))
156 for (txt, expected_dt) in parsable_expressions:
158 actual_dt = dp.parse(txt)
159 self.assertIsNotNone(actual_dt)
163 f'"{txt}", got "{actual_dt}" while expecting "{expected_dt}"',
165 except du.ParseException:
166 self.fail(f'Expected "{txt}" to parse successfully.')
168 def test_whitespace_handling(self):
169 dp = du.DateParser(override_now_for_test_purposes=datetime.datetime(2021, 7, 2))
171 for (txt, expected_dt) in parsable_expressions:
174 i = random.randint(2, 5)
175 replacement = ' ' * i
176 txt = re.sub(r'\s', replacement, txt)
177 actual_dt = dp.parse(txt)
178 self.assertIsNotNone(actual_dt)
182 f'"{txt}", got "{actual_dt}" while expecting "{expected_dt}"',
184 except du.ParseException:
185 self.fail(f'Expected "{txt}" to parse successfully.')
188 if __name__ == '__main__':