infdate 0.2.1__py3-none-any.whl → 0.2.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.
- infdate/__init__.py +173 -82
- infdate-0.2.3.dist-info/METADATA +170 -0
- infdate-0.2.3.dist-info/RECORD +6 -0
- infdate-0.2.1.dist-info/METADATA +0 -147
- infdate-0.2.1.dist-info/RECORD +0 -6
- {infdate-0.2.1.dist-info → infdate-0.2.3.dist-info}/WHEEL +0 -0
- {infdate-0.2.1.dist-info → infdate-0.2.3.dist-info}/licenses/LICENSE +0 -0
infdate/__init__.py
CHANGED
@@ -1,40 +1,37 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
"""
|
4
|
-
infdate: a wrapper around standard library’s datetime.date objects,
|
5
|
-
capable of representing
|
4
|
+
infdate: a wrapper around the standard library’s datetime.date objects,
|
5
|
+
capable of representing past and future infinity
|
6
6
|
"""
|
7
7
|
|
8
|
+
import warnings
|
9
|
+
|
8
10
|
from datetime import date, datetime
|
9
|
-
from math import inf
|
11
|
+
from math import inf, trunc
|
10
12
|
from typing import final, overload, Any, Final, TypeVar
|
11
13
|
|
12
14
|
|
13
15
|
# -----------------------------------------------------------------------------
|
14
|
-
#
|
15
|
-
#
|
16
|
+
# Internal module constants:
|
17
|
+
# TypeVar for GenericDate
|
16
18
|
# -----------------------------------------------------------------------------
|
17
19
|
|
18
|
-
|
19
|
-
NEGATIVE_INFINITE_DATE_DISPLAY: Final[str] = "<-inf>"
|
20
|
-
|
21
|
-
INFINITY_SYMBOL: Final[str] = "∝"
|
22
|
-
UP_TO_SYMBOL: Final[str] = "⤒"
|
23
|
-
FROM_ON_SYMBOL: Final[str] = "↥"
|
24
|
-
|
25
|
-
ISO_DATE_FORMAT: Final[str] = "%Y-%m-%d"
|
26
|
-
ISO_DATETIME_FORMAT_UTC: Final[str] = f"{ISO_DATE_FORMAT}T%H:%M:%S.%fZ"
|
20
|
+
_GD = TypeVar("_GD", bound="GenericDate")
|
27
21
|
|
28
|
-
|
29
|
-
MAX_ORDINAL: Final[int] = date.max.toordinal()
|
22
|
+
_INFINITY_FORMS = (-inf, inf)
|
30
23
|
|
31
24
|
|
32
25
|
# -----------------------------------------------------------------------------
|
33
|
-
#
|
34
|
-
#
|
26
|
+
# Public module constants:
|
27
|
+
# format strings
|
35
28
|
# -----------------------------------------------------------------------------
|
36
29
|
|
37
|
-
|
30
|
+
INFINITE_PAST_DATE_DISPLAY: Final[str] = "<-inf>"
|
31
|
+
INFINITE_FUTURE_DATE_DISPLAY: Final[str] = "<inf>"
|
32
|
+
|
33
|
+
ISO_DATE_FORMAT: Final[str] = "%Y-%m-%d"
|
34
|
+
ISO_DATETIME_FORMAT_UTC: Final[str] = f"{ISO_DATE_FORMAT}T%H:%M:%S.%fZ"
|
38
35
|
|
39
36
|
|
40
37
|
# -----------------------------------------------------------------------------
|
@@ -45,15 +42,15 @@ _GD = TypeVar("_GD", bound="GenericDate")
|
|
45
42
|
class GenericDate:
|
46
43
|
"""Base Date object derived from an ordinal"""
|
47
44
|
|
48
|
-
|
49
|
-
resolution: Final = 1
|
50
|
-
# pylint: enable=invalid-name
|
51
|
-
|
52
|
-
def __init__(self, ordinal: float, /) -> None:
|
45
|
+
def __init__(self, ordinal: int | float, /) -> None:
|
53
46
|
"""Create a date-like object"""
|
54
|
-
|
47
|
+
if ordinal in _INFINITY_FORMS:
|
48
|
+
self.__ordinal = ordinal
|
49
|
+
else:
|
50
|
+
self.__ordinal = trunc(ordinal)
|
51
|
+
#
|
55
52
|
|
56
|
-
def toordinal(self: _GD) -> float:
|
53
|
+
def toordinal(self: _GD) -> int | float:
|
57
54
|
"""to ordinal (almost like date.toordinal())"""
|
58
55
|
return self.__ordinal
|
59
56
|
|
@@ -90,12 +87,14 @@ class GenericDate:
|
|
90
87
|
return hash(f"date with ordinal {self.__ordinal}")
|
91
88
|
|
92
89
|
def _add_days(self: _GD, delta: int | float, /):
|
93
|
-
"""Add other, respecting maybe-nondeterministic values
|
90
|
+
"""Add other, respecting maybe-nondeterministic values
|
91
|
+
(ie. -inf or inf)
|
92
|
+
"""
|
94
93
|
# Check for infinity in either self or delta,
|
95
94
|
# and return a matching InfinityDate if found.
|
96
95
|
# Re-use existing objects if possible.
|
97
96
|
for observed_item in (delta, self.__ordinal):
|
98
|
-
for infinity_form in
|
97
|
+
for infinity_form in _INFINITY_FORMS:
|
99
98
|
if observed_item == infinity_form:
|
100
99
|
if observed_item == self.__ordinal:
|
101
100
|
return self
|
@@ -109,24 +108,30 @@ class GenericDate:
|
|
109
108
|
return self
|
110
109
|
#
|
111
110
|
# Return a RealDate instance if possible
|
112
|
-
return fromordinal(self.__ordinal + delta)
|
111
|
+
return fromordinal(self.__ordinal + trunc(delta))
|
113
112
|
|
114
113
|
def __add__(self: _GD, delta: int | float, /) -> _GD:
|
115
114
|
"""gd_instance1 + number capability"""
|
116
115
|
return self._add_days(delta)
|
117
116
|
|
117
|
+
__radd__ = __add__
|
118
|
+
|
118
119
|
@overload
|
119
120
|
def __sub__(self: _GD, other: int | float, /) -> _GD: ...
|
120
121
|
@overload
|
121
|
-
def __sub__(self: _GD, other: _GD, /) -> int | float: ...
|
122
|
+
def __sub__(self: _GD, other: _GD | date, /) -> int | float: ...
|
122
123
|
@final
|
123
|
-
def __sub__(self: _GD, other: _GD | int | float, /) -> _GD | int | float:
|
124
|
+
def __sub__(self: _GD, other: _GD | date | int | float, /) -> _GD | int | float:
|
124
125
|
"""subtract other, respecting possibly nondeterministic values"""
|
125
126
|
if isinstance(other, (int, float)):
|
126
127
|
return self._add_days(-other)
|
127
128
|
#
|
128
129
|
return self.__ordinal - other.toordinal()
|
129
130
|
|
131
|
+
def __rsub__(self: _GD, other: _GD | date, /) -> int | float:
|
132
|
+
"""subtract from other, respecting possibly nondeterministic values"""
|
133
|
+
return other.toordinal() - self.__ordinal
|
134
|
+
|
130
135
|
def __repr__(self: _GD, /) -> str:
|
131
136
|
"""String representation of the object"""
|
132
137
|
return f"{self.__class__.__name__}({repr(self.__ordinal)})"
|
@@ -147,6 +152,29 @@ class GenericDate:
|
|
147
152
|
"""Return a copy with year, month, and/or date replaced"""
|
148
153
|
raise NotImplementedError
|
149
154
|
|
155
|
+
@overload
|
156
|
+
def pretty(
|
157
|
+
self: _GD,
|
158
|
+
/,
|
159
|
+
*,
|
160
|
+
inf_common_prefix: str,
|
161
|
+
inf_past_suffix: str,
|
162
|
+
inf_future_suffix: str,
|
163
|
+
) -> str: ...
|
164
|
+
@overload
|
165
|
+
def pretty(self: _GD, /, *, fmt: str) -> str: ...
|
166
|
+
def pretty(
|
167
|
+
self: _GD,
|
168
|
+
/,
|
169
|
+
*,
|
170
|
+
inf_common_prefix: str = "",
|
171
|
+
inf_past_suffix: str = "",
|
172
|
+
inf_future_suffix: str = "",
|
173
|
+
fmt: str = ISO_DATE_FORMAT,
|
174
|
+
) -> str:
|
175
|
+
"""Return the date, pretty printed"""
|
176
|
+
raise NotImplementedError
|
177
|
+
|
150
178
|
|
151
179
|
class InfinityDate(GenericDate):
|
152
180
|
"""Infinity Date object"""
|
@@ -154,6 +182,7 @@ class InfinityDate(GenericDate):
|
|
154
182
|
def __init__(self, /, *, past_bound: bool = False) -> None:
|
155
183
|
"""Store -inf or inf"""
|
156
184
|
ordinal = -inf if past_bound else inf
|
185
|
+
self.__is_past = past_bound
|
157
186
|
super().__init__(ordinal)
|
158
187
|
|
159
188
|
def __repr__(self, /) -> str:
|
@@ -162,10 +191,10 @@ class InfinityDate(GenericDate):
|
|
162
191
|
|
163
192
|
def strftime(self, fmt: str, /) -> str:
|
164
193
|
"""String representation of the date"""
|
165
|
-
if self.
|
166
|
-
return
|
194
|
+
if self.__is_past:
|
195
|
+
return INFINITE_PAST_DATE_DISPLAY
|
167
196
|
#
|
168
|
-
return
|
197
|
+
return INFINITE_FUTURE_DATE_DISPLAY
|
169
198
|
|
170
199
|
__format__ = strftime
|
171
200
|
|
@@ -175,6 +204,25 @@ class InfinityDate(GenericDate):
|
|
175
204
|
f"{self.__class__.__name__} instances do not support .replace()"
|
176
205
|
)
|
177
206
|
|
207
|
+
@final
|
208
|
+
def pretty(
|
209
|
+
self,
|
210
|
+
/,
|
211
|
+
*,
|
212
|
+
inf_common_prefix: str = "",
|
213
|
+
inf_past_suffix: str = "",
|
214
|
+
inf_future_suffix: str = "",
|
215
|
+
# pylint:disable = unused-argument ; required by inheritance
|
216
|
+
fmt: str = ISO_DATE_FORMAT,
|
217
|
+
) -> str:
|
218
|
+
"""Return the date, pretty printed"""
|
219
|
+
if self.__is_past:
|
220
|
+
pretty_result = f"{inf_common_prefix}{inf_past_suffix}"
|
221
|
+
else:
|
222
|
+
pretty_result = f"{inf_common_prefix}{inf_future_suffix}"
|
223
|
+
#
|
224
|
+
return pretty_result or self.isoformat()
|
225
|
+
|
178
226
|
|
179
227
|
# pylint: disable=too-many-instance-attributes
|
180
228
|
class RealDate(GenericDate):
|
@@ -186,7 +234,7 @@ class RealDate(GenericDate):
|
|
186
234
|
self.year = year
|
187
235
|
self.month = month
|
188
236
|
self.day = day
|
189
|
-
super().__init__(
|
237
|
+
super().__init__(self._wrapped_date_object.toordinal())
|
190
238
|
self.timetuple = self._wrapped_date_object.timetuple
|
191
239
|
self.weekday = self._wrapped_date_object.weekday
|
192
240
|
self.isoweekday = self._wrapped_date_object.isoweekday
|
@@ -218,29 +266,78 @@ class RealDate(GenericDate):
|
|
218
266
|
)
|
219
267
|
)
|
220
268
|
|
269
|
+
@final
|
270
|
+
def pretty(
|
271
|
+
self,
|
272
|
+
/,
|
273
|
+
*,
|
274
|
+
inf_common_prefix: str = "",
|
275
|
+
inf_past_suffix: str = "",
|
276
|
+
inf_future_suffix: str = "",
|
277
|
+
fmt: str = ISO_DATE_FORMAT,
|
278
|
+
) -> str:
|
279
|
+
"""Return the date, pretty printed"""
|
280
|
+
return self.strftime(fmt)
|
281
|
+
|
282
|
+
|
283
|
+
# -----------------------------------------------------------------------------
|
284
|
+
# Private module functions
|
285
|
+
# -----------------------------------------------------------------------------
|
286
|
+
|
287
|
+
|
288
|
+
def _from_datetime_object(source: date | datetime, /) -> GenericDate:
|
289
|
+
"""Create a new RealDate instance from a
|
290
|
+
date or datetime object (module-private function)
|
291
|
+
"""
|
292
|
+
return RealDate(source.year, source.month, source.day)
|
293
|
+
|
221
294
|
|
222
295
|
# -----------------------------------------------------------------------------
|
223
296
|
# Public module constants continued:
|
224
|
-
#
|
297
|
+
# minimum and maximum dates and ordinals, resolution (one day)
|
225
298
|
# -----------------------------------------------------------------------------
|
226
299
|
|
227
|
-
|
228
|
-
|
300
|
+
|
301
|
+
INFINITE_PAST: Final[GenericDate] = InfinityDate(past_bound=True)
|
302
|
+
INFINITE_FUTURE: Final[GenericDate] = InfinityDate(past_bound=False)
|
303
|
+
|
304
|
+
MIN: Final[GenericDate] = INFINITE_PAST
|
305
|
+
MAX: Final[GenericDate] = INFINITE_FUTURE
|
306
|
+
|
307
|
+
REAL_MIN: Final[GenericDate] = _from_datetime_object(date.min)
|
308
|
+
REAL_MAX: Final[GenericDate] = _from_datetime_object(date.max)
|
309
|
+
|
310
|
+
MIN_ORDINAL: Final[int] = date.min.toordinal()
|
311
|
+
MAX_ORDINAL: Final[int] = date.max.toordinal()
|
312
|
+
|
313
|
+
RESOLUTION: Final[int] = 1
|
229
314
|
|
230
315
|
|
231
316
|
# -----------------------------------------------------------------------------
|
232
|
-
#
|
317
|
+
# Public module-level factory functions
|
233
318
|
# -----------------------------------------------------------------------------
|
234
319
|
|
235
320
|
|
236
|
-
def
|
321
|
+
def fromdatetime(source: date | datetime, /) -> GenericDate:
|
237
322
|
"""Create a new RealDate instance from a
|
238
323
|
date or datetime object
|
239
324
|
"""
|
240
|
-
return
|
325
|
+
return _from_datetime_object(source)
|
241
326
|
|
242
327
|
|
243
|
-
def
|
328
|
+
def from_datetime_object(source: date | datetime, /) -> GenericDate:
|
329
|
+
"""Create a new RealDate instance from a
|
330
|
+
date or datetime object (deprecated function name)
|
331
|
+
"""
|
332
|
+
warnings.warn(
|
333
|
+
"outdated function name, please use fromdatetime() instead",
|
334
|
+
DeprecationWarning,
|
335
|
+
stacklevel=2,
|
336
|
+
)
|
337
|
+
return fromdatetime(source)
|
338
|
+
|
339
|
+
|
340
|
+
def fromnative(
|
244
341
|
source: Any,
|
245
342
|
/,
|
246
343
|
*,
|
@@ -251,7 +348,7 @@ def from_native_type(
|
|
251
348
|
assuming infinity in the latter case
|
252
349
|
"""
|
253
350
|
if isinstance(source, str):
|
254
|
-
return
|
351
|
+
return _from_datetime_object(datetime.strptime(source, fmt))
|
255
352
|
#
|
256
353
|
if source == -inf or source is None and past_bound:
|
257
354
|
return MIN
|
@@ -262,69 +359,63 @@ def from_native_type(
|
|
262
359
|
raise ValueError(f"Don’t know how to convert {source!r} into a date")
|
263
360
|
|
264
361
|
|
362
|
+
def from_native_type(
|
363
|
+
source: Any,
|
364
|
+
/,
|
365
|
+
*,
|
366
|
+
fmt: str = ISO_DATETIME_FORMAT_UTC,
|
367
|
+
past_bound: bool = False,
|
368
|
+
) -> GenericDate:
|
369
|
+
"""Create an InfinityDate or RealDate instance from string or another type,
|
370
|
+
assuming infinity in the latter case (deprecated function name)
|
371
|
+
"""
|
372
|
+
warnings.warn(
|
373
|
+
"outdated function name, please use fromnative() instead",
|
374
|
+
DeprecationWarning,
|
375
|
+
stacklevel=2,
|
376
|
+
)
|
377
|
+
return fromnative(source, fmt=fmt, past_bound=past_bound)
|
378
|
+
|
379
|
+
|
265
380
|
def fromtimestamp(timestamp: float) -> GenericDate:
|
266
381
|
"""Create an InfinityDate or RealDate instance from the provided timestamp"""
|
267
|
-
if timestamp
|
268
|
-
return
|
269
|
-
#
|
270
|
-
if timestamp == inf:
|
271
|
-
return MAX
|
382
|
+
if timestamp in _INFINITY_FORMS:
|
383
|
+
return INFINITE_PAST if timestamp == -inf else INFINITE_FUTURE
|
272
384
|
#
|
273
385
|
stdlib_date_object = date.fromtimestamp(timestamp)
|
274
|
-
return
|
386
|
+
return _from_datetime_object(stdlib_date_object)
|
275
387
|
|
276
388
|
|
277
|
-
def fromordinal(ordinal:
|
389
|
+
def fromordinal(ordinal: int | float) -> GenericDate:
|
278
390
|
"""Create an InfinityDate or RealDate instance from the provided ordinal"""
|
279
|
-
if ordinal
|
280
|
-
return
|
281
|
-
#
|
282
|
-
if ordinal == inf:
|
283
|
-
return MAX
|
284
|
-
#
|
285
|
-
try:
|
286
|
-
new_ordinal = int(ordinal)
|
287
|
-
except ValueError as error:
|
288
|
-
raise ValueError(f"Cannot convert {ordinal!r} to integer") from error
|
391
|
+
if ordinal in _INFINITY_FORMS:
|
392
|
+
return INFINITE_PAST if ordinal == -inf else INFINITE_FUTURE
|
289
393
|
#
|
394
|
+
new_ordinal = trunc(ordinal)
|
290
395
|
if not MIN_ORDINAL <= new_ordinal <= MAX_ORDINAL:
|
291
396
|
raise OverflowError("RealDate value out of range")
|
292
397
|
#
|
293
398
|
stdlib_date_object = date.fromordinal(new_ordinal)
|
294
|
-
return
|
295
|
-
|
296
|
-
|
297
|
-
# -----------------------------------------------------------------------------
|
298
|
-
# Public module constants continued:
|
299
|
-
# minimum and maximum real dates
|
300
|
-
# -----------------------------------------------------------------------------
|
301
|
-
|
302
|
-
REAL_MIN: Final[GenericDate] = fromordinal(MIN_ORDINAL)
|
303
|
-
REAL_MAX: Final[GenericDate] = fromordinal(MAX_ORDINAL)
|
304
|
-
|
305
|
-
|
306
|
-
# -----------------------------------------------------------------------------
|
307
|
-
# Module-level factory functions continued
|
308
|
-
# -----------------------------------------------------------------------------
|
399
|
+
return _from_datetime_object(stdlib_date_object)
|
309
400
|
|
310
401
|
|
311
402
|
def fromisoformat(source: str, /) -> GenericDate:
|
312
403
|
"""Create an InfinityDate or RealDate instance from an iso format representation"""
|
313
404
|
lower_source_stripped = source.strip().lower()
|
314
|
-
if lower_source_stripped ==
|
315
|
-
return
|
405
|
+
if lower_source_stripped == INFINITE_FUTURE_DATE_DISPLAY:
|
406
|
+
return INFINITE_FUTURE
|
316
407
|
#
|
317
|
-
if lower_source_stripped ==
|
318
|
-
return
|
408
|
+
if lower_source_stripped == INFINITE_PAST_DATE_DISPLAY:
|
409
|
+
return INFINITE_PAST
|
319
410
|
#
|
320
|
-
return
|
411
|
+
return _from_datetime_object(date.fromisoformat(source))
|
321
412
|
|
322
413
|
|
323
414
|
def fromisocalendar(year: int, week: int, weekday: int) -> GenericDate:
|
324
415
|
"""Create a RealDate instance from an iso calendar date"""
|
325
|
-
return
|
416
|
+
return _from_datetime_object(date.fromisocalendar(year, week, weekday))
|
326
417
|
|
327
418
|
|
328
419
|
def today() -> GenericDate:
|
329
420
|
"""Today as RealDate object"""
|
330
|
-
return
|
421
|
+
return _from_datetime_object(date.today())
|
@@ -0,0 +1,170 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: infdate
|
3
|
+
Version: 0.2.3
|
4
|
+
Summary: Date object wrapper supporting infinity
|
5
|
+
Project-URL: Homepage, https://gitlab.com/blackstream-x/infdate
|
6
|
+
Project-URL: CI, https://gitlab.com/blackstream-x/infdate/-/pipelines
|
7
|
+
Project-URL: Bug Tracker, https://gitlab.com/blackstream-x/infdate/-/issues
|
8
|
+
Project-URL: Repository, https://gitlab.com/blackstream-x/infdate.git
|
9
|
+
Author-email: Rainer Schwarzbach <rainer@blackstream.de>
|
10
|
+
License-File: LICENSE
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
12
|
+
Classifier: Intended Audience :: Developers
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
14
|
+
Classifier: Operating System :: OS Independent
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
|
+
Requires-Python: >=3.10
|
24
|
+
Description-Content-Type: text/markdown
|
25
|
+
|
26
|
+
# infdate
|
27
|
+
|
28
|
+
_Python module for date calculations implementing a concept of infinity_
|
29
|
+
|
30
|
+
## Module description
|
31
|
+
|
32
|
+
### Classes overview
|
33
|
+
|
34
|
+
└── GenericDate
|
35
|
+
├── InfinityDate
|
36
|
+
└── RealDate
|
37
|
+
|
38
|
+
|
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 for that purpose.
|
42
|
+
|
43
|
+
**InfinityDate** can represent either past or future infinity.
|
44
|
+
The module-level constants **INFINITE_PAST** and **INFINITE_FUTURE**
|
45
|
+
contain the two possible **InfinityDate** instance variations.
|
46
|
+
|
47
|
+
**RealDate** instances represent real dates like the standard library’s
|
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.
|
51
|
+
|
52
|
+
For any valid **RealDate** instance, the following is **True**:
|
53
|
+
|
54
|
+
``` python
|
55
|
+
infdate.MIN < infdate.REAL_MIN <= real_date_instance <= infdate.REAL_MAX < infdate.MAX
|
56
|
+
```
|
57
|
+
|
58
|
+
### Module-level constants
|
59
|
+
|
60
|
+
* **INFINITE_PAST** = **InfinityDate(**_past_bound_=`True`**)** → infinity before any date
|
61
|
+
* **INFINITE_FUTURE** = **InfinityDate(**_past_bound_=`False`**)** → infinity after any date
|
62
|
+
* **MIN** = **INFINITE_PAST**
|
63
|
+
* **MAX** = **INFINITE_FUTURE**
|
64
|
+
* **REAL_MIN** = **RealDate(**_`1`, `1`, `1`_**)** → the same date as **datetime.date.min**
|
65
|
+
* **REAL_MAX** = **RealDate(**_`9999`, `12`, `31`_**)** → the same date as **datetime.date.max**
|
66
|
+
* **MIN_ORDINAL** = `1` → the same value as **datetime.date.min.toordinal()**
|
67
|
+
* **MAX_ORDINAL** = `3652059` → the same value as **datetime.date.max.toordinal()**
|
68
|
+
* **RESOLUTION** = `1` → represents the lowest possible date difference: _one day_
|
69
|
+
|
70
|
+
|
71
|
+
### Module-level factory functions
|
72
|
+
|
73
|
+
The following factory methods from the **datetime.date** class
|
74
|
+
are provided as module-level functions:
|
75
|
+
|
76
|
+
* **fromtimestamp()** (also accepting **-math.inf** or **math.inf**)
|
77
|
+
* **fromordinal()** (also accepting **-math.inf** or **math.inf**)
|
78
|
+
* **fromisoformat()**
|
79
|
+
* **fromisocalendar()**
|
80
|
+
* **today()**
|
81
|
+
|
82
|
+
Two additional factory functions are provided:
|
83
|
+
|
84
|
+
* **fromdatetime()** to create a **RealDate** instance from a
|
85
|
+
**datetime.date** or **datetime.datetime** instance
|
86
|
+
(deprecated old name: _from_datetime_object()_).
|
87
|
+
|
88
|
+
* **fromnative()** to create an **InfinityDate** or **RealDate**
|
89
|
+
instance from a string, from **None**, **-math.inf** or **math.inf**
|
90
|
+
(deprecated old name: _from_native_type()_).
|
91
|
+
|
92
|
+
This can come handy when dealing with API representations of dates,
|
93
|
+
eg. in GitLab’s [Personal Access Tokens API].
|
94
|
+
|
95
|
+
|
96
|
+
### Differences between the infdate module classes and datetime.date
|
97
|
+
|
98
|
+
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):
|
99
|
+
|
100
|
+
* infdate module classes have no **max**, **min** or **resolution** attributes,
|
101
|
+
but there are [module-level constants] serving the same purpose.
|
102
|
+
|
103
|
+
* The **.toordinal()** method returns **int**, **math.inf**, or **-math.inf**.
|
104
|
+
|
105
|
+
* Subtracting a date from an **InfinityDate** or **RealDate** always returns
|
106
|
+
an **int**, **math.inf**, or **-math.inf** instead of a **datetime.timedelta** instance.
|
107
|
+
|
108
|
+
* Likewise, you cannot add or subtract **datetime.timedelta** instances
|
109
|
+
from an **InfinityDate** or **RealDate**, only **float** or **int**
|
110
|
+
(support for adding and subtracting datetime.timedelta instances might be added in the future, [see the feature request]).
|
111
|
+
|
112
|
+
* infdate module classes have a **.pretty()** method that can be used to format **RealDate** instances with a format string like **.strftime()** (provided with the _fmt_ argument that defaults to the ISO format `%Y-%m-%d`),
|
113
|
+
or to apply a custom format to **InfinityDate** classes using the _inf_common_prefix_, _inf_past_suffix_ and _inf_future_suffix_ arguments.
|
114
|
+
|
115
|
+
|
116
|
+
## Example usage
|
117
|
+
|
118
|
+
``` pycon
|
119
|
+
>>> import infdate
|
120
|
+
>>> today = infdate.today()
|
121
|
+
>>> today
|
122
|
+
RealDate(2025, 6, 30)
|
123
|
+
>>> print(f"US date notation: {today:%m/%d/%y}")
|
124
|
+
US date notation: 06/30/25
|
125
|
+
>>> today.ctime()
|
126
|
+
'Mon Jun 30 00:00:00 2025'
|
127
|
+
>>> today.isocalendar()
|
128
|
+
datetime.IsoCalendarDate(year=2025, week=27, weekday=1)
|
129
|
+
>>> yesterday = today - 1
|
130
|
+
>>> yesterday.ctime()
|
131
|
+
'Sun Jun 29 00:00:00 2025'
|
132
|
+
>>> today - yesterday
|
133
|
+
1
|
134
|
+
>>> infdate.INFINITE_PAST
|
135
|
+
InfinityDate(past_bound=True)
|
136
|
+
>>> infdate.INFINITE_FUTURE
|
137
|
+
InfinityDate(past_bound=False)
|
138
|
+
>>> infdate.INFINITE_FUTURE - today
|
139
|
+
inf
|
140
|
+
>>> infdate.INFINITE_FUTURE - infdate.INFINITE_PAST
|
141
|
+
inf
|
142
|
+
```
|
143
|
+
|
144
|
+
**InfinityDate** and **RealDate** instances can be compared with each other, and also with **datetime.date** instances.
|
145
|
+
|
146
|
+
Subtracting **InfinityDate** or **RealDate** and **datetime.date** instances from each other also works:
|
147
|
+
|
148
|
+
``` pycon
|
149
|
+
>>> from datetime import date
|
150
|
+
>>> stdlib_today = date.today()
|
151
|
+
>>> stdlib_today
|
152
|
+
datetime.date(2025, 6, 30)
|
153
|
+
>>> today == stdlib_today
|
154
|
+
True
|
155
|
+
>>> yesterday < stdlib_today
|
156
|
+
True
|
157
|
+
>>> yesterday - stdlib_today
|
158
|
+
-1
|
159
|
+
>>> stdlib_today - yesterday
|
160
|
+
1
|
161
|
+
>>> stdlib_today - infdate.INFINITE_PAST
|
162
|
+
inf
|
163
|
+
```
|
164
|
+
|
165
|
+
|
166
|
+
* * *
|
167
|
+
[datetime.date]: https://docs.python.org/3/library/datetime.html#date-objects
|
168
|
+
[Personal Access Tokens API]: https://docs.gitlab.com/api/personal_access_tokens/
|
169
|
+
[module-level constants]: #module-level-constants
|
170
|
+
[see the feature request]: https://gitlab.com/blackstream-x/infdate/-/issues/6
|
@@ -0,0 +1,6 @@
|
|
1
|
+
infdate/__init__.py,sha256=9YUvBcztVWjzQYvu6N0ISSrG_OoH6BjusxX3TGNNv5s,13774
|
2
|
+
infdate/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
infdate-0.2.3.dist-info/METADATA,sha256=oX3erUYcGq-4dmRxqqMxyOG_RCHvTivIiFL3WcxaN94,6569
|
4
|
+
infdate-0.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
5
|
+
infdate-0.2.3.dist-info/licenses/LICENSE,sha256=867pxriiObx28vCU1JsRtu3H9kUKyl54e0-xl1IIv3Y,913
|
6
|
+
infdate-0.2.3.dist-info/RECORD,,
|
infdate-0.2.1.dist-info/METADATA
DELETED
@@ -1,147 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: infdate
|
3
|
-
Version: 0.2.1
|
4
|
-
Summary: Date object wrapper supporting infinity
|
5
|
-
Project-URL: Homepage, https://gitlab.com/blackstream-x/infdate
|
6
|
-
Project-URL: CI, https://gitlab.com/blackstream-x/infdate/-/pipelines
|
7
|
-
Project-URL: Bug Tracker, https://gitlab.com/blackstream-x/infdate/-/issues
|
8
|
-
Project-URL: Repository, https://gitlab.com/blackstream-x/infdate.git
|
9
|
-
Author-email: Rainer Schwarzbach <rainer@blackstream.de>
|
10
|
-
License-File: LICENSE
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
12
|
-
Classifier: Intended Audience :: Developers
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
14
|
-
Classifier: Operating System :: OS Independent
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
16
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
17
|
-
Classifier: Programming Language :: Python :: 3.11
|
18
|
-
Classifier: Programming Language :: Python :: 3.12
|
19
|
-
Classifier: Programming Language :: Python :: 3.13
|
20
|
-
Classifier: Programming Language :: Python :: 3.14
|
21
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
22
|
-
Requires-Python: >=3.11
|
23
|
-
Description-Content-Type: text/markdown
|
24
|
-
|
25
|
-
# infdate
|
26
|
-
|
27
|
-
_Python module for date calculations implementing a concept of infinity_
|
28
|
-
|
29
|
-
## Module description
|
30
|
-
|
31
|
-
### Classes overview
|
32
|
-
|
33
|
-
└── GenericDate
|
34
|
-
├── InfinityDate
|
35
|
-
└── RealDate
|
36
|
-
|
37
|
-
|
38
|
-
The base class **GenericDate** should not be instantiated but can be used as a type annotation.
|
39
|
-
|
40
|
-
**InfinityDate** can represent either positive or negative infinity.
|
41
|
-
The module-level constants **MIN** and **MAX** contain the two possible
|
42
|
-
**InfinityDate** instance variations.
|
43
|
-
|
44
|
-
**RealDate** instances represent real dates like the standard library’s
|
45
|
-
**datetime.date** class, with mostly equal or similar semantics. The module -level constants **REAL_MIN** and **REAL_MAX** are the eqivalents of **datetime.date.min** and **datetime.date.max** as **RealDate** instances.
|
46
|
-
|
47
|
-
For any valid **RealDate** instance, the following is **True**:
|
48
|
-
|
49
|
-
``` python
|
50
|
-
infdate.MIN < infdate.REAL_MIN <= real_date_instance <= infdate.REAL_MAX < infdate.MAX
|
51
|
-
```
|
52
|
-
|
53
|
-
### Module-level factory functions
|
54
|
-
|
55
|
-
|
56
|
-
The following factory methods from the **datetime.date** class
|
57
|
-
are provided as module-level functions:
|
58
|
-
|
59
|
-
* **fromtimestamp()** (also accepting **-math.inf** or **math.inf**)
|
60
|
-
* **fromordinal()** (also accepting **-math.inf** or **math.inf**)
|
61
|
-
* **fromisoformat()**
|
62
|
-
* **fromisocalendar()**
|
63
|
-
* **today()**
|
64
|
-
|
65
|
-
Two additional factory functions are provided:
|
66
|
-
|
67
|
-
* **from_datetime_object()** to create a **RealDate** instance from a
|
68
|
-
**datetime.date** or **datetime.datetime** instance
|
69
|
-
|
70
|
-
* **from_native_type()** to create an **InfinityDate** or **RealDate**
|
71
|
-
instance from a string, from **None**, **-math.inf** or **math.inf**.
|
72
|
-
|
73
|
-
This can come handy when dealing with API representations of dates,
|
74
|
-
eg. in GitLab’s [Personal Access Tokens API].
|
75
|
-
|
76
|
-
|
77
|
-
### Differences between infdate classes and datetime.date
|
78
|
-
|
79
|
-
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
|
-
|
81
|
-
* The **.toordinal()** method returns **float** instead of **int**
|
82
|
-
|
83
|
-
* The **resolution** attribute is **1.0** instead of **datetime.timedelta(days=1)**
|
84
|
-
but also represents exactly one day.
|
85
|
-
|
86
|
-
* Subtracting a date from an **InfinityDate** or **RealDate** always returns a float instead of a **datetime.timedelta** instance.
|
87
|
-
|
88
|
-
* Likewise, you cannot add or subtract **datetime.timedelta** instances
|
89
|
-
from an **InfinityDate** or **RealDate**, only **float** or **int** (support for adding and subtracting datetime.timedelta instances might be added in the future).
|
90
|
-
|
91
|
-
|
92
|
-
## Example usage
|
93
|
-
|
94
|
-
``` pycon
|
95
|
-
>>> import infdate
|
96
|
-
>>> today = infdate.today()
|
97
|
-
>>> today
|
98
|
-
RealDate(2025, 6, 25)
|
99
|
-
>>> print(today)
|
100
|
-
2025-06-25
|
101
|
-
>>> print(f"US date notation {today:%m/%d/%y}")
|
102
|
-
US date notation 06/25/25
|
103
|
-
>>> today.ctime()
|
104
|
-
'Wed Jun 25 00:00:00 2025'
|
105
|
-
>>> today.isocalendar()
|
106
|
-
datetime.IsoCalendarDate(year=2025, week=26, weekday=3)
|
107
|
-
>>>
|
108
|
-
>>> yesterday = today - 1
|
109
|
-
>>> yesterday.ctime()
|
110
|
-
'Tue Jun 24 00:00:00 2025'
|
111
|
-
>>>
|
112
|
-
>>> today - yesterday
|
113
|
-
1.0
|
114
|
-
>>> infdate.MIN
|
115
|
-
InfinityDate(past_bound=True)
|
116
|
-
>>> infdate.MAX
|
117
|
-
InfinityDate(past_bound=False)
|
118
|
-
>>> infdate.MAX - today
|
119
|
-
inf
|
120
|
-
>>> infdate.MAX - infdate.MIN
|
121
|
-
inf
|
122
|
-
```
|
123
|
-
|
124
|
-
**InfinityDate** and **RealDate** instances can be compared with each other, and also with **datetime.date** instances.
|
125
|
-
|
126
|
-
Subtracting instances from each other also works, but currently, `__rsub__` is not implemented yet, so although the other direction works, subtracting an **InfinityDate** or **RealDate** _from_ a **datetime.date**
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
* * *
|
147
|
-
[Personal Access Tokens API]: https://docs.gitlab.com/api/personal_access_tokens/
|
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
|