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 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
- # pylint: disable=invalid-name
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
- self.__ordinal = ordinal
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__(float(self._wrapped_date_object.toordinal()))
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: float | int) -> GenericDate:
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
- try:
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.1
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.11
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 but can be used as a type annotation.
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. The module -level constants **REAL_MIN** and **REAL_MAX** are the eqivalents of **datetime.date.min** and **datetime.date.max** as **RealDate** instances.
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 factory functions
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
- * The **.toordinal()** method returns **float** instead of **int**
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 **resolution** attribute is **1.0** instead of **datetime.timedelta(days=1)**
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 a float instead of a **datetime.timedelta** instance.
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** (support for adding and subtracting datetime.timedelta instances might be added in the future).
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, 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
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
- 'Wed Jun 25 00:00:00 2025'
121
+ 'Fri Jun 27 00:00:00 2025'
105
122
  >>> today.isocalendar()
106
- datetime.IsoCalendarDate(year=2025, week=26, weekday=3)
123
+ datetime.IsoCalendarDate(year=2025, week=26, weekday=5)
107
124
  >>>
108
125
  >>> yesterday = today - 1
109
126
  >>> yesterday.ctime()
110
- 'Tue Jun 24 00:00:00 2025'
127
+ 'Thu Jun 26 00:00:00 2025'
111
128
  >>>
112
129
  >>> today - yesterday
113
- 1.0
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 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
+ 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
- [Personal Access Tokens API]: https://docs.gitlab.com/api/personal_access_tokens/
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,,
@@ -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,,