none-shall-parse 0.6.2__py3-none-any.whl → 0.6.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- none_shall_parse/__init__.py +66 -27
- none_shall_parse/dates.py +87 -60
- none_shall_parse/imeis.py +0 -1
- none_shall_parse/lists.py +0 -1
- {none_shall_parse-0.6.2.dist-info → none_shall_parse-0.6.3.dist-info}/METADATA +1 -1
- none_shall_parse-0.6.3.dist-info/RECORD +10 -0
- none_shall_parse-0.6.2.dist-info/RECORD +0 -10
- {none_shall_parse-0.6.2.dist-info → none_shall_parse-0.6.3.dist-info}/WHEEL +0 -0
none_shall_parse/__init__.py
CHANGED
|
@@ -10,28 +10,48 @@ translates to "none". Combined this with our parsing intentions
|
|
|
10
10
|
to create a name which nods to the Black Knight in Monty Python's Holy Grail.
|
|
11
11
|
https://www.youtube.com/watch?v=zKhEw7nD9C4
|
|
12
12
|
"""
|
|
13
|
+
|
|
13
14
|
from __future__ import annotations
|
|
14
15
|
from .dates import (
|
|
15
|
-
DateUtilsError,
|
|
16
|
+
DateUtilsError,
|
|
17
|
+
assert_week_start_date_is_valid,
|
|
16
18
|
assert_month_start_date_is_valid,
|
|
17
|
-
get_datetime_now,
|
|
19
|
+
get_datetime_now,
|
|
20
|
+
za_now,
|
|
21
|
+
utc_now,
|
|
18
22
|
za_ordinal_year_day_now,
|
|
19
|
-
za_ordinal_year_day_tomorrow,
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
za_ordinal_year_day_tomorrow,
|
|
24
|
+
utc_epoch_start,
|
|
25
|
+
now_offset_n_minutes,
|
|
26
|
+
now_offset_n_hours,
|
|
27
|
+
now_offset_n_days,
|
|
28
|
+
now_offset_n_months,
|
|
29
|
+
get_datetime_tomorrow,
|
|
22
30
|
get_datetime_yesterday,
|
|
23
31
|
get_utc_datetime_offset_n_days,
|
|
24
|
-
epoch_to_datetime,
|
|
32
|
+
epoch_to_datetime,
|
|
33
|
+
epoch_to_utc_datetime,
|
|
25
34
|
is_office_hours_in_timezone,
|
|
26
35
|
get_datetime_from_ordinal_and_sentinel,
|
|
27
|
-
day_span,
|
|
28
|
-
|
|
36
|
+
day_span,
|
|
37
|
+
week_span,
|
|
38
|
+
month_span,
|
|
39
|
+
arb_span,
|
|
40
|
+
calendar_month_start_end,
|
|
41
|
+
unix_timestamp,
|
|
29
42
|
sentinel_date_and_ordinal_to_date,
|
|
30
|
-
seconds_to_end_of_month,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
seconds_to_end_of_month,
|
|
44
|
+
standard_tz_timestring,
|
|
45
|
+
get_notice_end_date,
|
|
46
|
+
dt_to_za_time_string,
|
|
47
|
+
months_ago_selection,
|
|
48
|
+
is_aware,
|
|
49
|
+
make_aware,
|
|
50
|
+
unaware_to_utc_aware,
|
|
51
|
+
timer_decorator,
|
|
52
|
+
ZA_TZ,
|
|
53
|
+
UTC_TZ,
|
|
54
|
+
keys_for_span_func,
|
|
35
55
|
)
|
|
36
56
|
from .imeis import (
|
|
37
57
|
get_luhn_digit,
|
|
@@ -83,26 +103,45 @@ __email__ = "andries.niemandt@trintel.co.za, jan@trintel.co.za"
|
|
|
83
103
|
__license__ = "MIT"
|
|
84
104
|
|
|
85
105
|
__all__ = (
|
|
86
|
-
"DateUtilsError",
|
|
106
|
+
"DateUtilsError",
|
|
107
|
+
"assert_week_start_date_is_valid",
|
|
87
108
|
"assert_month_start_date_is_valid",
|
|
88
|
-
"get_datetime_now",
|
|
109
|
+
"get_datetime_now",
|
|
110
|
+
"za_now",
|
|
111
|
+
"utc_now",
|
|
89
112
|
"za_ordinal_year_day_now",
|
|
90
|
-
"za_ordinal_year_day_tomorrow",
|
|
91
|
-
"
|
|
92
|
-
"
|
|
113
|
+
"za_ordinal_year_day_tomorrow",
|
|
114
|
+
"utc_epoch_start",
|
|
115
|
+
"now_offset_n_minutes",
|
|
116
|
+
"now_offset_n_hours",
|
|
117
|
+
"now_offset_n_days",
|
|
118
|
+
"now_offset_n_months",
|
|
119
|
+
"get_datetime_tomorrow",
|
|
93
120
|
"get_datetime_yesterday",
|
|
94
121
|
"get_utc_datetime_offset_n_days",
|
|
95
|
-
"epoch_to_datetime",
|
|
122
|
+
"epoch_to_datetime",
|
|
123
|
+
"epoch_to_utc_datetime",
|
|
96
124
|
"is_office_hours_in_timezone",
|
|
97
125
|
"get_datetime_from_ordinal_and_sentinel",
|
|
98
|
-
"day_span",
|
|
99
|
-
"
|
|
126
|
+
"day_span",
|
|
127
|
+
"week_span",
|
|
128
|
+
"month_span",
|
|
129
|
+
"arb_span",
|
|
130
|
+
"calendar_month_start_end",
|
|
131
|
+
"unix_timestamp",
|
|
100
132
|
"sentinel_date_and_ordinal_to_date",
|
|
101
|
-
"seconds_to_end_of_month",
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
133
|
+
"seconds_to_end_of_month",
|
|
134
|
+
"standard_tz_timestring",
|
|
135
|
+
"get_notice_end_date",
|
|
136
|
+
"dt_to_za_time_string",
|
|
137
|
+
"months_ago_selection",
|
|
138
|
+
"is_aware",
|
|
139
|
+
"make_aware",
|
|
140
|
+
"unaware_to_utc_aware",
|
|
141
|
+
"timer_decorator",
|
|
142
|
+
"ZA_TZ",
|
|
143
|
+
"UTC_TZ",
|
|
144
|
+
"keys_for_span_func",
|
|
106
145
|
"get_luhn_digit",
|
|
107
146
|
"is_valid_luhn",
|
|
108
147
|
"is_valid_imei",
|
|
@@ -136,4 +175,4 @@ __all__ = (
|
|
|
136
175
|
"DateTimeLike",
|
|
137
176
|
"DateLike",
|
|
138
177
|
"DateTimeOrDateLike",
|
|
139
|
-
)
|
|
178
|
+
)
|
none_shall_parse/dates.py
CHANGED
|
@@ -10,8 +10,8 @@ from pendulum import DateTime, Date
|
|
|
10
10
|
from pendulum import local_timezone
|
|
11
11
|
from pendulum.tz.exceptions import InvalidTimezone
|
|
12
12
|
|
|
13
|
-
ZA_TZ =
|
|
14
|
-
UTC_TZ =
|
|
13
|
+
ZA_TZ = "Africa/Johannesburg"
|
|
14
|
+
UTC_TZ = "UTC"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class DateUtilsError(Exception):
|
|
@@ -48,13 +48,17 @@ def get_datetime_now(naive: bool = False, tz: str | None = None) -> DateTime:
|
|
|
48
48
|
naive (bool): If True, returns a naive DateTime object without timezone information.
|
|
49
49
|
Defaults to True.
|
|
50
50
|
tz (Optional[str]): The timezone to use if a timezone-aware DateTime is requested.
|
|
51
|
-
If not provided and naive is False,
|
|
52
|
-
is used.
|
|
51
|
+
If not provided and naive is False, defaults to ZA_TZ.
|
|
53
52
|
|
|
54
53
|
Returns:
|
|
55
54
|
pendulum.DateTime: The current date and time based on the specified parameters.
|
|
56
55
|
"""
|
|
57
|
-
|
|
56
|
+
if naive:
|
|
57
|
+
return pendulum.now().naive()
|
|
58
|
+
else:
|
|
59
|
+
# Default to ZA_TZ if no timezone is specified for aware datetime
|
|
60
|
+
timezone = tz if tz is not None else ZA_TZ
|
|
61
|
+
return pendulum.now(tz=timezone)
|
|
58
62
|
|
|
59
63
|
|
|
60
64
|
def za_now() -> DateTime:
|
|
@@ -120,8 +124,9 @@ def utc_epoch_start() -> DateTime:
|
|
|
120
124
|
return pendulum.from_timestamp(0)
|
|
121
125
|
|
|
122
126
|
|
|
123
|
-
def _now_offset_n_units(
|
|
124
|
-
|
|
127
|
+
def _now_offset_n_units(
|
|
128
|
+
n: int, units: str, naive: bool = False, tz: str | None = None
|
|
129
|
+
) -> DateTime:
|
|
125
130
|
"""
|
|
126
131
|
Calculate a DateTime object offset by a specified number of time units.
|
|
127
132
|
|
|
@@ -141,22 +146,24 @@ def _now_offset_n_units(n: int, units: str, naive: bool = False,
|
|
|
141
146
|
DateTime: The calculated DateTime object, optionally timezone-aware or naive.
|
|
142
147
|
"""
|
|
143
148
|
kwargs = {units: n}
|
|
144
|
-
return
|
|
145
|
-
**kwargs)
|
|
149
|
+
return (
|
|
150
|
+
pendulum.now().add(**kwargs).naive()
|
|
151
|
+
if naive
|
|
152
|
+
else pendulum.now(tz).add(**kwargs)
|
|
153
|
+
)
|
|
146
154
|
|
|
147
155
|
|
|
148
|
-
def now_offset_n_minutes(
|
|
149
|
-
|
|
156
|
+
def now_offset_n_minutes(
|
|
157
|
+
n: int, naive: bool = False, tz: str | None = None
|
|
158
|
+
) -> DateTime:
|
|
150
159
|
return _now_offset_n_units(n, units="minutes", naive=naive, tz=tz)
|
|
151
160
|
|
|
152
161
|
|
|
153
|
-
def now_offset_n_hours(n: int, naive: bool = False,
|
|
154
|
-
tz: str | None = None) -> DateTime:
|
|
162
|
+
def now_offset_n_hours(n: int, naive: bool = False, tz: str | None = None) -> DateTime:
|
|
155
163
|
return _now_offset_n_units(n, units="hours", naive=naive, tz=tz)
|
|
156
164
|
|
|
157
165
|
|
|
158
|
-
def now_offset_n_days(n: int, naive: bool = False,
|
|
159
|
-
tz: str | None = None) -> DateTime:
|
|
166
|
+
def now_offset_n_days(n: int, naive: bool = False, tz: str | None = None) -> DateTime:
|
|
160
167
|
return _now_offset_n_units(n, units="days", naive=naive, tz=tz)
|
|
161
168
|
|
|
162
169
|
|
|
@@ -164,14 +171,12 @@ def now_offset_n_months(n: int, naive: bool = False, tz: str | None = None) -> D
|
|
|
164
171
|
return _now_offset_n_units(n, units="months", naive=naive, tz=tz)
|
|
165
172
|
|
|
166
173
|
|
|
167
|
-
def get_datetime_tomorrow(naive: bool = False,
|
|
168
|
-
tz: str | None = None) -> DateTime:
|
|
174
|
+
def get_datetime_tomorrow(naive: bool = False, tz: str | None = None) -> DateTime:
|
|
169
175
|
"""Get tomorrow's DateTime"""
|
|
170
176
|
return now_offset_n_days(1, naive=naive, tz=tz)
|
|
171
177
|
|
|
172
178
|
|
|
173
|
-
def get_datetime_yesterday(naive: bool = False,
|
|
174
|
-
tz: str | None = None) -> DateTime:
|
|
179
|
+
def get_datetime_yesterday(naive: bool = False, tz: str | None = None) -> DateTime:
|
|
175
180
|
"""Get yesterday's DateTime"""
|
|
176
181
|
return now_offset_n_days(-1, naive=naive, tz=tz)
|
|
177
182
|
|
|
@@ -181,8 +186,9 @@ def get_utc_datetime_offset_n_days(n: int = 0) -> DateTime:
|
|
|
181
186
|
return pendulum.now(UTC_TZ).add(days=n)
|
|
182
187
|
|
|
183
188
|
|
|
184
|
-
def epoch_to_datetime(
|
|
185
|
-
|
|
189
|
+
def epoch_to_datetime(
|
|
190
|
+
epoch_int: int | float, naive: bool = False, tz: str | None = None
|
|
191
|
+
) -> DateTime:
|
|
186
192
|
"""
|
|
187
193
|
Converts an epoch timestamp to a DateTime object.
|
|
188
194
|
|
|
@@ -231,7 +237,9 @@ def epoch_to_utc_datetime(epoch_int: int | float | str) -> DateTime:
|
|
|
231
237
|
return pendulum.from_timestamp(int(epoch_int), tz=UTC_TZ)
|
|
232
238
|
|
|
233
239
|
|
|
234
|
-
def is_office_hours_in_timezone(
|
|
240
|
+
def is_office_hours_in_timezone(
|
|
241
|
+
epoch_int: int | float | str, tz: str | None = None
|
|
242
|
+
) -> bool:
|
|
235
243
|
"""
|
|
236
244
|
Determines if a given epoch timestamp falls within office hours for a
|
|
237
245
|
specific timezone.
|
|
@@ -263,7 +271,8 @@ def is_office_hours_in_timezone(epoch_int: int | float | str, tz: str | None = N
|
|
|
263
271
|
|
|
264
272
|
|
|
265
273
|
def get_datetime_from_ordinal_and_sentinel(
|
|
266
|
-
|
|
274
|
+
sentinel: DateTimeLike | None = None,
|
|
275
|
+
) -> Callable[[int], DateTime]:
|
|
267
276
|
"""
|
|
268
277
|
Given an ordinal year day, and a sentinel datetime, get the closest past
|
|
269
278
|
datetime to the sentinel that had the given ordinal year day.
|
|
@@ -324,8 +333,8 @@ def day_span(pts: DateTimeLike) -> Tuple[DateTime, DateTime]:
|
|
|
324
333
|
pdt = pendulum.instance(pts)
|
|
325
334
|
|
|
326
335
|
# Use Pendulum's clean API
|
|
327
|
-
begin = pdt.start_of(
|
|
328
|
-
end = pdt.add(days=1).start_of(
|
|
336
|
+
begin = pdt.start_of("day")
|
|
337
|
+
end = pdt.add(days=1).start_of("day")
|
|
329
338
|
|
|
330
339
|
if naive:
|
|
331
340
|
return begin.naive(), end.naive()
|
|
@@ -362,7 +371,7 @@ def week_span(wso: int) -> Callable[[DateTimeLike], Tuple[DateTime, DateTime]]:
|
|
|
362
371
|
current_weekday = pdt.weekday() + 1 # Pendulum uses 0-6, we want 1-7
|
|
363
372
|
days_back = (current_weekday - wso) % 7
|
|
364
373
|
|
|
365
|
-
begin = pdt.start_of(
|
|
374
|
+
begin = pdt.start_of("day").subtract(days=days_back)
|
|
366
375
|
end = begin.add(days=7)
|
|
367
376
|
|
|
368
377
|
if naive:
|
|
@@ -401,10 +410,10 @@ def month_span(mso: int) -> Callable[[DateTimeLike], Tuple[DateTime, DateTime]]:
|
|
|
401
410
|
|
|
402
411
|
if current_day >= mso:
|
|
403
412
|
# We're in the current month period
|
|
404
|
-
begin = pdt.start_of(
|
|
413
|
+
begin = pdt.start_of("day").replace(day=mso)
|
|
405
414
|
else:
|
|
406
415
|
# We're in the previous month period
|
|
407
|
-
begin = pdt.start_of(
|
|
416
|
+
begin = pdt.start_of("day").subtract(months=1).replace(day=mso)
|
|
408
417
|
|
|
409
418
|
# End is mso of next month from `begin`
|
|
410
419
|
end = begin.add(months=1)
|
|
@@ -417,8 +426,9 @@ def month_span(mso: int) -> Callable[[DateTimeLike], Tuple[DateTime, DateTime]]:
|
|
|
417
426
|
return find_dates
|
|
418
427
|
|
|
419
428
|
|
|
420
|
-
def arb_span(
|
|
421
|
-
[
|
|
429
|
+
def arb_span(
|
|
430
|
+
dates: Sequence[str | DateTimeOrDateLike], naive: bool = False
|
|
431
|
+
) -> Callable[[Any], Tuple[DateTime, DateTime]]:
|
|
422
432
|
"""
|
|
423
433
|
Parses two given dates and returns a callable function that provides the date range
|
|
424
434
|
as a tuple of datetime objects. The function ensures the date range is valid and
|
|
@@ -447,19 +457,25 @@ def arb_span(dates: Sequence[str | DateTimeOrDateLike], naive: bool = False) ->
|
|
|
447
457
|
parsed = pendulum.parse(date)
|
|
448
458
|
|
|
449
459
|
# If it's a date-only string (no time/timezone info), treat as naive
|
|
450
|
-
if
|
|
460
|
+
if "T" not in date and " " not in date:
|
|
451
461
|
parsed = parsed.naive()
|
|
452
462
|
|
|
453
|
-
parsed_dates.append(parsed.start_of(
|
|
463
|
+
parsed_dates.append(parsed.start_of("day"))
|
|
454
464
|
else:
|
|
455
465
|
# It's already a datetime-like object
|
|
456
466
|
if isinstance(date, datetime):
|
|
457
467
|
# datetime objects have tzinfo, hour, minute, etc.
|
|
458
468
|
if date.tzinfo is None:
|
|
459
469
|
# Input is naive, keep it naive using pendulum.naive()
|
|
460
|
-
parsed = pendulum.naive(
|
|
461
|
-
|
|
462
|
-
|
|
470
|
+
parsed = pendulum.naive(
|
|
471
|
+
date.year,
|
|
472
|
+
date.month,
|
|
473
|
+
date.day,
|
|
474
|
+
date.hour,
|
|
475
|
+
date.minute,
|
|
476
|
+
date.second,
|
|
477
|
+
date.microsecond,
|
|
478
|
+
)
|
|
463
479
|
else:
|
|
464
480
|
# Input is timezone-aware, preserve it
|
|
465
481
|
parsed = pendulum.instance(date)
|
|
@@ -467,7 +483,7 @@ def arb_span(dates: Sequence[str | DateTimeOrDateLike], naive: bool = False) ->
|
|
|
467
483
|
# date objects (no time component, no tzinfo) - treat as naive
|
|
468
484
|
parsed = pendulum.naive(date.year, date.month, date.day)
|
|
469
485
|
|
|
470
|
-
parsed_dates.append(parsed.start_of(
|
|
486
|
+
parsed_dates.append(parsed.start_of("day"))
|
|
471
487
|
|
|
472
488
|
a, b = parsed_dates
|
|
473
489
|
|
|
@@ -497,8 +513,8 @@ def arb_span(dates: Sequence[str | DateTimeOrDateLike], naive: bool = False) ->
|
|
|
497
513
|
|
|
498
514
|
|
|
499
515
|
def unroll_span_func(
|
|
500
|
-
|
|
501
|
-
|
|
516
|
+
f: Callable[[DateTimeLike], Tuple[DateTime, DateTime]],
|
|
517
|
+
cover: DateTimeLike | None = None,
|
|
502
518
|
) -> Tuple[List[DateTime], List[int], List[str], DateTime, DateTime]:
|
|
503
519
|
"""
|
|
504
520
|
Generate keys for a date range based on a provided function.
|
|
@@ -554,7 +570,7 @@ def unroll_span_func(
|
|
|
554
570
|
if dt <= current_date_sentinel and dt < end:
|
|
555
571
|
date_range.append(dt)
|
|
556
572
|
ord_days.append(dt.day_of_year)
|
|
557
|
-
iso_date_strings.append(dt.format(
|
|
573
|
+
iso_date_strings.append(dt.format("YYYY-MM-DD"))
|
|
558
574
|
|
|
559
575
|
return date_range, ord_days, iso_date_strings, start, end
|
|
560
576
|
|
|
@@ -563,10 +579,10 @@ def unroll_span_func(
|
|
|
563
579
|
|
|
564
580
|
|
|
565
581
|
def keys_for_span_func(
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
582
|
+
f: Callable[[DateTimeLike], Tuple[DateTime, DateTime]],
|
|
583
|
+
cover: DateTimeLike | None = None,
|
|
584
|
+
key_in_format: str = "ODIN_{}",
|
|
585
|
+
key_out_format: str = "ODOUT_{}",
|
|
570
586
|
):
|
|
571
587
|
"""
|
|
572
588
|
Generate keys for a date range based on a provided function.
|
|
@@ -586,14 +602,17 @@ def keys_for_span_func(
|
|
|
586
602
|
Raises:
|
|
587
603
|
DateUtilsError: If the date range cannot be processed.
|
|
588
604
|
"""
|
|
589
|
-
date_range, ord_days, iso_date_strings, start, end = unroll_span_func(
|
|
605
|
+
date_range, ord_days, iso_date_strings, start, end = unroll_span_func(
|
|
606
|
+
f=f, cover=cover
|
|
607
|
+
)
|
|
590
608
|
keys_in = [key_in_format.format(d) for d in ord_days]
|
|
591
609
|
keys_out = [key_out_format.format(d) for d in ord_days]
|
|
592
610
|
return keys_in, keys_out, start, end
|
|
593
611
|
|
|
594
612
|
|
|
595
|
-
def calendar_month_start_end(
|
|
596
|
-
|
|
613
|
+
def calendar_month_start_end(
|
|
614
|
+
date_in_month: DateTimeLike | None = None,
|
|
615
|
+
) -> Tuple[DateTime, DateTime]:
|
|
597
616
|
naive = date_in_month.tzinfo is None
|
|
598
617
|
|
|
599
618
|
if date_in_month is None:
|
|
@@ -601,7 +620,7 @@ def calendar_month_start_end(date_in_month: DateTimeLike | None = None) -> Tuple
|
|
|
601
620
|
|
|
602
621
|
pdt = pendulum.instance(date_in_month)
|
|
603
622
|
|
|
604
|
-
start = pdt.start_of(
|
|
623
|
+
start = pdt.start_of("month")
|
|
605
624
|
end = start.add(months=1)
|
|
606
625
|
|
|
607
626
|
if naive:
|
|
@@ -620,8 +639,9 @@ def unix_timestamp() -> int:
|
|
|
620
639
|
return round(time.time())
|
|
621
640
|
|
|
622
641
|
|
|
623
|
-
def sentinel_date_and_ordinal_to_date(
|
|
624
|
-
|
|
642
|
+
def sentinel_date_and_ordinal_to_date(
|
|
643
|
+
sentinel_date: DateTimeLike | date, ordinal: int | float | str
|
|
644
|
+
) -> date:
|
|
625
645
|
"""Convert sentinel date and ordinal day to actual date"""
|
|
626
646
|
year = sentinel_date.year
|
|
627
647
|
int_ordinal = int(ordinal)
|
|
@@ -638,7 +658,7 @@ def sentinel_date_and_ordinal_to_date(sentinel_date: DateTimeLike | date,
|
|
|
638
658
|
def seconds_to_end_of_month() -> int:
|
|
639
659
|
"""Calculate seconds remaining until the end of the current month"""
|
|
640
660
|
now = pendulum.now(UTC_TZ)
|
|
641
|
-
end_of_month = now.end_of(
|
|
661
|
+
end_of_month = now.end_of("month")
|
|
642
662
|
return int((end_of_month - now).total_seconds())
|
|
643
663
|
|
|
644
664
|
|
|
@@ -667,31 +687,37 @@ def get_notice_end_date(given_date: DateTimeLike | date | Date | None = None) ->
|
|
|
667
687
|
given_date = given_date.date()
|
|
668
688
|
elif not isinstance(given_date, date):
|
|
669
689
|
raise ValueError(
|
|
670
|
-
"Given date must be a datetime.date or datetime.datetime object"
|
|
690
|
+
"Given date must be a datetime.date or datetime.datetime object"
|
|
691
|
+
)
|
|
671
692
|
|
|
672
693
|
pdt = pendulum.instance(given_date)
|
|
673
694
|
if given_date.day <= 15:
|
|
674
695
|
# End of current month
|
|
675
|
-
end_date = pdt.add(months=1).start_of(
|
|
696
|
+
end_date = pdt.add(months=1).start_of("month")
|
|
676
697
|
else:
|
|
677
698
|
# End of next month
|
|
678
|
-
end_date = pdt.add(months=2).start_of(
|
|
699
|
+
end_date = pdt.add(months=2).start_of("month")
|
|
679
700
|
|
|
680
701
|
if isinstance(end_date, DateTime):
|
|
681
702
|
return end_date.date()
|
|
682
703
|
return end_date
|
|
683
704
|
|
|
684
705
|
|
|
685
|
-
def dt_to_za_time_string(v: DateTimeOrDateLike | None) -> str | None:
|
|
706
|
+
def dt_to_za_time_string(v: DateTimeOrDateLike | float | None) -> str | None:
|
|
686
707
|
"""Convert DateTime or Date to South Africa time string.
|
|
687
708
|
|
|
688
709
|
For datetime objects: returns "YYYY-MM-DD HH:MM:SS" in ZA timezone.
|
|
689
710
|
For date objects: returns "YYYY-MM-DD" (no time component).
|
|
690
711
|
For None: returns None.
|
|
712
|
+
For float values (NaN): returns None.
|
|
691
713
|
"""
|
|
692
714
|
if v is None:
|
|
693
715
|
return None
|
|
694
716
|
|
|
717
|
+
# Handle float values (NaN from pandas NULL database values)
|
|
718
|
+
if isinstance(v, float):
|
|
719
|
+
return None
|
|
720
|
+
|
|
695
721
|
# Handle date objects (no time component)
|
|
696
722
|
if isinstance(v, date) and not isinstance(v, datetime):
|
|
697
723
|
return v.strftime("%Y-%m-%d")
|
|
@@ -709,10 +735,7 @@ def months_ago_selection() -> List[Tuple[int, str]]:
|
|
|
709
735
|
"""Generate list of (index, "Month-Year") tuples for last 12 months"""
|
|
710
736
|
today = pendulum.today()
|
|
711
737
|
|
|
712
|
-
return [
|
|
713
|
-
(i, today.subtract(months=i).strftime("%B-%Y"))
|
|
714
|
-
for i in range(12)
|
|
715
|
-
]
|
|
738
|
+
return [(i, today.subtract(months=i).strftime("%B-%Y")) for i in range(12)]
|
|
716
739
|
|
|
717
740
|
|
|
718
741
|
def is_aware(dt: DateTimeLike | Date | date) -> bool:
|
|
@@ -720,7 +743,9 @@ def is_aware(dt: DateTimeLike | Date | date) -> bool:
|
|
|
720
743
|
return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
|
|
721
744
|
|
|
722
745
|
|
|
723
|
-
def make_aware(
|
|
746
|
+
def make_aware(
|
|
747
|
+
dt: DateTimeLike | date | Date | None, tz: str = None
|
|
748
|
+
) -> DateTime | Date | None:
|
|
724
749
|
"""
|
|
725
750
|
Convert a naive DateTime to a timezone-aware DateTime using Pendulum.
|
|
726
751
|
|
|
@@ -755,7 +780,9 @@ def make_aware(dt: DateTimeLike | date | Date | None, tz: str = None) -> DateTim
|
|
|
755
780
|
raise DateUtilsError(f"Invalid timezone: {tz}") from e
|
|
756
781
|
|
|
757
782
|
|
|
758
|
-
def unaware_to_utc_aware(
|
|
783
|
+
def unaware_to_utc_aware(
|
|
784
|
+
dt: DateTimeLike | date | Date | None,
|
|
785
|
+
) -> DateTime | Date | None:
|
|
759
786
|
"""Convert naive DateTime to UTC-aware DateTime using Pendulum."""
|
|
760
787
|
if not isinstance(dt, (datetime, type(None))):
|
|
761
788
|
raise TypeError(f"Expected datetime or None, got {type(dt)}")
|
none_shall_parse/imeis.py
CHANGED
none_shall_parse/lists.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: none-shall-parse
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Trinity Shared Python utilities.
|
|
5
5
|
Author: Andries Niemandt, Jan Badenhorst
|
|
6
6
|
Author-email: Andries Niemandt <andries.niemandt@trintel.co.za>, Jan Badenhorst <jan@trintel.co.za>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
none_shall_parse/__init__.py,sha256=VQrtFMNFZGpZAWhbSnVuJIhGamkb2WUzXOzleUQL274,4051
|
|
2
|
+
none_shall_parse/dates.py,sha256=rT_1gE5QeS9YxOQJET0qWesEZ5u2qS5ATN0SnM6LPXY,28459
|
|
3
|
+
none_shall_parse/imeis.py,sha256=1fFRdFryOAg2djbZjtDmJb8aRdKZ-vhqbJUVy-AFaEA,6769
|
|
4
|
+
none_shall_parse/lists.py,sha256=AZ5UbPYcLJQkLTP5X6mD72c2gcJ6helgfzBAVRWft2U,1746
|
|
5
|
+
none_shall_parse/parse.py,sha256=77bXZAtwFksRwuZ9Ax0lPxEjFpyjkQBqRa5mBc1WkF4,6843
|
|
6
|
+
none_shall_parse/strings.py,sha256=F7491CJAHJjL7vdEGwoH_4S6PjaovYUS_yzVGJ-bYIE,8463
|
|
7
|
+
none_shall_parse/types.py,sha256=WAgILMtW2_fm9MBpUuQvq68yXYBNd3rnSoQk70ibOd4,1320
|
|
8
|
+
none_shall_parse-0.6.3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
9
|
+
none_shall_parse-0.6.3.dist-info/METADATA,sha256=df4CPiPG9v1IURs8NVlUpqbIiWc0UPbW3R-lC97yDxY,1701
|
|
10
|
+
none_shall_parse-0.6.3.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
none_shall_parse/__init__.py,sha256=ekNMvJaRzR-SOv2bQAOuH86ImPHXGpIj2NbeCO45GOc,3897
|
|
2
|
-
none_shall_parse/dates.py,sha256=lbdfw6DCq5EvmUIHliZK8yMkouqDNvY0jNWMWrTt9Zk,28216
|
|
3
|
-
none_shall_parse/imeis.py,sha256=20pONoUhLKomZxAJegqSSjG72hZjYs60r8IcaRt-15M,6770
|
|
4
|
-
none_shall_parse/lists.py,sha256=DhbiElDBYTVss1ivDytCCwbUKpKpaCIy4orv8KOK-4M,1765
|
|
5
|
-
none_shall_parse/parse.py,sha256=77bXZAtwFksRwuZ9Ax0lPxEjFpyjkQBqRa5mBc1WkF4,6843
|
|
6
|
-
none_shall_parse/strings.py,sha256=F7491CJAHJjL7vdEGwoH_4S6PjaovYUS_yzVGJ-bYIE,8463
|
|
7
|
-
none_shall_parse/types.py,sha256=WAgILMtW2_fm9MBpUuQvq68yXYBNd3rnSoQk70ibOd4,1320
|
|
8
|
-
none_shall_parse-0.6.2.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
9
|
-
none_shall_parse-0.6.2.dist-info/METADATA,sha256=a9CfMCC9wVb8gw4NoI964u9y2S_FibwMOAzpwPqGreo,1701
|
|
10
|
-
none_shall_parse-0.6.2.dist-info/RECORD,,
|
|
File without changes
|