encommon 0.10.0__py3-none-any.whl → 0.11.0__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.
- encommon/config/__init__.py +3 -3
- encommon/config/config.py +17 -1
- encommon/config/files.py +3 -3
- encommon/config/logger.py +37 -6
- encommon/config/params.py +21 -1
- encommon/config/paths.py +2 -2
- encommon/config/test/test_logger.py +3 -0
- encommon/config/test/{test_common.py → test_utils.py} +3 -3
- encommon/config/{common.py → utils.py} +1 -10
- encommon/conftest.py +2 -1
- encommon/crypts/crypts.py +23 -22
- encommon/crypts/params.py +14 -1
- encommon/crypts/test/test_crypts.py +8 -20
- encommon/times/__init__.py +14 -2
- encommon/times/common.py +0 -127
- encommon/times/params.py +155 -0
- encommon/times/parse.py +5 -5
- encommon/times/test/test_params.py +64 -0
- encommon/times/test/test_parse.py +1 -1
- encommon/times/test/test_timer.py +86 -0
- encommon/times/test/test_timers.py +87 -36
- encommon/times/test/{test_common.py → test_utils.py} +3 -3
- encommon/times/test/test_window.py +101 -51
- encommon/times/test/test_windows.py +264 -0
- encommon/times/timer.py +147 -0
- encommon/times/timers.py +207 -133
- encommon/times/times.py +6 -6
- encommon/times/utils.py +148 -0
- encommon/times/window.py +124 -85
- encommon/times/windows.py +459 -0
- encommon/types/notate.py +0 -2
- encommon/utils/__init__.py +2 -2
- encommon/utils/common.py +0 -39
- encommon/utils/files.py +71 -0
- encommon/utils/paths.py +1 -1
- encommon/utils/sample.py +2 -2
- encommon/utils/test/{test_common.py → test_files.py} +2 -2
- encommon/utils/test/test_paths.py +2 -1
- encommon/version.txt +1 -1
- {encommon-0.10.0.dist-info → encommon-0.11.0.dist-info}/METADATA +1 -1
- encommon-0.11.0.dist-info/RECORD +73 -0
- encommon-0.10.0.dist-info/RECORD +0 -65
- {encommon-0.10.0.dist-info → encommon-0.11.0.dist-info}/LICENSE +0 -0
- {encommon-0.10.0.dist-info → encommon-0.11.0.dist-info}/WHEEL +0 -0
- {encommon-0.10.0.dist-info → encommon-0.11.0.dist-info}/top_level.txt +0 -0
encommon/times/timers.py
CHANGED
@@ -10,79 +10,89 @@ is permitted, for more information consult the project license file.
|
|
10
10
|
from sqlite3 import Connection
|
11
11
|
from sqlite3 import connect as SQLite
|
12
12
|
from typing import Optional
|
13
|
+
from typing import TYPE_CHECKING
|
13
14
|
|
14
|
-
from .common import NUMERIC
|
15
15
|
from .common import PARSABLE
|
16
|
+
from .timer import Timer
|
16
17
|
from .times import Times
|
17
18
|
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from .params import TimerParams
|
21
|
+
from .params import TimersParams
|
22
|
+
|
18
23
|
|
19
24
|
|
20
25
|
CACHE_TABLE = (
|
21
26
|
"""
|
22
27
|
create table if not exists
|
23
28
|
{0} (
|
29
|
+
"group" text not null,
|
24
30
|
"unique" text not null,
|
25
31
|
"update" text not null,
|
26
|
-
primary key (
|
32
|
+
primary key (
|
33
|
+
"group", "unique"));
|
27
34
|
""") # noqa: LIT003
|
28
35
|
|
29
36
|
|
30
37
|
|
31
|
-
|
32
|
-
_CACHED = dict[str, Times]
|
38
|
+
TIMERS = dict[str, Timer]
|
33
39
|
|
34
40
|
|
35
41
|
|
36
42
|
class Timers:
|
37
43
|
"""
|
38
|
-
Track timers on unique key
|
44
|
+
Track timers on unique key determining when to proceed.
|
39
45
|
|
40
46
|
.. warning::
|
41
47
|
This class will use an in-memory database for cache,
|
42
48
|
unless a cache file is explicity defined.
|
43
49
|
|
44
50
|
.. testsetup::
|
51
|
+
>>> from .params import TimerParams
|
52
|
+
>>> from .params import TimersParams
|
45
53
|
>>> from time import sleep
|
46
54
|
|
47
55
|
Example
|
48
56
|
-------
|
49
|
-
>>>
|
57
|
+
>>> source = {'one': TimerParams(timer=1)}
|
58
|
+
>>> params = TimersParams(timers=source)
|
59
|
+
>>> timers = Timers(params)
|
50
60
|
>>> timers.ready('one')
|
51
61
|
False
|
52
62
|
>>> sleep(1)
|
53
63
|
>>> timers.ready('one')
|
54
64
|
True
|
55
65
|
|
56
|
-
:param
|
57
|
-
:param file: Optional path to SQLite database
|
58
|
-
|
59
|
-
:param table: Optional override default table name.
|
66
|
+
:param params: Parameters for instantiating the instance.
|
67
|
+
:param file: Optional path to file for SQLite database,
|
68
|
+
allowing for state retention between the executions.
|
69
|
+
:param table: Optional override for default table name.
|
70
|
+
:param group: Optional override for default group name.
|
60
71
|
"""
|
61
72
|
|
62
|
-
|
73
|
+
__params: 'TimersParams'
|
74
|
+
|
63
75
|
__sqlite: Connection
|
64
76
|
__file: str
|
65
77
|
__table: str
|
66
|
-
|
78
|
+
__group: str
|
79
|
+
|
80
|
+
__timers: TIMERS
|
67
81
|
|
68
82
|
|
69
83
|
def __init__(
|
70
84
|
self,
|
71
|
-
|
85
|
+
params: 'TimersParams',
|
86
|
+
*,
|
72
87
|
file: str = ':memory:',
|
73
88
|
table: str = 'timers',
|
89
|
+
group: str = 'default',
|
74
90
|
) -> None:
|
75
91
|
"""
|
76
92
|
Initialize instance for class using provided parameters.
|
77
93
|
"""
|
78
94
|
|
79
|
-
|
80
|
-
timers = dict(timers or {})
|
81
|
-
|
82
|
-
items = timers.items()
|
83
|
-
|
84
|
-
for key, value in items:
|
85
|
-
timers[key] = float(value)
|
95
|
+
self.__params = params
|
86
96
|
|
87
97
|
|
88
98
|
sqlite = SQLite(file)
|
@@ -93,113 +103,71 @@ class Timers:
|
|
93
103
|
|
94
104
|
sqlite.commit()
|
95
105
|
|
96
|
-
|
97
|
-
cached: _CACHED = {}
|
98
|
-
|
99
|
-
for timer in timers:
|
100
|
-
cached[timer] = Times()
|
101
|
-
|
102
|
-
|
103
|
-
self.__config = timers
|
104
106
|
self.__sqlite = sqlite
|
105
107
|
self.__file = file
|
106
108
|
self.__table = table
|
107
|
-
self.
|
109
|
+
self.__group = group
|
110
|
+
|
108
111
|
|
112
|
+
self.__timers = {}
|
109
113
|
|
110
|
-
self.
|
111
|
-
self.save_cache()
|
114
|
+
self.load_children()
|
112
115
|
|
113
116
|
|
114
|
-
|
117
|
+
@property
|
118
|
+
def params(
|
115
119
|
self,
|
116
|
-
) ->
|
117
|
-
"""
|
118
|
-
Load the timers cache from the database into attribute.
|
120
|
+
) -> 'TimersParams':
|
119
121
|
"""
|
122
|
+
Return the Pydantic model containing the configuration.
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
cachem = self.__cache
|
124
|
-
|
125
|
-
cursor = cached.execute(
|
126
|
-
f'select * from {table}'
|
127
|
-
' order by "unique" asc')
|
128
|
-
|
129
|
-
records = cursor.fetchall()
|
130
|
-
|
131
|
-
for record in records:
|
132
|
-
|
133
|
-
unique = record[0]
|
134
|
-
update = record[1]
|
135
|
-
|
136
|
-
times = Times(update)
|
124
|
+
:returns: Pydantic model containing the configuration.
|
125
|
+
"""
|
137
126
|
|
138
|
-
|
127
|
+
return self.__params
|
139
128
|
|
140
129
|
|
141
|
-
|
130
|
+
@property
|
131
|
+
def sqlite(
|
142
132
|
self,
|
143
|
-
) ->
|
144
|
-
"""
|
145
|
-
Save the timers cache from the attribute into database.
|
133
|
+
) -> Connection:
|
146
134
|
"""
|
135
|
+
Return the value for the attribute from class instance.
|
147
136
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
cached = self.__sqlite
|
153
|
-
table = self.__table
|
154
|
-
cachem = self.__cache
|
155
|
-
|
156
|
-
|
157
|
-
items = cachem.items()
|
158
|
-
|
159
|
-
for key, value in items:
|
160
|
-
|
161
|
-
append = (key, str(value))
|
162
|
-
|
163
|
-
inserts.append(append)
|
164
|
-
|
165
|
-
|
166
|
-
cached.executemany(
|
167
|
-
(f'replace into {table}'
|
168
|
-
' ("unique", "update")'
|
169
|
-
' values (?, ?)'),
|
170
|
-
tuple(sorted(inserts)))
|
137
|
+
:returns: Value for the attribute from class instance.
|
138
|
+
"""
|
171
139
|
|
172
|
-
|
140
|
+
return self.__sqlite
|
173
141
|
|
174
142
|
|
175
143
|
@property
|
176
|
-
def
|
144
|
+
def file(
|
177
145
|
self,
|
178
|
-
) ->
|
146
|
+
) -> str:
|
179
147
|
"""
|
180
148
|
Return the value for the attribute from class instance.
|
181
149
|
|
182
150
|
:returns: Value for the attribute from class instance.
|
183
151
|
"""
|
184
152
|
|
185
|
-
return
|
153
|
+
return self.__file
|
186
154
|
|
187
155
|
|
188
156
|
@property
|
189
|
-
def
|
157
|
+
def table(
|
190
158
|
self,
|
191
|
-
) ->
|
159
|
+
) -> str:
|
192
160
|
"""
|
193
161
|
Return the value for the attribute from class instance.
|
194
162
|
|
195
163
|
:returns: Value for the attribute from class instance.
|
196
164
|
"""
|
197
165
|
|
198
|
-
return self.
|
166
|
+
return self.__table
|
199
167
|
|
200
168
|
|
201
169
|
@property
|
202
|
-
def
|
170
|
+
def group(
|
203
171
|
self,
|
204
172
|
) -> str:
|
205
173
|
"""
|
@@ -208,33 +176,130 @@ class Timers:
|
|
208
176
|
:returns: Value for the attribute from class instance.
|
209
177
|
"""
|
210
178
|
|
211
|
-
return self.
|
179
|
+
return self.__group
|
212
180
|
|
213
181
|
|
214
182
|
@property
|
215
|
-
def
|
183
|
+
def children(
|
216
184
|
self,
|
217
|
-
) -> str:
|
185
|
+
) -> dict[str, Timer]:
|
218
186
|
"""
|
219
187
|
Return the value for the attribute from class instance.
|
220
188
|
|
221
189
|
:returns: Value for the attribute from class instance.
|
222
190
|
"""
|
223
191
|
|
224
|
-
return self.
|
192
|
+
return dict(self.__timers)
|
225
193
|
|
226
194
|
|
227
|
-
|
228
|
-
def cache(
|
195
|
+
def load_children(
|
229
196
|
self,
|
230
|
-
) ->
|
197
|
+
) -> None:
|
198
|
+
"""
|
199
|
+
Construct the children instances for the primary class.
|
231
200
|
"""
|
232
|
-
Return the value for the attribute from class instance.
|
233
201
|
|
234
|
-
|
202
|
+
params = self.__params
|
203
|
+
timers = self.__timers
|
204
|
+
|
205
|
+
sqlite = self.__sqlite
|
206
|
+
table = self.__table
|
207
|
+
group = self.__group
|
208
|
+
|
209
|
+
|
210
|
+
config = params.timers
|
211
|
+
|
212
|
+
|
213
|
+
cursor = sqlite.execute(
|
214
|
+
f"""
|
215
|
+
select * from {table}
|
216
|
+
where "group"="{group}"
|
217
|
+
order by "unique" asc
|
218
|
+
""") # noqa: LIT003
|
219
|
+
|
220
|
+
records = cursor.fetchall()
|
221
|
+
|
222
|
+
for record in records:
|
223
|
+
|
224
|
+
unique = record[1]
|
225
|
+
update = record[2]
|
226
|
+
|
227
|
+
if unique not in config:
|
228
|
+
continue
|
229
|
+
|
230
|
+
_config = config[unique]
|
231
|
+
|
232
|
+
_config.start = update
|
233
|
+
|
234
|
+
|
235
|
+
items = config.items()
|
236
|
+
|
237
|
+
for key, value in items:
|
238
|
+
|
239
|
+
if key in timers:
|
240
|
+
|
241
|
+
timer = timers[key]
|
242
|
+
|
243
|
+
timer.update(
|
244
|
+
value.start)
|
245
|
+
|
246
|
+
continue
|
247
|
+
|
248
|
+
timer = Timer(
|
249
|
+
value.timer,
|
250
|
+
start=value.start)
|
251
|
+
|
252
|
+
timers[key] = timer
|
253
|
+
|
254
|
+
|
255
|
+
self.__timers = timers
|
256
|
+
|
257
|
+
|
258
|
+
def save_children(
|
259
|
+
self,
|
260
|
+
) -> None:
|
235
261
|
"""
|
262
|
+
Save the child caches from the attribute into database.
|
263
|
+
"""
|
264
|
+
|
265
|
+
timers = self.__timers
|
236
266
|
|
237
|
-
|
267
|
+
sqlite = self.__sqlite
|
268
|
+
table = self.__table
|
269
|
+
group = self.__group
|
270
|
+
|
271
|
+
|
272
|
+
insert = tuple[
|
273
|
+
str, # group
|
274
|
+
str, # unique
|
275
|
+
str] # update
|
276
|
+
|
277
|
+
inserts: list[insert] = []
|
278
|
+
|
279
|
+
items = timers.items()
|
280
|
+
|
281
|
+
for unique, timer in items:
|
282
|
+
|
283
|
+
append = (
|
284
|
+
group, unique,
|
285
|
+
Times('now').subsec)
|
286
|
+
|
287
|
+
inserts.append(append)
|
288
|
+
|
289
|
+
|
290
|
+
statement = (
|
291
|
+
f"""
|
292
|
+
replace into {table}
|
293
|
+
("group", "unique",
|
294
|
+
"update")
|
295
|
+
values (?, ?, ?)
|
296
|
+
""") # noqa: LIT003
|
297
|
+
|
298
|
+
sqlite.executemany(
|
299
|
+
statement,
|
300
|
+
tuple(sorted(inserts)))
|
301
|
+
|
302
|
+
sqlite.commit()
|
238
303
|
|
239
304
|
|
240
305
|
def ready(
|
@@ -245,74 +310,83 @@ class Timers:
|
|
245
310
|
"""
|
246
311
|
Determine whether or not the appropriate time has passed.
|
247
312
|
|
248
|
-
|
249
|
-
For performance reasons, this method will not notice
|
250
|
-
changes within the database unless refreshed first.
|
251
|
-
|
252
|
-
:param unique: Which timer configuration from reference.
|
313
|
+
:param unique: Unique identifier for the related child.
|
253
314
|
:param update: Determines whether or not time is updated.
|
315
|
+
:returns: Boolean indicating whether enough time passed.
|
254
316
|
"""
|
255
317
|
|
256
|
-
|
257
|
-
caches = self.__cache
|
318
|
+
timers = self.__timers
|
258
319
|
|
259
|
-
if unique not in
|
320
|
+
if unique not in timers:
|
260
321
|
raise ValueError('unique')
|
261
322
|
|
262
|
-
|
263
|
-
|
323
|
+
timer = timers[unique]
|
324
|
+
|
325
|
+
return timer.ready(update)
|
326
|
+
|
327
|
+
|
328
|
+
def create(
|
329
|
+
self,
|
330
|
+
unique: str,
|
331
|
+
params: 'TimerParams',
|
332
|
+
) -> Timer:
|
333
|
+
"""
|
334
|
+
Create a new timer using the provided input parameters.
|
335
|
+
|
336
|
+
:param unique: Unique identifier for the related child.
|
337
|
+
:param params: Parameters for instantiating the instance.
|
338
|
+
:returns: Newly constructed instance of related class.
|
339
|
+
"""
|
340
|
+
|
341
|
+
timers = self.params.timers
|
342
|
+
|
343
|
+
self.save_children()
|
264
344
|
|
265
|
-
|
345
|
+
if unique in timers:
|
346
|
+
raise ValueError('unique')
|
347
|
+
|
348
|
+
timers[unique] = params
|
266
349
|
|
267
|
-
|
268
|
-
self.update(unique)
|
350
|
+
self.load_children()
|
269
351
|
|
270
|
-
return
|
352
|
+
return self.children[unique]
|
271
353
|
|
272
354
|
|
273
355
|
def update(
|
274
356
|
self,
|
275
357
|
unique: str,
|
276
|
-
|
358
|
+
value: Optional[PARSABLE] = None,
|
277
359
|
) -> None:
|
278
360
|
"""
|
279
|
-
Update the
|
361
|
+
Update the timer from the provided parasable time value.
|
280
362
|
|
281
|
-
:param unique:
|
282
|
-
:param
|
363
|
+
:param unique: Unique identifier for the related child.
|
364
|
+
:param value: Override the time updated for timer value.
|
283
365
|
"""
|
284
366
|
|
285
|
-
|
367
|
+
timers = self.__timers
|
286
368
|
|
287
|
-
if unique not in
|
369
|
+
if unique not in timers:
|
288
370
|
raise ValueError('unique')
|
289
371
|
|
290
|
-
|
372
|
+
timer = timers[unique]
|
291
373
|
|
292
|
-
|
374
|
+
return timer.update(value)
|
293
375
|
|
294
376
|
|
295
|
-
def
|
377
|
+
def delete(
|
296
378
|
self,
|
297
379
|
unique: str,
|
298
|
-
minimum: int | float,
|
299
|
-
started: Optional[PARSABLE] = None,
|
300
380
|
) -> None:
|
301
381
|
"""
|
302
|
-
|
382
|
+
Delete the timer from the internal dictionary reference.
|
303
383
|
|
304
|
-
:param unique:
|
305
|
-
:param minimum: Determine minimum seconds that must pass.
|
306
|
-
:param started: Determine when time starts for the timer.
|
384
|
+
:param unique: Unique identifier for the related child.
|
307
385
|
"""
|
308
386
|
|
309
|
-
|
310
|
-
caches = self.__cache
|
387
|
+
timers = self.__timers
|
311
388
|
|
312
|
-
if unique in
|
389
|
+
if unique not in timers:
|
313
390
|
raise ValueError('unique')
|
314
391
|
|
315
|
-
|
316
|
-
caches[unique] = Times(started)
|
317
|
-
|
318
|
-
self.save_cache()
|
392
|
+
del timers[unique]
|
encommon/times/times.py
CHANGED
@@ -16,10 +16,10 @@ from .common import PARSABLE
|
|
16
16
|
from .common import STAMP_HUMAN
|
17
17
|
from .common import STAMP_SIMPLE
|
18
18
|
from .common import STAMP_SUBSEC
|
19
|
-
from .common import findtz
|
20
|
-
from .common import strftime
|
21
19
|
from .parse import parse_time
|
22
20
|
from .parse import since_time
|
21
|
+
from .utils import findtz
|
22
|
+
from .utils import strftime
|
23
23
|
|
24
24
|
|
25
25
|
|
@@ -348,9 +348,9 @@ class Times:
|
|
348
348
|
self,
|
349
349
|
) -> float:
|
350
350
|
"""
|
351
|
-
Determine the time in seconds
|
351
|
+
Determine the time in seconds occurring since instance.
|
352
352
|
|
353
|
-
:returns: Time in seconds
|
353
|
+
:returns: Time in seconds occurring since the instance.
|
354
354
|
"""
|
355
355
|
|
356
356
|
return since_time(self.__source)
|
@@ -361,9 +361,9 @@ class Times:
|
|
361
361
|
self,
|
362
362
|
) -> float:
|
363
363
|
"""
|
364
|
-
Determine the time in seconds
|
364
|
+
Determine the time in seconds occurring since instance.
|
365
365
|
|
366
|
-
:returns: Time in seconds
|
366
|
+
:returns: Time in seconds occurring since the instance.
|
367
367
|
"""
|
368
368
|
|
369
369
|
return since_time(self.__source)
|
encommon/times/utils.py
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
"""
|
2
|
+
Functions and routines associated with Enasis Network Common Library.
|
3
|
+
|
4
|
+
This file is part of Enasis Network software eco-system. Distribution
|
5
|
+
is permitted, for more information consult the project license file.
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
from contextlib import suppress
|
11
|
+
from datetime import datetime
|
12
|
+
from datetime import timezone
|
13
|
+
from datetime import tzinfo
|
14
|
+
from typing import Any
|
15
|
+
from typing import Optional
|
16
|
+
|
17
|
+
from dateutil.tz import gettz
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
def findtz(
|
22
|
+
tzname: Optional[str] = None,
|
23
|
+
) -> tzinfo:
|
24
|
+
"""
|
25
|
+
Return the located timezone object for the provided name.
|
26
|
+
|
27
|
+
Example
|
28
|
+
-------
|
29
|
+
>>> findtz('US/Central')
|
30
|
+
tzfile('/usr/share/zoneinfo/US/Central')
|
31
|
+
|
32
|
+
:param tzname: Name of the timezone associated to source.
|
33
|
+
:returns: Located timezone object for the provided name.
|
34
|
+
"""
|
35
|
+
|
36
|
+
if tzname is None:
|
37
|
+
return timezone.utc
|
38
|
+
|
39
|
+
tzinfo = gettz(tzname)
|
40
|
+
|
41
|
+
if tzinfo is None:
|
42
|
+
raise ValueError('tzname')
|
43
|
+
|
44
|
+
return tzinfo
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
def utcdatetime(
|
49
|
+
*args: Any,
|
50
|
+
**kwargs: Any,
|
51
|
+
) -> datetime:
|
52
|
+
"""
|
53
|
+
Return the instance of datetime within the UTC timezone.
|
54
|
+
|
55
|
+
.. warning::
|
56
|
+
If no arguments are provided, returns current time.
|
57
|
+
|
58
|
+
Example
|
59
|
+
-------
|
60
|
+
>>> utcdatetime(1970, 1, 1)
|
61
|
+
datetime.datetime(1970, 1, 1, 0...
|
62
|
+
|
63
|
+
:param args: Positional arguments passed for downstream.
|
64
|
+
:param kwargs: Keyword arguments passed for downstream.
|
65
|
+
:returns: Instance of datetime within the UTC timezone.
|
66
|
+
"""
|
67
|
+
|
68
|
+
tzinfo = timezone.utc
|
69
|
+
|
70
|
+
if not args and not kwargs:
|
71
|
+
return datetime.now(tz=tzinfo)
|
72
|
+
|
73
|
+
if 'tzinfo' not in kwargs:
|
74
|
+
kwargs['tzinfo'] = tzinfo
|
75
|
+
|
76
|
+
return (
|
77
|
+
datetime(*args, **kwargs)
|
78
|
+
.astimezone(timezone.utc))
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
def strptime(
|
83
|
+
source: str,
|
84
|
+
formats: str | list[str],
|
85
|
+
) -> datetime:
|
86
|
+
"""
|
87
|
+
Parse provided time value with various supported formats.
|
88
|
+
|
89
|
+
Example
|
90
|
+
-------
|
91
|
+
>>> strptime('2023', '%Y')
|
92
|
+
datetime.datetime(2023, 1, 1, 0...
|
93
|
+
|
94
|
+
:param source: Time in various forms that will be parsed.
|
95
|
+
:param formats: Various formats compatable with strptime.
|
96
|
+
:returns: Python datetime object containing related time.
|
97
|
+
"""
|
98
|
+
|
99
|
+
tzinfo = timezone.utc
|
100
|
+
|
101
|
+
if isinstance(formats, str):
|
102
|
+
formats = [formats]
|
103
|
+
|
104
|
+
|
105
|
+
def _strptime(
|
106
|
+
format: str,
|
107
|
+
) -> datetime:
|
108
|
+
|
109
|
+
return (
|
110
|
+
datetime
|
111
|
+
.strptime(source, format)
|
112
|
+
.astimezone(tzinfo))
|
113
|
+
|
114
|
+
|
115
|
+
for format in formats:
|
116
|
+
|
117
|
+
with suppress(ValueError):
|
118
|
+
return _strptime(format)
|
119
|
+
|
120
|
+
|
121
|
+
raise ValueError('invalid')
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
def strftime(
|
126
|
+
source: datetime,
|
127
|
+
format: str,
|
128
|
+
) -> str:
|
129
|
+
"""
|
130
|
+
Return the timestamp string for datetime object provided.
|
131
|
+
|
132
|
+
.. note::
|
133
|
+
This function is extremely pedantic and cosmetic.
|
134
|
+
|
135
|
+
Example
|
136
|
+
-------
|
137
|
+
>>> dtime = datetime(2023, 1, 1)
|
138
|
+
>>> strftime(dtime, '%Y')
|
139
|
+
'2023'
|
140
|
+
|
141
|
+
:param source: Python datetime instance containing source.
|
142
|
+
:param format: Format for the timestamp string returned.
|
143
|
+
:returns: Timestamp string for datetime object provided.
|
144
|
+
"""
|
145
|
+
|
146
|
+
return (
|
147
|
+
datetime
|
148
|
+
.strftime(source, format))
|