|
1 | | -import time |
2 | 1 | import statistics |
| 2 | +import time |
3 | 3 | from datetime import datetime, timezone |
| 4 | + |
4 | 5 | from dateutil import parser |
5 | 6 | from whenever import Instant, OffsetDateTime |
6 | 7 |
|
7 | 8 | from airbyte_cdk.utils.datetime_helpers import ab_datetime_parse |
8 | 9 |
|
9 | 10 | # Test formats |
10 | 11 | test_formats = [ |
11 | | - '2023-03-14T15:09:26Z', # ISO format with T delimiter and Z timezone |
12 | | - '2023-03-14T15:09:26+00:00', # ISO format with +00:00 timezone |
13 | | - '2023-03-14T15:09:26.123456Z', # ISO format with microseconds |
14 | | - '2023-03-14T15:09:26-04:00', # ISO format with non-UTC timezone |
15 | | - '2023-03-14 15:09:26Z', # Missing T delimiter |
16 | | - '2023-03-14 15:09:26', # Missing T delimiter and timezone |
17 | | - '2023-03-14', # Date-only format |
18 | | - '14/03/2023 15:09:26', # Different date format |
19 | | - '2023/03/14T15:09:26Z', # Non-standard date separator |
| 12 | + "2023-03-14T15:09:26Z", # ISO format with T delimiter and Z timezone |
| 13 | + "2023-03-14T15:09:26+00:00", # ISO format with +00:00 timezone |
| 14 | + "2023-03-14T15:09:26.123456Z", # ISO format with microseconds |
| 15 | + "2023-03-14T15:09:26-04:00", # ISO format with non-UTC timezone |
| 16 | + "2023-03-14 15:09:26Z", # Missing T delimiter |
| 17 | + "2023-03-14 15:09:26", # Missing T delimiter and timezone |
| 18 | + "2023-03-14", # Date-only format |
| 19 | + "14/03/2023 15:09:26", # Different date format |
| 20 | + "2023/03/14T15:09:26Z", # Non-standard date separator |
20 | 21 | ] |
21 | 22 |
|
22 | 23 | # Number of iterations for each test |
|
31 | 32 |
|
32 | 33 | for dt_str in test_formats: |
33 | 34 | print(f"\nFormat: {dt_str}") |
34 | | - |
| 35 | + |
35 | 36 | # Test whenever parsing |
36 | 37 | whenever_times = [] |
37 | 38 | whenever_success = False |
38 | | - |
| 39 | + |
39 | 40 | # Try Instant.parse_common_iso |
40 | 41 | try: |
41 | 42 | # Warmup |
42 | 43 | Instant.parse_common_iso(dt_str) |
43 | | - |
| 44 | + |
44 | 45 | for _ in range(iterations): |
45 | 46 | start = time.perf_counter() |
46 | 47 | Instant.parse_common_iso(dt_str) |
47 | 48 | end = time.perf_counter() |
48 | 49 | whenever_times.append((end - start) * 1000) |
49 | | - |
| 50 | + |
50 | 51 | whenever_success = True |
51 | 52 | whenever_method = "Instant.parse_common_iso" |
52 | 53 | except Exception: |
53 | 54 | pass |
54 | | - |
| 55 | + |
55 | 56 | # If parse_common_iso failed, try parse_rfc3339 |
56 | 57 | if not whenever_success: |
57 | 58 | try: |
58 | 59 | # Warmup |
59 | 60 | Instant.parse_rfc3339(dt_str) |
60 | | - |
| 61 | + |
61 | 62 | whenever_times = [] |
62 | 63 | for _ in range(iterations): |
63 | 64 | start = time.perf_counter() |
64 | 65 | Instant.parse_rfc3339(dt_str) |
65 | 66 | end = time.perf_counter() |
66 | 67 | whenever_times.append((end - start) * 1000) |
67 | | - |
| 68 | + |
68 | 69 | whenever_success = True |
69 | 70 | whenever_method = "Instant.parse_rfc3339" |
70 | 71 | except Exception: |
71 | 72 | pass |
72 | | - |
| 73 | + |
73 | 74 | # If both Instant methods failed, try OffsetDateTime.parse_common_iso |
74 | 75 | if not whenever_success: |
75 | 76 | try: |
76 | 77 | # Warmup |
77 | 78 | OffsetDateTime.parse_common_iso(dt_str) |
78 | | - |
| 79 | + |
79 | 80 | whenever_times = [] |
80 | 81 | for _ in range(iterations): |
81 | 82 | start = time.perf_counter() |
82 | 83 | OffsetDateTime.parse_common_iso(dt_str) |
83 | 84 | end = time.perf_counter() |
84 | 85 | whenever_times.append((end - start) * 1000) |
85 | | - |
| 86 | + |
86 | 87 | whenever_success = True |
87 | 88 | whenever_method = "OffsetDateTime.parse_common_iso" |
88 | 89 | except Exception: |
89 | 90 | whenever_method = "None" |
90 | | - |
| 91 | + |
91 | 92 | # Test dateutil parsing |
92 | 93 | dateutil_times = [] |
93 | 94 | try: |
94 | 95 | # Warmup |
95 | 96 | parser.parse(dt_str) |
96 | | - |
| 97 | + |
97 | 98 | for _ in range(iterations): |
98 | 99 | start = time.perf_counter() |
99 | 100 | parser.parse(dt_str) |
100 | 101 | end = time.perf_counter() |
101 | 102 | dateutil_times.append((end - start) * 1000) |
102 | | - |
| 103 | + |
103 | 104 | dateutil_success = True |
104 | 105 | except Exception: |
105 | 106 | dateutil_success = False |
106 | | - |
| 107 | + |
107 | 108 | # Test ab_datetime_parse |
108 | 109 | ab_times = [] |
109 | 110 | try: |
110 | 111 | # Warmup |
111 | 112 | ab_datetime_parse(dt_str) |
112 | | - |
| 113 | + |
113 | 114 | for _ in range(iterations): |
114 | 115 | start = time.perf_counter() |
115 | 116 | ab_datetime_parse(dt_str) |
116 | 117 | end = time.perf_counter() |
117 | 118 | ab_times.append((end - start) * 1000) |
118 | | - |
| 119 | + |
119 | 120 | ab_success = True |
120 | 121 | except Exception: |
121 | 122 | ab_success = False |
122 | | - |
| 123 | + |
123 | 124 | # Print results |
124 | 125 | if whenever_success: |
125 | 126 | whenever_avg = statistics.mean(whenever_times) |
126 | 127 | whenever_min = min(whenever_times) |
127 | 128 | whenever_max = max(whenever_times) |
128 | | - print(f" {whenever_method}: avg={whenever_avg:.3f}ms, min={whenever_min:.3f}ms, max={whenever_max:.3f}ms") |
| 129 | + print( |
| 130 | + f" {whenever_method}: avg={whenever_avg:.3f}ms, min={whenever_min:.3f}ms, max={whenever_max:.3f}ms" |
| 131 | + ) |
129 | 132 | else: |
130 | 133 | print(f" whenever: Failed to parse") |
131 | | - |
| 134 | + |
132 | 135 | if dateutil_success: |
133 | 136 | dateutil_avg = statistics.mean(dateutil_times) |
134 | 137 | dateutil_min = min(dateutil_times) |
135 | 138 | dateutil_max = max(dateutil_times) |
136 | | - print(f" dateutil.parser.parse: avg={dateutil_avg:.3f}ms, min={dateutil_min:.3f}ms, max={dateutil_max:.3f}ms") |
| 139 | + print( |
| 140 | + f" dateutil.parser.parse: avg={dateutil_avg:.3f}ms, min={dateutil_min:.3f}ms, max={dateutil_max:.3f}ms" |
| 141 | + ) |
137 | 142 | else: |
138 | 143 | print(f" dateutil.parser.parse: Failed to parse") |
139 | | - |
| 144 | + |
140 | 145 | if ab_success: |
141 | 146 | ab_avg = statistics.mean(ab_times) |
142 | 147 | ab_min = min(ab_times) |
143 | 148 | ab_max = max(ab_times) |
144 | 149 | print(f" ab_datetime_parse: avg={ab_avg:.3f}ms, min={ab_min:.3f}ms, max={ab_max:.3f}ms") |
145 | 150 | else: |
146 | 151 | print(f" ab_datetime_parse: Failed to parse") |
147 | | - |
| 152 | + |
148 | 153 | # Compare performance if both succeeded |
149 | 154 | if whenever_success and dateutil_success: |
150 | 155 | speedup = dateutil_avg / whenever_avg |
151 | 156 | print(f" Performance: whenever is {speedup:.2f}x faster than dateutil") |
152 | | - |
| 157 | + |
153 | 158 | # Store results for summary |
154 | 159 | results[dt_str] = { |
155 | | - 'whenever': { |
156 | | - 'success': whenever_success, |
157 | | - 'method': whenever_method, |
158 | | - 'avg': statistics.mean(whenever_times) if whenever_success else None, |
| 160 | + "whenever": { |
| 161 | + "success": whenever_success, |
| 162 | + "method": whenever_method, |
| 163 | + "avg": statistics.mean(whenever_times) if whenever_success else None, |
| 164 | + }, |
| 165 | + "dateutil": { |
| 166 | + "success": dateutil_success, |
| 167 | + "avg": statistics.mean(dateutil_times) if dateutil_success else None, |
159 | 168 | }, |
160 | | - 'dateutil': { |
161 | | - 'success': dateutil_success, |
162 | | - 'avg': statistics.mean(dateutil_times) if dateutil_success else None, |
| 169 | + "ab_datetime_parse": { |
| 170 | + "success": ab_success, |
| 171 | + "avg": statistics.mean(ab_times) if ab_success else None, |
163 | 172 | }, |
164 | | - 'ab_datetime_parse': { |
165 | | - 'success': ab_success, |
166 | | - 'avg': statistics.mean(ab_times) if ab_success else None, |
167 | | - } |
168 | 173 | } |
169 | 174 |
|
170 | 175 | # Print summary |
|
174 | 179 | print("-" * 60) |
175 | 180 |
|
176 | 181 | for dt_str, result in results.items(): |
177 | | - whenever_avg = result['whenever']['avg'] |
178 | | - dateutil_avg = result['dateutil']['avg'] |
179 | | - ab_avg = result['ab_datetime_parse']['avg'] |
180 | | - |
| 182 | + whenever_avg = result["whenever"]["avg"] |
| 183 | + dateutil_avg = result["dateutil"]["avg"] |
| 184 | + ab_avg = result["ab_datetime_parse"]["avg"] |
| 185 | + |
181 | 186 | whenever_str = f"{whenever_avg:.3f}ms" if whenever_avg else "N/A" |
182 | 187 | dateutil_str = f"{dateutil_avg:.3f}ms" if dateutil_avg else "N/A" |
183 | 188 | ab_str = f"{ab_avg:.3f}ms" if ab_avg else "N/A" |
184 | | - |
| 189 | + |
185 | 190 | if whenever_avg and dateutil_avg: |
186 | 191 | speedup = dateutil_avg / whenever_avg |
187 | 192 | speedup_str = f"{speedup:.2f}x" |
188 | 193 | else: |
189 | 194 | speedup_str = "N/A" |
190 | | - |
| 195 | + |
191 | 196 | print(f"{dt_str} | {whenever_str} | {dateutil_str} | {ab_str} | {speedup_str}") |
0 commit comments