infdate 0.2.0__tar.gz → 0.2.2__tar.gz

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-0.2.2/PKG-INFO ADDED
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: infdate
3
+ Version: 0.2.2
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 in typing annotations.
42
+
43
+ **InfinityDate** can represent either positive or negative infinity.
44
+ The module-level constants **MIN** and **MAX** contain the two possible
45
+ **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
+ * **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_
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
+ * **from_datetime_object()** to create a **RealDate** instance from a
85
+ **datetime.date** or **datetime.datetime** instance
86
+
87
+ * **from_native_type()** to create an **InfinityDate** or **RealDate**
88
+ instance from a string, from **None**, **-math.inf** or **math.inf**.
89
+
90
+ This can come handy when dealing with API representations of dates,
91
+ eg. in GitLab’s [Personal Access Tokens API].
92
+
93
+
94
+ ### Differences between infdate classes and datetime.date
95
+
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):
97
+
98
+ * infdate classes have no **max**, **min** or **resolution** attributes,
99
+ but there are [module-level constants] serving the same purpose.
100
+
101
+ * The **.toordinal()** method returns **int**, **math.inf**, or **-math.inf**.
102
+
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.
105
+
106
+ * Likewise, you cannot add or subtract **datetime.timedelta** instances
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]).
109
+
110
+
111
+ ## Example usage
112
+
113
+ ``` pycon
114
+ >>> import infdate
115
+ >>> today = infdate.today()
116
+ >>> today
117
+ RealDate(2025, 6, 27)
118
+ >>> print(f"US date notation: {today:%m/%d/%y}")
119
+ US date notation: 06/27/25
120
+ >>> today.ctime()
121
+ 'Fri Jun 27 00:00:00 2025'
122
+ >>> today.isocalendar()
123
+ datetime.IsoCalendarDate(year=2025, week=26, weekday=5)
124
+ >>>
125
+ >>> yesterday = today - 1
126
+ >>> yesterday.ctime()
127
+ 'Thu Jun 26 00:00:00 2025'
128
+ >>>
129
+ >>> today - yesterday
130
+ 1
131
+ >>> infdate.MIN
132
+ InfinityDate(past_bound=True)
133
+ >>> infdate.MAX
134
+ InfinityDate(past_bound=False)
135
+ >>> infdate.MAX - today
136
+ inf
137
+ >>> infdate.MAX - infdate.MIN
138
+ inf
139
+ ```
140
+
141
+ **InfinityDate** and **RealDate** instances can be compared with each other, and also with **datetime.date** instances.
142
+
143
+ Subtracting **InfinityDate** or **RealDate** and **datetime.date** instances from each other also works:
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
+ ```
159
+
160
+
161
+ * * *
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,140 @@
1
+ # infdate
2
+
3
+ _Python module for date calculations implementing a concept of infinity_
4
+
5
+ ## Module description
6
+
7
+ ### Classes overview
8
+
9
+ └── GenericDate
10
+ ├── InfinityDate
11
+ └── RealDate
12
+
13
+
14
+ The base class **GenericDate** should not be instantiated
15
+ but can be used as a type annotation. In fact, it should be preferred
16
+ over the other classes in typing annotations.
17
+
18
+ **InfinityDate** can represent either positive or negative infinity.
19
+ The module-level constants **MIN** and **MAX** contain the two possible
20
+ **InfinityDate** instance variations.
21
+
22
+ **RealDate** instances represent real dates like the standard library’s
23
+ **[datetime.date]** class, with mostly equal or similar semantics.
24
+ The module -level constants **REAL_MIN** and **REAL_MAX** are the eqivalents
25
+ of **datetime.date.min** and **datetime.date.max** as **RealDate** instances.
26
+
27
+ For any valid **RealDate** instance, the following is **True**:
28
+
29
+ ``` python
30
+ infdate.MIN < infdate.REAL_MIN <= real_date_instance <= infdate.REAL_MAX < infdate.MAX
31
+ ```
32
+
33
+ ### Module-level constants
34
+
35
+ * **MIN** = **InfinityDate(**_past_bound_=`True`**)** → the beginning of time
36
+ * **MAX** = **InfinityDate(**_past_bound_=`False`**)** → the end of time
37
+ * **MIN_ORDINAL** = `1` → same as **datetime.date.min.toordinal()**
38
+ * **MAX_ORDINAL** = `3652059` → same as **datetime.date.max.toordinal()**
39
+ * **REAL_MIN** = **fromordinal(MIN_ORDINAL)**
40
+ → represents _0001-01-01_, the same date as **datetime.date.min**
41
+ * **REAL_MAX** = **fromordinal(MAX_ORDINAL)**
42
+ → represents _9999-12-31_, the same date as **datetime.date.max**
43
+ * **RESOLUTION** = `1` → represents the lowest possible date difference: _one day_
44
+
45
+
46
+ ### Module-level factory functions
47
+
48
+ The following factory methods from the **datetime.date** class
49
+ are provided as module-level functions:
50
+
51
+ * **fromtimestamp()** (also accepting **-math.inf** or **math.inf**)
52
+ * **fromordinal()** (also accepting **-math.inf** or **math.inf**)
53
+ * **fromisoformat()**
54
+ * **fromisocalendar()**
55
+ * **today()**
56
+
57
+ Two additional factory functions are provided:
58
+
59
+ * **from_datetime_object()** to create a **RealDate** instance from a
60
+ **datetime.date** or **datetime.datetime** instance
61
+
62
+ * **from_native_type()** to create an **InfinityDate** or **RealDate**
63
+ instance from a string, from **None**, **-math.inf** or **math.inf**.
64
+
65
+ This can come handy when dealing with API representations of dates,
66
+ eg. in GitLab’s [Personal Access Tokens API].
67
+
68
+
69
+ ### Differences between infdate classes and datetime.date
70
+
71
+ 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):
72
+
73
+ * infdate classes have no **max**, **min** or **resolution** attributes,
74
+ but there are [module-level constants] serving the same purpose.
75
+
76
+ * The **.toordinal()** method returns **int**, **math.inf**, or **-math.inf**.
77
+
78
+ * Subtracting a date from an **InfinityDate** or **RealDate** always returns
79
+ an **int**, **math.inf**, or **-math.inf** instead of a **datetime.timedelta** instance.
80
+
81
+ * Likewise, you cannot add or subtract **datetime.timedelta** instances
82
+ from an **InfinityDate** or **RealDate**, only **float** or **int**
83
+ (support for adding and subtracting datetime.timedelta instances might be added in the future, [see the feature request]).
84
+
85
+
86
+ ## Example usage
87
+
88
+ ``` pycon
89
+ >>> import infdate
90
+ >>> today = infdate.today()
91
+ >>> today
92
+ RealDate(2025, 6, 27)
93
+ >>> print(f"US date notation: {today:%m/%d/%y}")
94
+ US date notation: 06/27/25
95
+ >>> today.ctime()
96
+ 'Fri Jun 27 00:00:00 2025'
97
+ >>> today.isocalendar()
98
+ datetime.IsoCalendarDate(year=2025, week=26, weekday=5)
99
+ >>>
100
+ >>> yesterday = today - 1
101
+ >>> yesterday.ctime()
102
+ 'Thu Jun 26 00:00:00 2025'
103
+ >>>
104
+ >>> today - yesterday
105
+ 1
106
+ >>> infdate.MIN
107
+ InfinityDate(past_bound=True)
108
+ >>> infdate.MAX
109
+ InfinityDate(past_bound=False)
110
+ >>> infdate.MAX - today
111
+ inf
112
+ >>> infdate.MAX - infdate.MIN
113
+ inf
114
+ ```
115
+
116
+ **InfinityDate** and **RealDate** instances can be compared with each other, and also with **datetime.date** instances.
117
+
118
+ Subtracting **InfinityDate** or **RealDate** and **datetime.date** instances from each other also works:
119
+
120
+ ``` pycon
121
+ >>> from datetime import date
122
+ >>> stdlib_today = date.today()
123
+ >>> today == stdlib_today
124
+ True
125
+ >>> yesterday < stdlib_today
126
+ True
127
+ >>> yesterday - stdlib_today
128
+ -1
129
+ >>> stdlib_today - yesterday
130
+ 1
131
+ >>> stdlib_today - infdate.MIN
132
+ inf
133
+ ```
134
+
135
+
136
+ * * *
137
+ [datetime.date]: https://docs.python.org/3/library/datetime.html#date-objects
138
+ [Personal Access Tokens API]: https://docs.gitlab.com/api/personal_access_tokens/
139
+ [module-level constants]: #module-level-constants
140
+ [see the feature request]: https://gitlab.com/blackstream-x/infdate/-/issues/6
@@ -1,17 +1,18 @@
1
1
  [project]
2
2
  name = "infdate"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "Date object wrapper supporting infinity"
5
5
  readme = "README.md"
6
6
  authors = [
7
7
  { name = "Rainer Schwarzbach", email = "rainer@blackstream.de" },
8
8
  ]
9
- requires-python = ">=3.11"
9
+ requires-python = ">=3.10"
10
10
  classifiers = [
11
11
  "Development Status :: 4 - Beta",
12
12
  "License :: OSI Approved :: MIT License",
13
13
  "Operating System :: OS Independent",
14
14
  "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
15
16
  "Programming Language :: Python :: 3.11",
16
17
  "Programming Language :: Python :: 3.12",
17
18
  "Programming Language :: Python :: 3.13",
@@ -0,0 +1,8 @@
1
+ {
2
+ "ci_pipeline_created_at": "2025-06-27T15:27:06Z",
3
+ "ci_pipeline_id": "1894255377",
4
+ "ci_pipeline_url": "https://gitlab.com/blackstream-x/infdate/-/pipelines/1894255377",
5
+ "ci_project_title": "infdate",
6
+ "ci_project_url": "https://gitlab.com/blackstream-x/infdate",
7
+ "ci_commit_sha": "80b8357d36a7b921e46ce45b5c82298080b08b5f"
8
+ }
@@ -4,6 +4,6 @@ _provided by [glsr-present](https://pypi.org/project/glsr-present/) 0.3.6_
4
4
 
5
5
  [infdate](https://gitlab.com/blackstream-x/infdate)
6
6
  built with pipeline
7
- [1889338569](https://gitlab.com/blackstream-x/infdate/-/pipelines/1889338569)
8
- (build started 2025-06-25T15:48:59Z)
7
+ [1894255377](https://gitlab.com/blackstream-x/infdate/-/pipelines/1894255377)
8
+ (build started 2025-06-27T15:27:06Z)
9
9
 
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="39" time="1.137" timestamp="2025-06-27T15:27:43.088896+00:00" hostname="runner-xxurkrix-project-71015407-concurrent-0"><testcase classname="tests.test_infdate.GenericDateBase" name="test_bool" time="0.003" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_eq" time="0.001" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_ge" time="0.058" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_gt" time="0.046" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_hash" time="0.001" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_isoformat" time="0.002" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_le" time="0.057" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_lt" time="0.047" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_nan_not_allowed" time="0.002" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_ne" time="0.047" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_replace" time="0.001" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_repr" time="0.001" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_str" time="0.002" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_strftime" time="0.001" /><testcase classname="tests.test_infdate.GenericDateBase" name="test_toordinal" time="0.001" /><testcase classname="tests.test_infdate.GenericDateArithmetics" name="test_add_and_radd" time="0.008" /><testcase classname="tests.test_infdate.GenericDateArithmetics" name="test_add_days" time="0.005" /><testcase classname="tests.test_infdate.GenericDateArithmetics" name="test_rsub_stdlib_date" time="0.001" /><testcase classname="tests.test_infdate.GenericDateArithmetics" name="test_sub_date" time="0.001" /><testcase classname="tests.test_infdate.GenericDateArithmetics" name="test_sub_number" time="0.005" /><testcase classname="tests.test_infdate.GenericDateArithmetics" name="test_sub_stdlib_date" time="0.001" /><testcase classname="tests.test_infdate.InfinityDate" name="test_replace" time="0.002" /><testcase classname="tests.test_infdate.InfinityDate" name="test_repr" time="0.001" /><testcase classname="tests.test_infdate.InfinityDate" name="test_strftime" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_attributes" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_bool" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_proxied_methods" time="0.002" /><testcase classname="tests.test_infdate.RealDate" name="test_random_date_within_limits" time="0.295" /><testcase classname="tests.test_infdate.RealDate" name="test_replace" time="0.002" /><testcase classname="tests.test_infdate.RealDate" name="test_repr" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_strftime" time="0.003" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_from_datetime_object" time="0.002" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_from_native_type" time="0.006" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromisocalendar" time="0.002" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromisoformat" time="0.002" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromordinal" time="0.003" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromtimestamp" time="0.003" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromtimestamp_errors" time="0.001" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_today" time="0.003" /></testsuite></testsuites>
@@ -6,10 +6,15 @@ 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
 
13
+ # -----------------------------------------------------------------------------
14
+ # Public module constants:
15
+ # display definitions, format strings, upper and loer ordinal boundaries
16
+ # -----------------------------------------------------------------------------
17
+
13
18
  INFINITE_DATE_DISPLAY: Final[str] = "<inf>"
14
19
  NEGATIVE_INFINITE_DATE_DISPLAY: Final[str] = "<-inf>"
15
20
 
@@ -23,22 +28,34 @@ ISO_DATETIME_FORMAT_UTC: Final[str] = f"{ISO_DATE_FORMAT}T%H:%M:%S.%fZ"
23
28
  MIN_ORDINAL: Final[int] = date.min.toordinal()
24
29
  MAX_ORDINAL: Final[int] = date.max.toordinal()
25
30
 
31
+ RESOLUTION: Final[int] = 1
32
+
33
+
34
+ # -----------------------------------------------------------------------------
35
+ # Internal module constants:
36
+ # TypeVar for GenericDate
37
+ # -----------------------------------------------------------------------------
26
38
 
27
39
  _GD = TypeVar("_GD", bound="GenericDate")
28
40
 
29
41
 
42
+ # -----------------------------------------------------------------------------
43
+ # Classes
44
+ # -----------------------------------------------------------------------------
45
+
46
+
30
47
  class GenericDate:
31
48
  """Base Date object derived from an ordinal"""
32
49
 
33
- # pylint: disable=invalid-name
34
- resolution: Final = 1
35
- # pylint: enable=invalid-name
36
-
37
- def __init__(self, ordinal: float, /) -> None:
50
+ def __init__(self, ordinal: int | float, /) -> None:
38
51
  """Create a date-like object"""
39
- self.__ordinal = ordinal
52
+ if ordinal in (-inf, inf):
53
+ self.__ordinal = ordinal
54
+ else:
55
+ self.__ordinal = trunc(ordinal)
56
+ #
40
57
 
41
- def toordinal(self: _GD) -> float:
58
+ def toordinal(self: _GD) -> int | float:
42
59
  """to ordinal (almost like date.toordinal())"""
43
60
  return self.__ordinal
44
61
 
@@ -94,24 +111,30 @@ class GenericDate:
94
111
  return self
95
112
  #
96
113
  # Return a RealDate instance if possible
97
- return fromordinal(self.__ordinal + delta)
114
+ return fromordinal(self.__ordinal + trunc(delta))
98
115
 
99
116
  def __add__(self: _GD, delta: int | float, /) -> _GD:
100
117
  """gd_instance1 + number capability"""
101
118
  return self._add_days(delta)
102
119
 
120
+ __radd__ = __add__
121
+
103
122
  @overload
104
123
  def __sub__(self: _GD, other: int | float, /) -> _GD: ...
105
124
  @overload
106
- def __sub__(self: _GD, other: _GD, /) -> int | float: ...
125
+ def __sub__(self: _GD, other: _GD | date, /) -> int | float: ...
107
126
  @final
108
- def __sub__(self: _GD, other: _GD | int | float, /) -> _GD | int | float:
127
+ def __sub__(self: _GD, other: _GD | date | int | float, /) -> _GD | int | float:
109
128
  """subtract other, respecting possibly nondeterministic values"""
110
129
  if isinstance(other, (int, float)):
111
130
  return self._add_days(-other)
112
131
  #
113
132
  return self.__ordinal - other.toordinal()
114
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
+
115
138
  def __repr__(self: _GD, /) -> str:
116
139
  """String representation of the object"""
117
140
  return f"{self.__class__.__name__}({repr(self.__ordinal)})"
@@ -171,7 +194,7 @@ class RealDate(GenericDate):
171
194
  self.year = year
172
195
  self.month = month
173
196
  self.day = day
174
- super().__init__(float(self._wrapped_date_object.toordinal()))
197
+ super().__init__(self._wrapped_date_object.toordinal())
175
198
  self.timetuple = self._wrapped_date_object.timetuple
176
199
  self.weekday = self._wrapped_date_object.weekday
177
200
  self.isoweekday = self._wrapped_date_object.isoweekday
@@ -204,12 +227,18 @@ class RealDate(GenericDate):
204
227
  )
205
228
 
206
229
 
207
- # Absolute minimum and maximum dates
230
+ # -----------------------------------------------------------------------------
231
+ # Public module constants continued:
232
+ # absolute minimum and maximum dates
233
+ # -----------------------------------------------------------------------------
234
+
208
235
  MIN: Final[GenericDate] = InfinityDate(past_bound=True)
209
236
  MAX: Final[GenericDate] = InfinityDate(past_bound=False)
210
237
 
211
238
 
212
- # Factory functions
239
+ # -----------------------------------------------------------------------------
240
+ # Module-level factory functions
241
+ # -----------------------------------------------------------------------------
213
242
 
214
243
 
215
244
  def from_datetime_object(source: date | datetime, /) -> GenericDate:
@@ -241,7 +270,19 @@ def from_native_type(
241
270
  raise ValueError(f"Don’t know how to convert {source!r} into a date")
242
271
 
243
272
 
244
- def fromordinal(ordinal: float | int) -> GenericDate:
273
+ def fromtimestamp(timestamp: float) -> GenericDate:
274
+ """Create an InfinityDate or RealDate instance from the provided timestamp"""
275
+ if timestamp == -inf:
276
+ return MIN
277
+ #
278
+ if timestamp == inf:
279
+ return MAX
280
+ #
281
+ stdlib_date_object = date.fromtimestamp(timestamp)
282
+ return from_datetime_object(stdlib_date_object)
283
+
284
+
285
+ def fromordinal(ordinal: int | float) -> GenericDate:
245
286
  """Create an InfinityDate or RealDate instance from the provided ordinal"""
246
287
  if ordinal == -inf:
247
288
  return MIN
@@ -249,11 +290,7 @@ def fromordinal(ordinal: float | int) -> GenericDate:
249
290
  if ordinal == inf:
250
291
  return MAX
251
292
  #
252
- try:
253
- new_ordinal = int(ordinal)
254
- except ValueError as error:
255
- raise ValueError(f"Cannot convert {ordinal!r} to integer") from error
256
- #
293
+ new_ordinal = trunc(ordinal)
257
294
  if not MIN_ORDINAL <= new_ordinal <= MAX_ORDINAL:
258
295
  raise OverflowError("RealDate value out of range")
259
296
  #
@@ -261,6 +298,20 @@ def fromordinal(ordinal: float | int) -> GenericDate:
261
298
  return from_datetime_object(stdlib_date_object)
262
299
 
263
300
 
301
+ # -----------------------------------------------------------------------------
302
+ # Public module constants continued:
303
+ # minimum and maximum real dates
304
+ # -----------------------------------------------------------------------------
305
+
306
+ REAL_MIN: Final[GenericDate] = fromordinal(MIN_ORDINAL)
307
+ REAL_MAX: Final[GenericDate] = fromordinal(MAX_ORDINAL)
308
+
309
+
310
+ # -----------------------------------------------------------------------------
311
+ # Module-level factory functions continued
312
+ # -----------------------------------------------------------------------------
313
+
314
+
264
315
  def fromisoformat(source: str, /) -> GenericDate:
265
316
  """Create an InfinityDate or RealDate instance from an iso format representation"""
266
317
  lower_source_stripped = source.strip().lower()
@@ -281,8 +332,3 @@ def fromisocalendar(year: int, week: int, weekday: int) -> GenericDate:
281
332
  def today() -> GenericDate:
282
333
  """Today as RealDate object"""
283
334
  return from_datetime_object(date.today())
284
-
285
-
286
- # Minimum and maximum real dates
287
- # setattr(RealDate, "min", fromordinal(MIN_ORDINAL))
288
- # setattr(RealDate, "max", fromordinal(MAX_ORDINAL))
@@ -6,12 +6,11 @@ Tests for the infdate module
6
6
 
7
7
  import datetime
8
8
 
9
- # import math
10
9
  import secrets
11
10
  import unittest
12
11
 
13
12
  from math import inf, isnan, nan
14
- from time import struct_time
13
+ from time import mktime, struct_time
15
14
  from unittest.mock import Mock, call, patch
16
15
 
17
16
  import infdate
@@ -19,6 +18,9 @@ import infdate
19
18
 
20
19
  MAX_ORDINAL = datetime.date.max.toordinal()
21
20
 
21
+ # This is an error message from Python itself
22
+ _NAN_INT_CONVERSION_ERROR_RE = "^cannot convert float NaN to integer$"
23
+
22
24
 
23
25
  def random_deterministic_date() -> infdate.GenericDate:
24
26
  """Helper function: create a random deterministic Date"""
@@ -33,14 +35,20 @@ class VerboseTestCase(unittest.TestCase):
33
35
  self.maxDiff = None # pylint: disable=invalid-name ; name from unittest module
34
36
 
35
37
 
36
- class GenericDate(VerboseTestCase):
37
- """GenericDate objects"""
38
+ class GenericDateBase(VerboseTestCase):
39
+ """GenericDate objects - base functionality"""
40
+
41
+ def test_nan_not_allowed(self):
42
+ """test initialization with nan"""
43
+ self.assertRaisesRegex(
44
+ ValueError, _NAN_INT_CONVERSION_ERROR_RE, infdate.GenericDate, nan
45
+ )
38
46
 
39
47
  def test_toordinal(self):
40
48
  """initialization and .toordinal() method"""
41
49
  num = 1.23
42
50
  gd = infdate.GenericDate(num)
43
- self.assertEqual(gd.toordinal(), num)
51
+ self.assertEqual(gd.toordinal(), 1)
44
52
 
45
53
  # pylint: disable=comparison-with-itself ; to show lt/gt ↔ le/ge difference
46
54
 
@@ -175,6 +183,8 @@ class GenericDate(VerboseTestCase):
175
183
  self.assertTrue(random_date == random_date)
176
184
  #
177
185
 
186
+ # pylint: enable=comparison-with-itself
187
+
178
188
  def test_bool(self):
179
189
  """bool(gd_instance) capability"""
180
190
  gd = infdate.GenericDate(3.579)
@@ -183,7 +193,61 @@ class GenericDate(VerboseTestCase):
183
193
  def test_hash(self):
184
194
  """bool(gd_instance) capability"""
185
195
  gd = infdate.GenericDate(0.5)
186
- self.assertEqual(hash(gd), hash("date with ordinal 0.5"))
196
+ self.assertEqual(hash(gd), hash("date with ordinal 0"))
197
+
198
+ def test_repr(self):
199
+ """repr(gd_instance) capability"""
200
+ for base, expected_display in (
201
+ (inf, "inf"),
202
+ (-inf, "-inf"),
203
+ (9.81, "9"),
204
+ (314, "314"),
205
+ ):
206
+ gd = infdate.GenericDate(base)
207
+ with self.subTest(
208
+ "representation of",
209
+ base=base,
210
+ expected_display=expected_display,
211
+ ):
212
+ self.assertEqual(repr(gd), f"GenericDate({expected_display})")
213
+ #
214
+ #
215
+
216
+ def test_str(self):
217
+ """str(gd_instance) capability"""
218
+ gd = infdate.GenericDate(777)
219
+ mocked_isoformat_result = "[777]"
220
+ with patch.object(gd, "isoformat") as mock_isoformat:
221
+ mock_isoformat.return_value = mocked_isoformat_result
222
+ result = str(gd)
223
+ self.assertEqual(result, mocked_isoformat_result)
224
+ mock_isoformat.assert_called_with()
225
+ #
226
+
227
+ def test_isoformat(self):
228
+ """.isoformat() method"""
229
+ gd = infdate.GenericDate(777)
230
+ mocked_strftime_result = "[777]"
231
+ with patch.object(gd, "strftime") as mock_strftime:
232
+ mock_strftime.return_value = mocked_strftime_result
233
+ result = str(gd)
234
+ self.assertEqual(result, mocked_strftime_result)
235
+ mock_strftime.assert_called_with(infdate.ISO_DATE_FORMAT)
236
+ #
237
+
238
+ def test_strftime(self):
239
+ """.strftime() method"""
240
+ gd = infdate.GenericDate(-inf)
241
+ self.assertRaises(NotImplementedError, gd.strftime, "")
242
+
243
+ def test_replace(self):
244
+ """.replace() method"""
245
+ gd = infdate.GenericDate(inf)
246
+ self.assertRaises(NotImplementedError, gd.replace, year=1)
247
+
248
+
249
+ class GenericDateArithmetics(VerboseTestCase):
250
+ """GenericDate objects - arithmetics"""
187
251
 
188
252
  # pylint: disable=protected-access ; ok for testing
189
253
 
@@ -198,10 +262,10 @@ class GenericDate(VerboseTestCase):
198
262
  (-inf, 77.98, -inf, None, True),
199
263
  (9.81, inf, inf, inf, False),
200
264
  (9.81, -inf, -inf, -inf, False),
201
- (1.234, 7.89, 1.234 + 7.89, 1.234 + 7.89, False),
202
- (7.62, 0, 7.62, None, True),
203
- (1.234, -5.678, 1.234 - 5.678, 1.234 - 5.678, False),
204
- (3.14, -0.0, 3.14, None, True),
265
+ (1.234, 7.89, 8, 8, False),
266
+ (7.62, 0, 7, None, True),
267
+ (1.234, -5.678, -4, -4, False),
268
+ (3.14, -0, 3, None, True),
205
269
  ):
206
270
  with patch.object(infdate, "fromordinal") as mock_fromordinal:
207
271
  gd = infdate.GenericDate(base)
@@ -239,7 +303,7 @@ class GenericDate(VerboseTestCase):
239
303
  #
240
304
  #
241
305
 
242
- def test_add(self):
306
+ def test_add_and_radd(self):
243
307
  """gd_instance + delta capability"""
244
308
  for base, delta in (
245
309
  (inf, inf),
@@ -259,9 +323,13 @@ class GenericDate(VerboseTestCase):
259
323
  with patch.object(gd, "_add_days") as mock_adder:
260
324
  _ = gd + delta
261
325
  with self.subTest(
262
- "._add_days() call with",
263
- base=base,
264
- delta=delta,
326
+ f"GenericDate({base}) + {delta} → Gen…()._add_days({delta}) call"
327
+ ):
328
+ mock_adder.assert_called_with(delta)
329
+ #
330
+ _ = delta + gd
331
+ with self.subTest(
332
+ f"{delta} + GenericDate({base}) → Gen…()._add_days({delta}) call"
265
333
  ):
266
334
  mock_adder.assert_called_with(delta)
267
335
  #
@@ -279,10 +347,10 @@ class GenericDate(VerboseTestCase):
279
347
  (-inf, 77.98, -inf, None, True),
280
348
  (9.81, -inf, inf, inf, False),
281
349
  (9.81, inf, -inf, -inf, False),
282
- (1.234, -7.89, 1.234 + 7.89, 7.89, False),
283
- (7.62, -0, 7.62, None, True),
284
- (1.234, 5.678, 1.234 - 5.678, -5.678, False),
285
- (3.14, 0.0, 3.14, None, True),
350
+ (1.234, -7.89, 8, 7.89, False),
351
+ (7.62, -0, 7, None, True),
352
+ (1.234, 5.678, -4, -5.678, False),
353
+ (3.14, 0.0, 3, None, True),
286
354
  ):
287
355
  gd = infdate.GenericDate(base)
288
356
  with patch.object(gd, "_add_days") as mock_adder:
@@ -335,10 +403,10 @@ class GenericDate(VerboseTestCase):
335
403
  (-inf, 77.98, -inf),
336
404
  (9.81, -inf, inf),
337
405
  (9.81, inf, -inf),
338
- (1.234, -7.89, 1.234 + 7.89),
339
- (7.62, -0, 7.62),
340
- (1.234, 5.678, 1.234 - 5.678),
341
- (3.14, 0.0, 3.14),
406
+ (1.234, -7.89, 8),
407
+ (7.62, -0, 7),
408
+ (1.234, 5.678, -4),
409
+ (3.14, 0.0, 3),
342
410
  ):
343
411
  gd1 = infdate.GenericDate(first)
344
412
  gd2 = infdate.GenericDate(second)
@@ -357,57 +425,51 @@ class GenericDate(VerboseTestCase):
357
425
  #
358
426
  #
359
427
 
360
- def test_repr(self):
361
- """repr(gd_instance) capability"""
362
- for base, expected_display in (
363
- (inf, "inf"),
364
- (-inf, "-inf"),
365
- (9.81, "9.81"),
366
- (nan, "nan"),
367
- (314, "314"),
428
+ def test_sub_stdlib_date(self):
429
+ """gd_instance - stdlib_date capability"""
430
+ for gd_ordinal, stdlib_date_ordinal, expected_result in (
431
+ (inf, 981, inf),
432
+ (-inf, 733981, -inf),
433
+ (12, 1234, -1222),
434
+ (762, 1, 761),
435
+ (99999.99, 7777, 92222),
368
436
  ):
369
- gd = infdate.GenericDate(base)
437
+ gd_instance = infdate.GenericDate(gd_ordinal)
438
+ stdlib_date = datetime.date.fromordinal(stdlib_date_ordinal)
439
+ result = gd_instance - stdlib_date
370
440
  with self.subTest(
371
- "representation of",
372
- base=base,
373
- expected_display=expected_display,
441
+ "result",
442
+ gd_ordinal=gd_ordinal,
443
+ stdlib_date_ordinal=stdlib_date_ordinal,
444
+ expected_result=expected_result,
374
445
  ):
375
- self.assertEqual(repr(gd), f"GenericDate({expected_display})")
446
+ self.assertEqual(result, expected_result)
376
447
  #
377
448
  #
378
449
 
379
- def test_str(self):
380
- """str(gd_instance) capability"""
381
- gd = infdate.GenericDate(777)
382
- mocked_isoformat_result = "[777]"
383
- with patch.object(gd, "isoformat") as mock_isoformat:
384
- mock_isoformat.return_value = mocked_isoformat_result
385
- result = str(gd)
386
- self.assertEqual(result, mocked_isoformat_result)
387
- mock_isoformat.assert_called_with()
388
- #
389
-
390
- def test_isoformat(self):
391
- """.isoformat() method"""
392
- gd = infdate.GenericDate(777)
393
- mocked_strftime_result = "[777]"
394
- with patch.object(gd, "strftime") as mock_strftime:
395
- mock_strftime.return_value = mocked_strftime_result
396
- result = str(gd)
397
- self.assertEqual(result, mocked_strftime_result)
398
- mock_strftime.assert_called_with(infdate.ISO_DATE_FORMAT)
450
+ def test_rsub_stdlib_date(self):
451
+ """stdlib_date - gd_instance capability"""
452
+ for stdlib_date_ordinal, gd_ordinal, expected_result in (
453
+ (981, -inf, inf),
454
+ (981, inf, -inf),
455
+ (1234, -7.89, 1241),
456
+ (762, -0, 762),
457
+ (1234, 5.678, 1229),
458
+ (314, 0.0, 314),
459
+ ):
460
+ stdlib_date = datetime.date.fromordinal(stdlib_date_ordinal)
461
+ gd_instance = infdate.GenericDate(gd_ordinal)
462
+ result = stdlib_date - gd_instance
463
+ with self.subTest(
464
+ "result",
465
+ stdlib_date_ordinal=stdlib_date_ordinal,
466
+ gd_ordinal=gd_ordinal,
467
+ expected_result=expected_result,
468
+ ):
469
+ self.assertEqual(result, expected_result)
470
+ #
399
471
  #
400
472
 
401
- def test_strftime(self):
402
- """.strftime() method"""
403
- gd = infdate.GenericDate(-inf)
404
- self.assertRaises(NotImplementedError, gd.strftime, "")
405
-
406
- def test_replace(self):
407
- """.replace() method"""
408
- gd = infdate.GenericDate(inf)
409
- self.assertRaises(NotImplementedError, gd.replace, year=1)
410
-
411
473
 
412
474
  class InfinityDate(VerboseTestCase):
413
475
  """InfinityDate class"""
@@ -607,6 +669,27 @@ class RealDate(VerboseTestCase):
607
669
  #
608
670
  #
609
671
 
672
+ def test_random_date_within_limits(self):
673
+ """The following relation
674
+ infdate.MIN < infdate.REAL_MIN <= real_date_instance <= …
675
+ … infdate.REAL_MAX < infdate.MAX
676
+ should always be True
677
+ """
678
+ for iteration in range(1, 10000):
679
+ random_date = random_deterministic_date()
680
+ with self.subTest(
681
+ "date within limits", iteration=iteration, random_date=random_date
682
+ ):
683
+ self.assertTrue(
684
+ infdate.MIN
685
+ < infdate.REAL_MIN
686
+ <= random_date
687
+ <= infdate.REAL_MAX
688
+ < infdate.MAX
689
+ )
690
+ #
691
+ #
692
+
610
693
 
611
694
  class FactoryFunctions(VerboseTestCase):
612
695
  """Factory functions in the module"""
@@ -683,7 +766,60 @@ class FactoryFunctions(VerboseTestCase):
683
766
  #
684
767
  #
685
768
 
686
- @patch("infdate.datetime")
769
+ @patch("infdate.date")
770
+ @patch.object(infdate, "from_datetime_object")
771
+ def test_fromtimestamp(self, mock_factory, mock_date):
772
+ """test_fromtimestamp() factory function"""
773
+ for timestamp, expected_result in (
774
+ (-inf, infdate.MIN),
775
+ (inf, infdate.MAX),
776
+ (1e500, infdate.MAX),
777
+ ):
778
+ with self.subTest(
779
+ "infinity", timestamp=timestamp, expected_result=expected_result
780
+ ):
781
+ self.assertIs(infdate.fromtimestamp(timestamp), expected_result)
782
+ #
783
+ #
784
+ for stdlib_date in (
785
+ datetime.date.min,
786
+ datetime.date(1000, 1, 1),
787
+ datetime.date(2022, 2, 22),
788
+ datetime.date.max,
789
+ ):
790
+ timestamp = mktime(stdlib_date.timetuple())
791
+ with self.subTest(
792
+ "regular_result", timestamp=timestamp, stdlib_date=stdlib_date
793
+ ):
794
+ mocked_final_result = Mock(
795
+ year=stdlib_date.year, month=stdlib_date.month, day=stdlib_date.day
796
+ )
797
+ mock_date.fromtimestamp.return_value = stdlib_date
798
+ mock_factory.return_value = mocked_final_result
799
+ self.assertEqual(infdate.fromtimestamp(timestamp), mocked_final_result)
800
+ mock_date.fromtimestamp.assert_called_with(timestamp)
801
+ mock_factory.assert_called_with(stdlib_date)
802
+ #
803
+ #
804
+
805
+ def test_fromtimestamp_errors(self):
806
+ """test_fromtimestamp() factory function errors"""
807
+ for timestamp in (
808
+ mktime(datetime.date.min.timetuple()) - 1,
809
+ mktime(datetime.date.max.timetuple()) + 86400,
810
+ ):
811
+ with self.subTest(
812
+ "unsupported date, re-raised value error", timestamp=timestamp
813
+ ):
814
+ self.assertRaises(
815
+ ValueError,
816
+ infdate.fromtimestamp,
817
+ timestamp,
818
+ )
819
+ #
820
+ #
821
+
822
+ @patch("infdate.date")
687
823
  @patch.object(infdate, "from_datetime_object")
688
824
  def test_fromordinal(self, mock_factory, mock_date):
689
825
  """fromordinal() factory function"""
@@ -709,7 +845,7 @@ class FactoryFunctions(VerboseTestCase):
709
845
  mock_date.fromordinal.return_value = intermediate_result
710
846
  mock_factory.return_value = mocked_final_result
711
847
  self.assertEqual(infdate.fromordinal(ordinal), mocked_final_result)
712
- # mock_date.fromordinal.assert_called_with(int(ordinal))
848
+ mock_date.fromordinal.assert_called_with(int(ordinal))
713
849
  mock_factory.assert_called_with(intermediate_result)
714
850
  #
715
851
  #
@@ -718,9 +854,9 @@ class FactoryFunctions(VerboseTestCase):
718
854
  (-1, OverflowError, default_overflow_re),
719
855
  (0, OverflowError, default_overflow_re),
720
856
  (1234567890, OverflowError, default_overflow_re),
721
- (nan, ValueError, "^Cannot convert nan to integer$"),
857
+ (nan, ValueError, _NAN_INT_CONVERSION_ERROR_RE),
722
858
  ):
723
- with self.subTest("overflow error", ordinal=nan):
859
+ with self.subTest("overflow error", ordinal=ordinal):
724
860
  self.assertRaisesRegex(
725
861
  exception_class,
726
862
  error_re,
infdate-0.2.0/PKG-INFO DELETED
@@ -1,141 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: infdate
3
- Version: 0.2.0
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
- Class hierarchy:
32
-
33
- └── GenericDate
34
- ├── InfinityDate
35
- └── RealDate
36
-
37
-
38
-
39
- **InfinityDate** can represent either positive or negative infinity.
40
- The module-level constants **MIN** and **MAX** contain the two possible
41
- **InfinityDate** instance variations.
42
-
43
- **RealDate** instances represent real dates like the standard library’s
44
- **datetime.date** class, with mostly equal or similöar semantics.
45
-
46
- For any valid **RealDate** instance, the following is **True**:
47
-
48
- ``` python
49
- infdate.MIN < infdate.RealDate(1, 1, 1) <= real_date_instance <= infdate.RealDate(9999, 12, 31) < infdate.MAX
50
- ```
51
-
52
- The following factory methods from the **datetime.date** class
53
- are provided as module-level functions:
54
-
55
- * **fromordinal()** (also accepting **-math.inf** or **math.inf**)
56
- * **fromisoformat()**
57
- * **fromisocalendar()**
58
- * **today()**
59
-
60
- **fromtimestamp()** is still missing by mistake.
61
-
62
- Two additional factory functions are provided in the module:
63
-
64
- * **from_datetime_object()** to create a **RealDate** instance from a
65
- **datetime.date** or **datetime.datetime** instance
66
-
67
- * **from_native_type()** to create an **InfinityDate** or **RealDate**
68
- instance from a string, from **None**, **-math.inf** or **math.inf**.
69
-
70
- This can come handy when dealing with API representations of dates,
71
- eg. in GitLab’s [Personal Access Tokens API].
72
-
73
- Some notable difference from the **datetime.date** class:
74
-
75
- * The **.toordinal()** method returns **float** instead of **int**
76
-
77
- * The **resolution** attribute is **1.0** instead of **datetime.timedelta(days=1)**
78
- but also represents exactly one day.
79
-
80
- * Subtracting a date from an **InfinityDate** or **RealDate** always returns a float
81
- (because **math.inf** is a float), not a **datetime.timedelta** instance.
82
-
83
- * Likewise, you cannot add or subtract **datetime.timedelta** instances
84
- from n **InfinityDate** or **RealDate**, only **loat** or **int**.
85
-
86
-
87
- ## Example usage
88
-
89
- ``` pycon
90
- >>> import infdate
91
- >>> today = infdate.today()
92
- >>> today
93
- RealDate(2025, 6, 25)
94
- >>> print(today)
95
- 2025-06-25
96
- >>> print(f"US date notation {today:%m/%d/%y}")
97
- US date notation 06/25/25
98
- >>> today.ctime()
99
- 'Wed Jun 25 00:00:00 2025'
100
- >>> today.isocalendar()
101
- datetime.IsoCalendarDate(year=2025, week=26, weekday=3)
102
- >>>
103
- >>> yesterday = today - 1
104
- >>> yesterday.ctime()
105
- 'Tue Jun 24 00:00:00 2025'
106
- >>>
107
- >>> today - yesterday
108
- 1.0
109
- >>> infdate.MIN
110
- InfinityDate(past_bound=True)
111
- >>> infdate.MAX
112
- InfinityDate(past_bound=False)
113
- >>> infdate.MAX - today
114
- inf
115
- >>> infdate.MAX - infdate.MIN
116
- inf
117
- ```
118
-
119
- You can compare **InfinityDate**, **RealDate** and **datetime.date** instances,
120
- and subtract them from each other (although currently, `__rsub__` is not implemented yet,
121
- so subtracting an **InfinityDate** or **RealDate** from a **datetime.date**
122
- still gives a **TypeError**):
123
-
124
- >>> from datetime import date
125
- >>> stdlib_today = date.today()
126
- >>> today == stdlib_today
127
- True
128
- >>> yesterday < stdlib_today
129
- True
130
- >>> yesterday - stdlib_today
131
- -1.0
132
- >>> stdlib_today - yesterday
133
- Traceback (most recent call last):
134
- File "<python-input-22>", line 1, in <module>
135
- stdlib_today - yesterday
136
- ~~~~~~~~~~~~~^~~~~~~~~~~
137
- TypeError: unsupported operand type(s) for -: 'datetime.date' and 'RealDate'
138
-
139
-
140
- * * *
141
- [Personal Access Tokens API]: https://docs.gitlab.com/api/personal_access_tokens/
infdate-0.2.0/README.md DELETED
@@ -1,117 +0,0 @@
1
- # infdate
2
-
3
- _Python module for date calculations implementing a concept of infinity_
4
-
5
- ## Module description
6
-
7
- Class hierarchy:
8
-
9
- └── GenericDate
10
- ├── InfinityDate
11
- └── RealDate
12
-
13
-
14
-
15
- **InfinityDate** can represent either positive or negative infinity.
16
- The module-level constants **MIN** and **MAX** contain the two possible
17
- **InfinityDate** instance variations.
18
-
19
- **RealDate** instances represent real dates like the standard library’s
20
- **datetime.date** class, with mostly equal or similöar semantics.
21
-
22
- For any valid **RealDate** instance, the following is **True**:
23
-
24
- ``` python
25
- infdate.MIN < infdate.RealDate(1, 1, 1) <= real_date_instance <= infdate.RealDate(9999, 12, 31) < infdate.MAX
26
- ```
27
-
28
- The following factory methods from the **datetime.date** class
29
- are provided as module-level functions:
30
-
31
- * **fromordinal()** (also accepting **-math.inf** or **math.inf**)
32
- * **fromisoformat()**
33
- * **fromisocalendar()**
34
- * **today()**
35
-
36
- **fromtimestamp()** is still missing by mistake.
37
-
38
- Two additional factory functions are provided in the module:
39
-
40
- * **from_datetime_object()** to create a **RealDate** instance from a
41
- **datetime.date** or **datetime.datetime** instance
42
-
43
- * **from_native_type()** to create an **InfinityDate** or **RealDate**
44
- instance from a string, from **None**, **-math.inf** or **math.inf**.
45
-
46
- This can come handy when dealing with API representations of dates,
47
- eg. in GitLab’s [Personal Access Tokens API].
48
-
49
- Some notable difference from the **datetime.date** class:
50
-
51
- * The **.toordinal()** method returns **float** instead of **int**
52
-
53
- * The **resolution** attribute is **1.0** instead of **datetime.timedelta(days=1)**
54
- but also represents exactly one day.
55
-
56
- * Subtracting a date from an **InfinityDate** or **RealDate** always returns a float
57
- (because **math.inf** is a float), not a **datetime.timedelta** instance.
58
-
59
- * Likewise, you cannot add or subtract **datetime.timedelta** instances
60
- from n **InfinityDate** or **RealDate**, only **loat** or **int**.
61
-
62
-
63
- ## Example usage
64
-
65
- ``` pycon
66
- >>> import infdate
67
- >>> today = infdate.today()
68
- >>> today
69
- RealDate(2025, 6, 25)
70
- >>> print(today)
71
- 2025-06-25
72
- >>> print(f"US date notation {today:%m/%d/%y}")
73
- US date notation 06/25/25
74
- >>> today.ctime()
75
- 'Wed Jun 25 00:00:00 2025'
76
- >>> today.isocalendar()
77
- datetime.IsoCalendarDate(year=2025, week=26, weekday=3)
78
- >>>
79
- >>> yesterday = today - 1
80
- >>> yesterday.ctime()
81
- 'Tue Jun 24 00:00:00 2025'
82
- >>>
83
- >>> today - yesterday
84
- 1.0
85
- >>> infdate.MIN
86
- InfinityDate(past_bound=True)
87
- >>> infdate.MAX
88
- InfinityDate(past_bound=False)
89
- >>> infdate.MAX - today
90
- inf
91
- >>> infdate.MAX - infdate.MIN
92
- inf
93
- ```
94
-
95
- You can compare **InfinityDate**, **RealDate** and **datetime.date** instances,
96
- and subtract them from each other (although currently, `__rsub__` is not implemented yet,
97
- so subtracting an **InfinityDate** or **RealDate** from a **datetime.date**
98
- still gives a **TypeError**):
99
-
100
- >>> from datetime import date
101
- >>> stdlib_today = date.today()
102
- >>> today == stdlib_today
103
- True
104
- >>> yesterday < stdlib_today
105
- True
106
- >>> yesterday - stdlib_today
107
- -1.0
108
- >>> stdlib_today - yesterday
109
- Traceback (most recent call last):
110
- File "<python-input-22>", line 1, in <module>
111
- stdlib_today - yesterday
112
- ~~~~~~~~~~~~~^~~~~~~~~~~
113
- TypeError: unsupported operand type(s) for -: 'datetime.date' and 'RealDate'
114
-
115
-
116
- * * *
117
- [Personal Access Tokens API]: https://docs.gitlab.com/api/personal_access_tokens/
@@ -1,8 +0,0 @@
1
- {
2
- "ci_pipeline_created_at": "2025-06-25T15:48:59Z",
3
- "ci_pipeline_id": "1889338569",
4
- "ci_pipeline_url": "https://gitlab.com/blackstream-x/infdate/-/pipelines/1889338569",
5
- "ci_project_title": "infdate",
6
- "ci_project_url": "https://gitlab.com/blackstream-x/infdate",
7
- "ci_commit_sha": "2f339a6673ac21ac72292598799f6c602866afaf"
8
- }
@@ -1 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="33" time="0.768" timestamp="2025-06-25T15:49:40.687820+00:00" hostname="runner-xxurkrix-project-71015407-concurrent-0"><testcase classname="tests.test_infdate.GenericDate" name="test_add" time="0.009" /><testcase classname="tests.test_infdate.GenericDate" name="test_add_days" time="0.005" /><testcase classname="tests.test_infdate.GenericDate" name="test_bool" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_eq" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_ge" time="0.055" /><testcase classname="tests.test_infdate.GenericDate" name="test_gt" time="0.046" /><testcase classname="tests.test_infdate.GenericDate" name="test_hash" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_isoformat" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_le" time="0.055" /><testcase classname="tests.test_infdate.GenericDate" name="test_lt" time="0.045" /><testcase classname="tests.test_infdate.GenericDate" name="test_ne" time="0.045" /><testcase classname="tests.test_infdate.GenericDate" name="test_replace" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_repr" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_str" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_strftime" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_sub_date" time="0.001" /><testcase classname="tests.test_infdate.GenericDate" name="test_sub_number" time="0.004" /><testcase classname="tests.test_infdate.GenericDate" name="test_toordinal" time="0.001" /><testcase classname="tests.test_infdate.InfinityDate" name="test_replace" time="0.001" /><testcase classname="tests.test_infdate.InfinityDate" name="test_repr" time="0.001" /><testcase classname="tests.test_infdate.InfinityDate" name="test_strftime" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_attributes" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_bool" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_proxied_methods" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_replace" time="0.002" /><testcase classname="tests.test_infdate.RealDate" name="test_repr" time="0.001" /><testcase classname="tests.test_infdate.RealDate" name="test_strftime" time="0.003" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_from_datetime_object" time="0.002" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_from_native_type" time="0.005" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromisocalendar" time="0.003" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromisoformat" time="0.002" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_fromordinal" time="0.002" /><testcase classname="tests.test_infdate.FactoryFunctions" name="test_today" time="0.002" /></testsuite></testsuites>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes