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 +165 -0
- infdate-0.2.2/README.md +140 -0
- {infdate-0.2.0 → infdate-0.2.2}/pyproject.toml +3 -2
- infdate-0.2.2/reports/docs/build-info.json +8 -0
- {infdate-0.2.0 → infdate-0.2.2}/reports/docs/build-info.md +2 -2
- infdate-0.2.2/reports/test-junit.xml +1 -0
- {infdate-0.2.0 → infdate-0.2.2}/src/infdate/__init__.py +71 -25
- {infdate-0.2.0 → infdate-0.2.2}/tests/test_infdate.py +205 -69
- infdate-0.2.0/PKG-INFO +0 -141
- infdate-0.2.0/README.md +0 -117
- infdate-0.2.0/reports/docs/build-info.json +0 -8
- infdate-0.2.0/reports/test-junit.xml +0 -1
- {infdate-0.2.0 → infdate-0.2.2}/.gitignore +0 -0
- {infdate-0.2.0 → infdate-0.2.2}/.gitlab-ci.yml +0 -0
- {infdate-0.2.0 → infdate-0.2.2}/LICENSE +0 -0
- {infdate-0.2.0 → infdate-0.2.2}/src/infdate/py.typed +0 -0
- {infdate-0.2.0 → infdate-0.2.2}/tests/__init__.py +0 -0
- {infdate-0.2.0 → infdate-0.2.2}/tools/ci_get_pypi_token.py +0 -0
- {infdate-0.2.0 → infdate-0.2.2}/tools/codestyle.sh +0 -0
- {infdate-0.2.0 → infdate-0.2.2}/tools/pytest.sh +0 -0
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
|
infdate-0.2.2/README.md
ADDED
@@ -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.
|
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.
|
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
|
-
[
|
8
|
-
(build started 2025-06-
|
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
|
-
|
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
|
-
|
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__(
|
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
|
-
#
|
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
|
-
#
|
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
|
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
|
-
|
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
|
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(),
|
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
|
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,
|
202
|
-
(7.62, 0, 7
|
203
|
-
(1.234, -5.678,
|
204
|
-
(3.14, -0
|
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
|
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
|
263
|
-
|
264
|
-
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,
|
283
|
-
(7.62, -0, 7
|
284
|
-
(1.234, 5.678,
|
285
|
-
(3.14, 0.0, 3
|
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,
|
339
|
-
(7.62, -0, 7
|
340
|
-
(1.234, 5.678,
|
341
|
-
(3.14, 0.0, 3
|
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
|
361
|
-
"""
|
362
|
-
for
|
363
|
-
(inf,
|
364
|
-
(-inf,
|
365
|
-
(
|
366
|
-
(
|
367
|
-
(
|
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
|
-
|
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
|
-
"
|
372
|
-
|
373
|
-
|
441
|
+
"result",
|
442
|
+
gd_ordinal=gd_ordinal,
|
443
|
+
stdlib_date_ordinal=stdlib_date_ordinal,
|
444
|
+
expected_result=expected_result,
|
374
445
|
):
|
375
|
-
self.assertEqual(
|
446
|
+
self.assertEqual(result, expected_result)
|
376
447
|
#
|
377
448
|
#
|
378
449
|
|
379
|
-
def
|
380
|
-
"""
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
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.
|
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
|
-
|
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,
|
857
|
+
(nan, ValueError, _NAN_INT_CONVERSION_ERROR_RE),
|
722
858
|
):
|
723
|
-
with self.subTest("overflow error", ordinal=
|
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
|
File without changes
|