dycw-utilities 0.113.2__py3-none-any.whl → 0.113.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.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.113.2
3
+ Version: 0.113.3
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
7
7
  Requires-Dist: typing-extensions<4.14,>=4.13.1
8
8
  Provides-Extra: test
9
- Requires-Dist: hypothesis<6.132,>=6.131.10; extra == 'test'
9
+ Requires-Dist: hypothesis<6.132,>=6.131.14; extra == 'test'
10
10
  Requires-Dist: pytest-asyncio<0.27,>=0.26.0; extra == 'test'
11
11
  Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
12
12
  Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
@@ -80,7 +80,7 @@ Provides-Extra: zzz-test-hypothesis
80
80
  Requires-Dist: aiosqlite<0.22,>=0.21.0; extra == 'zzz-test-hypothesis'
81
81
  Requires-Dist: asyncpg<0.31,>=0.30.0; extra == 'zzz-test-hypothesis'
82
82
  Requires-Dist: greenlet<3.3,>=3.2.0; extra == 'zzz-test-hypothesis'
83
- Requires-Dist: hypothesis<6.132,>=6.131.10; extra == 'zzz-test-hypothesis'
83
+ Requires-Dist: hypothesis<6.132,>=6.131.14; extra == 'zzz-test-hypothesis'
84
84
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-hypothesis'
85
85
  Requires-Dist: numpy<2.3,>=2.2.5; extra == 'zzz-test-hypothesis'
86
86
  Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=gPP9gUduOhrk3TNScyIM28aOLTbYa1tnDF9Vu80gxZU,60
1
+ utilities/__init__.py,sha256=eOSRaK1eqblBSlkDTnJ-3-xWW49Ql-RkfxbwvIfUGyQ,60
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
3
  utilities/astor.py,sha256=xuDUkjq0-b6fhtwjhbnebzbqQZAjMSHR1IIS5uOodVg,777
4
4
  utilities/asyncio.py,sha256=6Pv0xK2jP55iF6EYjY1g6_rFGOeJyihxRNxXnOsWv14,15691
@@ -12,7 +12,7 @@ utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
12
12
  utilities/cryptography.py,sha256=HyOewI20cl3uRXsKivhIaeLVDInQdzgXZGaly7hS5dE,771
13
13
  utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
14
14
  utilities/dataclasses.py,sha256=iiC1wpGXWhaocIikzwBt8bbLWyImoUlOlcDZJGejaIg,33011
15
- utilities/datetime.py,sha256=OF7jZE702UecnwAbq9D3N-GINpp9gSGoidki1RhimCE,35752
15
+ utilities/datetime.py,sha256=PcN-4_sSPX1zbpdzBQRdo08pubCuGHyigxkV6SUnvlo,38733
16
16
  utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
17
17
  utilities/errors.py,sha256=BtSNP0JC3ik536ddPyTerLomCRJV9f6kdMe6POz0QHM,361
18
18
  utilities/eventkit.py,sha256=6M5Xu1SzN-juk9PqBHwy5dS-ta7T0qA6SMpDsakOJ0E,13039
@@ -87,7 +87,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
87
87
  utilities/whenever.py,sha256=iLRP_-8CZtBpHKbGZGu-kjSMg1ZubJ-VSmgSy7Eudxw,17787
88
88
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
89
89
  utilities/zoneinfo.py,sha256=-Xm57PMMwDTYpxJdkiJG13wnbwK--I7XItBh5WVhD-o,1874
90
- dycw_utilities-0.113.2.dist-info/METADATA,sha256=QgO7W2WhDgyW3k4-rB8FUJXk-vqpi7MfZfthSYdAD2o,12943
91
- dycw_utilities-0.113.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.113.2.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.113.2.dist-info/RECORD,,
90
+ dycw_utilities-0.113.3.dist-info/METADATA,sha256=FekxkXInaGKIntNnsuUBc8VxfVEcuyBaoxzXdhhdxII,12943
91
+ dycw_utilities-0.113.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.113.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.113.3.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.113.2"
3
+ __version__ = "0.113.3"
utilities/datetime.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
- from collections.abc import Callable, Iterable
4
+ from collections.abc import Callable, Iterable, Sequence
5
5
  from dataclasses import dataclass, replace
6
6
  from re import search, sub
7
7
  from statistics import fmean
@@ -495,6 +495,101 @@ def get_half_years(*, n: int = 1) -> dt.timedelta:
495
495
 
496
496
  HALF_YEAR = get_half_years(n=1)
497
497
 
498
+ ##
499
+
500
+
501
+ def get_min_max_date(
502
+ *,
503
+ min_date: dt.date | None = None,
504
+ max_date: dt.date | None = None,
505
+ min_age: Duration | None = None,
506
+ max_age: Duration | None = None,
507
+ time_zone: TimeZoneLike = UTC,
508
+ ) -> tuple[dt.date | None, dt.date | None]:
509
+ """Get the min/max date given a combination of dates/ages."""
510
+ today = get_today(time_zone=time_zone)
511
+ min_parts: Sequence[dt.date] = []
512
+ if min_date is not None:
513
+ if min_date > today:
514
+ raise _GetMinMaxDateMinDateError(min_date=min_date, today=today)
515
+ min_parts.append(min_date)
516
+ if max_age is not None:
517
+ if date_duration_to_timedelta(max_age) < ZERO_TIME:
518
+ raise _GetMinMaxDateMaxAgeError(max_age=max_age)
519
+ min_parts.append(sub_duration(today, duration=max_age))
520
+ min_date_use = max(min_parts, default=None)
521
+ max_parts: Sequence[dt.date] = []
522
+ if max_date is not None:
523
+ if max_date > today:
524
+ raise _GetMinMaxDateMaxDateError(max_date=max_date, today=today)
525
+ max_parts.append(max_date)
526
+ if min_age is not None:
527
+ if date_duration_to_timedelta(min_age) < ZERO_TIME:
528
+ raise _GetMinMaxDateMinAgeError(min_age=min_age)
529
+ max_parts.append(sub_duration(today, duration=min_age))
530
+ max_date_use = min(max_parts, default=None)
531
+ if (
532
+ (min_date_use is not None)
533
+ and (max_date_use is not None)
534
+ and (min_date_use > max_date_use)
535
+ ):
536
+ raise _GetMinMaxDatePeriodError(min_date=min_date_use, max_date=max_date_use)
537
+ return min_date_use, max_date_use
538
+
539
+
540
+ @dataclass(kw_only=True, slots=True)
541
+ class GetMinMaxDateError(Exception): ...
542
+
543
+
544
+ @dataclass(kw_only=True, slots=True)
545
+ class _GetMinMaxDateMinDateError(GetMinMaxDateError):
546
+ min_date: dt.date
547
+ today: dt.date
548
+
549
+ @override
550
+ def __str__(self) -> str:
551
+ return f"Min date must be at most today; got {self.min_date} > {self.today}"
552
+
553
+
554
+ @dataclass(kw_only=True, slots=True)
555
+ class _GetMinMaxDateMinAgeError(GetMinMaxDateError):
556
+ min_age: Duration
557
+
558
+ @override
559
+ def __str__(self) -> str:
560
+ return f"Min age must be non-negative; got {self.min_age}"
561
+
562
+
563
+ @dataclass(kw_only=True, slots=True)
564
+ class _GetMinMaxDateMaxDateError(GetMinMaxDateError):
565
+ max_date: dt.date
566
+ today: dt.date
567
+
568
+ @override
569
+ def __str__(self) -> str:
570
+ return f"Max date must be at most today; got {self.max_date} > {self.today}"
571
+
572
+
573
+ @dataclass(kw_only=True, slots=True)
574
+ class _GetMinMaxDateMaxAgeError(GetMinMaxDateError):
575
+ max_age: Duration
576
+
577
+ @override
578
+ def __str__(self) -> str:
579
+ return f"Max age must be non-negative; got {self.max_age}"
580
+
581
+
582
+ @dataclass(kw_only=True, slots=True)
583
+ class _GetMinMaxDatePeriodError(GetMinMaxDateError):
584
+ min_date: dt.date
585
+ max_date: dt.date
586
+
587
+ @override
588
+ def __str__(self) -> str:
589
+ return (
590
+ f"Min date must be at most max date; got {self.min_date} > {self.max_date}"
591
+ )
592
+
498
593
 
499
594
  ##
500
595
 
@@ -1244,6 +1339,7 @@ __all__ = [
1244
1339
  "CheckDateNotDateTimeError",
1245
1340
  "DateOrMonth",
1246
1341
  "EnsureMonthError",
1342
+ "GetMinMaxDateError",
1247
1343
  "MeanDateTimeError",
1248
1344
  "MeanTimeDeltaError",
1249
1345
  "MillisecondsSinceEpochError",
@@ -1280,6 +1376,7 @@ __all__ = [
1280
1376
  "get_date",
1281
1377
  "get_datetime",
1282
1378
  "get_half_years",
1379
+ "get_min_max_date",
1283
1380
  "get_months",
1284
1381
  "get_now",
1285
1382
  "get_quarters",