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