infdate 0.2.1__py3-none-any.whl → 0.2.2__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.
- infdate/__init__.py +22 -18
- {infdate-0.2.1.dist-info → infdate-0.2.2.dist-info}/METADATA +55 -37
- infdate-0.2.2.dist-info/RECORD +6 -0
- infdate-0.2.1.dist-info/RECORD +0 -6
- {infdate-0.2.1.dist-info → infdate-0.2.2.dist-info}/WHEEL +0 -0
- {infdate-0.2.1.dist-info → infdate-0.2.2.dist-info}/licenses/LICENSE +0 -0
infdate/__init__.py
CHANGED
@@ -6,7 +6,7 @@ capable of representing positive and negative infinity
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
from datetime import date, datetime
|
9
|
-
from math import inf
|
9
|
+
from math import inf, trunc
|
10
10
|
from typing import final, overload, Any, Final, TypeVar
|
11
11
|
|
12
12
|
|
@@ -28,6 +28,8 @@ ISO_DATETIME_FORMAT_UTC: Final[str] = f"{ISO_DATE_FORMAT}T%H:%M:%S.%fZ"
|
|
28
28
|
MIN_ORDINAL: Final[int] = date.min.toordinal()
|
29
29
|
MAX_ORDINAL: Final[int] = date.max.toordinal()
|
30
30
|
|
31
|
+
RESOLUTION: Final[int] = 1
|
32
|
+
|
31
33
|
|
32
34
|
# -----------------------------------------------------------------------------
|
33
35
|
# Internal module constants:
|
@@ -45,15 +47,15 @@ _GD = TypeVar("_GD", bound="GenericDate")
|
|
45
47
|
class GenericDate:
|
46
48
|
"""Base Date object derived from an ordinal"""
|
47
49
|
|
48
|
-
|
49
|
-
resolution: Final = 1
|
50
|
-
# pylint: enable=invalid-name
|
51
|
-
|
52
|
-
def __init__(self, ordinal: float, /) -> None:
|
50
|
+
def __init__(self, ordinal: int | float, /) -> None:
|
53
51
|
"""Create a date-like object"""
|
54
|
-
|
52
|
+
if ordinal in (-inf, inf):
|
53
|
+
self.__ordinal = ordinal
|
54
|
+
else:
|
55
|
+
self.__ordinal = trunc(ordinal)
|
56
|
+
#
|
55
57
|
|
56
|
-
def toordinal(self: _GD) -> float:
|
58
|
+
def toordinal(self: _GD) -> int | float:
|
57
59
|
"""to ordinal (almost like date.toordinal())"""
|
58
60
|
return self.__ordinal
|
59
61
|
|
@@ -109,24 +111,30 @@ class GenericDate:
|
|
109
111
|
return self
|
110
112
|
#
|
111
113
|
# Return a RealDate instance if possible
|
112
|
-
return fromordinal(self.__ordinal + delta)
|
114
|
+
return fromordinal(self.__ordinal + trunc(delta))
|
113
115
|
|
114
116
|
def __add__(self: _GD, delta: int | float, /) -> _GD:
|
115
117
|
"""gd_instance1 + number capability"""
|
116
118
|
return self._add_days(delta)
|
117
119
|
|
120
|
+
__radd__ = __add__
|
121
|
+
|
118
122
|
@overload
|
119
123
|
def __sub__(self: _GD, other: int | float, /) -> _GD: ...
|
120
124
|
@overload
|
121
|
-
def __sub__(self: _GD, other: _GD, /) -> int | float: ...
|
125
|
+
def __sub__(self: _GD, other: _GD | date, /) -> int | float: ...
|
122
126
|
@final
|
123
|
-
def __sub__(self: _GD, other: _GD | int | float, /) -> _GD | int | float:
|
127
|
+
def __sub__(self: _GD, other: _GD | date | int | float, /) -> _GD | int | float:
|
124
128
|
"""subtract other, respecting possibly nondeterministic values"""
|
125
129
|
if isinstance(other, (int, float)):
|
126
130
|
return self._add_days(-other)
|
127
131
|
#
|
128
132
|
return self.__ordinal - other.toordinal()
|
129
133
|
|
134
|
+
def __rsub__(self: _GD, other: _GD | date, /) -> int | float:
|
135
|
+
"""subtract from other, respecting possibly nondeterministic values"""
|
136
|
+
return other.toordinal() - self.__ordinal
|
137
|
+
|
130
138
|
def __repr__(self: _GD, /) -> str:
|
131
139
|
"""String representation of the object"""
|
132
140
|
return f"{self.__class__.__name__}({repr(self.__ordinal)})"
|
@@ -186,7 +194,7 @@ class RealDate(GenericDate):
|
|
186
194
|
self.year = year
|
187
195
|
self.month = month
|
188
196
|
self.day = day
|
189
|
-
super().__init__(
|
197
|
+
super().__init__(self._wrapped_date_object.toordinal())
|
190
198
|
self.timetuple = self._wrapped_date_object.timetuple
|
191
199
|
self.weekday = self._wrapped_date_object.weekday
|
192
200
|
self.isoweekday = self._wrapped_date_object.isoweekday
|
@@ -274,7 +282,7 @@ def fromtimestamp(timestamp: float) -> GenericDate:
|
|
274
282
|
return from_datetime_object(stdlib_date_object)
|
275
283
|
|
276
284
|
|
277
|
-
def fromordinal(ordinal:
|
285
|
+
def fromordinal(ordinal: int | float) -> GenericDate:
|
278
286
|
"""Create an InfinityDate or RealDate instance from the provided ordinal"""
|
279
287
|
if ordinal == -inf:
|
280
288
|
return MIN
|
@@ -282,11 +290,7 @@ def fromordinal(ordinal: float | int) -> GenericDate:
|
|
282
290
|
if ordinal == inf:
|
283
291
|
return MAX
|
284
292
|
#
|
285
|
-
|
286
|
-
new_ordinal = int(ordinal)
|
287
|
-
except ValueError as error:
|
288
|
-
raise ValueError(f"Cannot convert {ordinal!r} to integer") from error
|
289
|
-
#
|
293
|
+
new_ordinal = trunc(ordinal)
|
290
294
|
if not MIN_ORDINAL <= new_ordinal <= MAX_ORDINAL:
|
291
295
|
raise OverflowError("RealDate value out of range")
|
292
296
|
#
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: infdate
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.2
|
4
4
|
Summary: Date object wrapper supporting infinity
|
5
5
|
Project-URL: Homepage, https://gitlab.com/blackstream-x/infdate
|
6
6
|
Project-URL: CI, https://gitlab.com/blackstream-x/infdate/-/pipelines
|
@@ -14,12 +14,13 @@ Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
16
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
17
18
|
Classifier: Programming Language :: Python :: 3.11
|
18
19
|
Classifier: Programming Language :: Python :: 3.12
|
19
20
|
Classifier: Programming Language :: Python :: 3.13
|
20
21
|
Classifier: Programming Language :: Python :: 3.14
|
21
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
22
|
-
Requires-Python: >=3.
|
23
|
+
Requires-Python: >=3.10
|
23
24
|
Description-Content-Type: text/markdown
|
24
25
|
|
25
26
|
# infdate
|
@@ -35,14 +36,18 @@ _Python module for date calculations implementing a concept of infinity_
|
|
35
36
|
└── RealDate
|
36
37
|
|
37
38
|
|
38
|
-
The base class **GenericDate** should not be instantiated
|
39
|
+
The base class **GenericDate** should not be instantiated
|
40
|
+
but can be used as a type annotation. In fact, it should be preferred
|
41
|
+
over the other classes in typing annotations.
|
39
42
|
|
40
43
|
**InfinityDate** can represent either positive or negative infinity.
|
41
44
|
The module-level constants **MIN** and **MAX** contain the two possible
|
42
45
|
**InfinityDate** instance variations.
|
43
46
|
|
44
47
|
**RealDate** instances represent real dates like the standard library’s
|
45
|
-
**datetime.date** class, with mostly equal or similar semantics.
|
48
|
+
**[datetime.date]** class, with mostly equal or similar semantics.
|
49
|
+
The module -level constants **REAL_MIN** and **REAL_MAX** are the eqivalents
|
50
|
+
of **datetime.date.min** and **datetime.date.max** as **RealDate** instances.
|
46
51
|
|
47
52
|
For any valid **RealDate** instance, the following is **True**:
|
48
53
|
|
@@ -50,9 +55,21 @@ For any valid **RealDate** instance, the following is **True**:
|
|
50
55
|
infdate.MIN < infdate.REAL_MIN <= real_date_instance <= infdate.REAL_MAX < infdate.MAX
|
51
56
|
```
|
52
57
|
|
53
|
-
### Module-level
|
58
|
+
### Module-level constants
|
59
|
+
|
60
|
+
* **MIN** = **InfinityDate(**_past_bound_=`True`**)** → the beginning of time
|
61
|
+
* **MAX** = **InfinityDate(**_past_bound_=`False`**)** → the end of time
|
62
|
+
* **MIN_ORDINAL** = `1` → same as **datetime.date.min.toordinal()**
|
63
|
+
* **MAX_ORDINAL** = `3652059` → same as **datetime.date.max.toordinal()**
|
64
|
+
* **REAL_MIN** = **fromordinal(MIN_ORDINAL)**
|
65
|
+
→ represents _0001-01-01_, the same date as **datetime.date.min**
|
66
|
+
* **REAL_MAX** = **fromordinal(MAX_ORDINAL)**
|
67
|
+
→ represents _9999-12-31_, the same date as **datetime.date.max**
|
68
|
+
* **RESOLUTION** = `1` → represents the lowest possible date difference: _one day_
|
54
69
|
|
55
70
|
|
71
|
+
### Module-level factory functions
|
72
|
+
|
56
73
|
The following factory methods from the **datetime.date** class
|
57
74
|
are provided as module-level functions:
|
58
75
|
|
@@ -78,15 +95,17 @@ Two additional factory functions are provided:
|
|
78
95
|
|
79
96
|
Some notable difference from the **datetime.date** class, mainly due to the design decision to express date differences in pure numbers (ie. **float** because **math.inf** also is a float):
|
80
97
|
|
81
|
-
*
|
98
|
+
* infdate classes have no **max**, **min** or **resolution** attributes,
|
99
|
+
but there are [module-level constants] serving the same purpose.
|
82
100
|
|
83
|
-
* The **
|
84
|
-
but also represents exactly one day.
|
101
|
+
* The **.toordinal()** method returns **int**, **math.inf**, or **-math.inf**.
|
85
102
|
|
86
|
-
* Subtracting a date from an **InfinityDate** or **RealDate** always returns
|
103
|
+
* Subtracting a date from an **InfinityDate** or **RealDate** always returns
|
104
|
+
an **int**, **math.inf**, or **-math.inf** instead of a **datetime.timedelta** instance.
|
87
105
|
|
88
106
|
* Likewise, you cannot add or subtract **datetime.timedelta** instances
|
89
|
-
from an **InfinityDate** or **RealDate**, only **float** or **int**
|
107
|
+
from an **InfinityDate** or **RealDate**, only **float** or **int**
|
108
|
+
(support for adding and subtracting datetime.timedelta instances might be added in the future, [see the feature request]).
|
90
109
|
|
91
110
|
|
92
111
|
## Example usage
|
@@ -95,22 +114,20 @@ Some notable difference from the **datetime.date** class, mainly due to the desi
|
|
95
114
|
>>> import infdate
|
96
115
|
>>> today = infdate.today()
|
97
116
|
>>> today
|
98
|
-
RealDate(2025, 6,
|
99
|
-
>>> print(today)
|
100
|
-
|
101
|
-
>>> print(f"US date notation {today:%m/%d/%y}")
|
102
|
-
US date notation 06/25/25
|
117
|
+
RealDate(2025, 6, 27)
|
118
|
+
>>> print(f"US date notation: {today:%m/%d/%y}")
|
119
|
+
US date notation: 06/27/25
|
103
120
|
>>> today.ctime()
|
104
|
-
'
|
121
|
+
'Fri Jun 27 00:00:00 2025'
|
105
122
|
>>> today.isocalendar()
|
106
|
-
datetime.IsoCalendarDate(year=2025, week=26, weekday=
|
123
|
+
datetime.IsoCalendarDate(year=2025, week=26, weekday=5)
|
107
124
|
>>>
|
108
125
|
>>> yesterday = today - 1
|
109
126
|
>>> yesterday.ctime()
|
110
|
-
'
|
127
|
+
'Thu Jun 26 00:00:00 2025'
|
111
128
|
>>>
|
112
129
|
>>> today - yesterday
|
113
|
-
1
|
130
|
+
1
|
114
131
|
>>> infdate.MIN
|
115
132
|
InfinityDate(past_bound=True)
|
116
133
|
>>> infdate.MAX
|
@@ -123,25 +140,26 @@ inf
|
|
123
140
|
|
124
141
|
**InfinityDate** and **RealDate** instances can be compared with each other, and also with **datetime.date** instances.
|
125
142
|
|
126
|
-
Subtracting
|
127
|
-
currently still raises a **TypeError**):
|
128
|
-
|
129
|
-
>>> from datetime import date
|
130
|
-
>>> stdlib_today = date.today()
|
131
|
-
>>> today == stdlib_today
|
132
|
-
True
|
133
|
-
>>> yesterday < stdlib_today
|
134
|
-
True
|
135
|
-
>>> yesterday - stdlib_today
|
136
|
-
-1.0
|
137
|
-
>>> stdlib_today - yesterday
|
138
|
-
Traceback (most recent call last):
|
139
|
-
File "<python-input-22>", line 1, in <module>
|
140
|
-
stdlib_today - yesterday
|
141
|
-
~~~~~~~~~~~~~^~~~~~~~~~~
|
142
|
-
TypeError: unsupported operand type(s) for -: 'datetime.date' and 'RealDate'
|
143
|
+
Subtracting **InfinityDate** or **RealDate** and **datetime.date** instances from each other also works:
|
143
144
|
|
145
|
+
``` pycon
|
146
|
+
>>> from datetime import date
|
147
|
+
>>> stdlib_today = date.today()
|
148
|
+
>>> today == stdlib_today
|
149
|
+
True
|
150
|
+
>>> yesterday < stdlib_today
|
151
|
+
True
|
152
|
+
>>> yesterday - stdlib_today
|
153
|
+
-1
|
154
|
+
>>> stdlib_today - yesterday
|
155
|
+
1
|
156
|
+
>>> stdlib_today - infdate.MIN
|
157
|
+
inf
|
158
|
+
```
|
144
159
|
|
145
160
|
|
146
161
|
* * *
|
147
|
-
[
|
162
|
+
[datetime.date]: https://docs.python.org/3/library/datetime.html#date-objects
|
163
|
+
[Personal Access Tokens API]: https://docs.gitlab.com/api/personal_access_tokens/
|
164
|
+
[module-level constants]: #module-level-constants
|
165
|
+
[see the feature request]: https://gitlab.com/blackstream-x/infdate/-/issues/6
|
@@ -0,0 +1,6 @@
|
|
1
|
+
infdate/__init__.py,sha256=5hjONWaY3mtCaXUQz2YN3qm6R84p6eV5bjTwW8JcvFU,11360
|
2
|
+
infdate/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
infdate-0.2.2.dist-info/METADATA,sha256=LBnq5cSaqv4eIom0ZC0NTMV0jNiAziuRyLqEY-Rhvls,5904
|
4
|
+
infdate-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
5
|
+
infdate-0.2.2.dist-info/licenses/LICENSE,sha256=867pxriiObx28vCU1JsRtu3H9kUKyl54e0-xl1IIv3Y,913
|
6
|
+
infdate-0.2.2.dist-info/RECORD,,
|
infdate-0.2.1.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
infdate/__init__.py,sha256=n3WfJnHma_FGelLrkOjBt4gj49P-_rlsqc0FTLmUxCI,11195
|
2
|
-
infdate/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
infdate-0.2.1.dist-info/METADATA,sha256=V85LBfMkyB66rZJI4ceghR0KL-x0b2RQoOo-0_Sr77s,5205
|
4
|
-
infdate-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
5
|
-
infdate-0.2.1.dist-info/licenses/LICENSE,sha256=867pxriiObx28vCU1JsRtu3H9kUKyl54e0-xl1IIv3Y,913
|
6
|
-
infdate-0.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|