python-utils 4.2.1__py3-none-any.whl → 4.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
python_utils/time.py CHANGED
@@ -110,6 +110,49 @@ def delta_to_seconds_or_none(
110
110
  return delta_to_seconds(interval)
111
111
 
112
112
 
113
+ def _format_wall_clock(
114
+ timestamp: datetime.datetime, precision: datetime.timedelta
115
+ ) -> str | None:
116
+ """Truncate a naive datetime by exact arithmetic, None when it cannot.
117
+
118
+ Windows cannot convert pre-epoch naive dates to timestamps, and no
119
+ platform converts the datetime bounds. Wall-clock arithmetic relative
120
+ to the naive epoch handles both. Aware datetimes, subclasses (whose
121
+ own arithmetic and ``__str__`` may differ), precisions that are not a
122
+ usable timedelta and results outside the datetime range give None, so
123
+ the caller keeps the 4.2.1 outcome for them.
124
+ """
125
+ if (
126
+ type(timestamp) is not datetime.datetime
127
+ or timestamp.tzinfo is not None
128
+ ):
129
+ return None
130
+ try:
131
+ return str(timestamp - (timestamp - epoch) % precision)
132
+ except (ArithmeticError, TypeError, ValueError):
133
+ return None
134
+
135
+
136
+ def _format_datetime(
137
+ timestamp: datetime.datetime, precision: datetime.timedelta
138
+ ) -> str:
139
+ """Format a datetime even outside the platform's timestamp range."""
140
+ try:
141
+ since_epoch = datetime.timedelta(seconds=timestamp.timestamp())
142
+ seconds = (since_epoch - since_epoch % precision).total_seconds()
143
+ except (OverflowError, OSError, ValueError):
144
+ # 4.2.1 raised here, which stays the answer without a wall clock.
145
+ wall_clock: str | None = _format_wall_clock(timestamp, precision)
146
+ if wall_clock is None:
147
+ raise
148
+ return wall_clock
149
+ try:
150
+ return str(datetime.datetime.fromtimestamp(seconds))
151
+ except (OverflowError, OSError, ValueError):
152
+ wall_clock = _format_wall_clock(timestamp, precision)
153
+ return str(datetime.datetime.max) if wall_clock is None else wall_clock
154
+
155
+
113
156
  def format_time(
114
157
  timestamp: _aliases.timestamp_type,
115
158
  precision: datetime.timedelta = datetime.timedelta(seconds=1),
@@ -124,6 +167,8 @@ def format_time(
124
167
  '0:00:01'
125
168
  >>> format_time(datetime.datetime(2000, 1, 2, 3, 4, 5, 6))
126
169
  '2000-01-02 03:04:05'
170
+ >>> format_time(datetime.datetime(1900, 1, 2, 3, 4, 5))
171
+ '1900-01-02 03:04:05'
127
172
  >>> format_time(datetime.date(2000, 1, 2))
128
173
  '2000-01-02'
129
174
  >>> format_time(datetime.timedelta(seconds=3661))
@@ -152,23 +197,8 @@ def format_time(
152
197
  # whole microseconds, which keeps the modulo exact. In float seconds
153
198
  # `1.0 % 0.1` is just under 0.1 and the result drops a whole step.
154
199
  return str(timestamp - timestamp % precision)
155
- elif isinstance(timestamp, datetime.datetime): # pragma: no cover
156
- # Python 2 doesn't have the timestamp method
157
- if hasattr(timestamp, 'timestamp'):
158
- seconds = timestamp.timestamp()
159
- else:
160
- seconds = timedelta_to_seconds(timestamp - epoch)
161
-
162
- # Truncate the number to the given precision, in whole microseconds
163
- # for the same reason as above
164
- since_epoch: datetime.timedelta = datetime.timedelta(seconds=seconds)
165
- seconds = (since_epoch - since_epoch % precision).total_seconds()
166
-
167
- try: # pragma: no cover
168
- dt = datetime.datetime.fromtimestamp(seconds)
169
- except (ValueError, OSError): # pragma: no cover
170
- dt = datetime.datetime.max
171
- return str(dt)
200
+ elif isinstance(timestamp, datetime.datetime):
201
+ return _format_datetime(timestamp, precision)
172
202
  elif isinstance(timestamp, datetime.date):
173
203
  return str(timestamp)
174
204
  elif timestamp is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-utils
3
- Version: 4.2.1
3
+ Version: 4.2.2
4
4
  Summary: Python Utils is a module with some convenient utilities not included with the standard Python install
5
5
  Keywords: utils,utilities,helpers,typing,typed,async,lazy-imports
6
6
  Author: Rick van Hattem
@@ -13,9 +13,9 @@ python_utils/logger.py,sha256=-QuF7Zfo7pxc4nTmAPc9Pm-oz5wsxXIX7NeCnYEPI6I,14043
13
13
  python_utils/loguru.py,sha256=qsap-aXByGXsd4KrYmtneTSUARVIdeVu9xgmrFPtqSI,2696
14
14
  python_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  python_utils/terminal.py,sha256=GL3n2_rT98LBNmxy-amY-hezGKStCo-9_d_pR-TMgv8,6823
16
- python_utils/time.py,sha256=fZz00bpTeLXFFhbAo03LiMLWuzSJU4iPbyGSe7PYshU,16979
16
+ python_utils/time.py,sha256=YjZAj8YDFfCRWqKKtCu0eeyMG0egdVRfsryeNbBnjdk,18137
17
17
  python_utils/types.py,sha256=VQLrKDRerOuhYxMhCaWWbUBnDXJhFx_G20gIfyolgug,4682
18
- python_utils-4.2.1.dist-info/licenses/LICENSE,sha256=_Zrs9NZu-G6_aZT2g3Jv5_JREJQfhRNBC61EH6WDH-o,1501
19
- python_utils-4.2.1.dist-info/WHEEL,sha256=3OmCxFA0ffBIsX4x0o8CdfSvZAOvO3mzEG8U_z5TvV4,80
20
- python_utils-4.2.1.dist-info/METADATA,sha256=pX5DW8A_mect5mjxcpNTkdETgjxcnEnhtoclf8Y825o,16105
21
- python_utils-4.2.1.dist-info/RECORD,,
18
+ python_utils-4.2.2.dist-info/licenses/LICENSE,sha256=_Zrs9NZu-G6_aZT2g3Jv5_JREJQfhRNBC61EH6WDH-o,1501
19
+ python_utils-4.2.2.dist-info/WHEEL,sha256=3OmCxFA0ffBIsX4x0o8CdfSvZAOvO3mzEG8U_z5TvV4,80
20
+ python_utils-4.2.2.dist-info/METADATA,sha256=aM0GImOcrZLXPo25hDyAGT9A1hYSGaxZuokTFPNH9EU,16105
21
+ python_utils-4.2.2.dist-info/RECORD,,