invesytoolbox 0.0.20__tar.gz → 0.0.22__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.
- invesytoolbox-0.0.22/HISTORY.md +91 -0
- {invesytoolbox-0.0.20/src/invesytoolbox.egg-info → invesytoolbox-0.0.22}/PKG-INFO +26 -4
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/setup.cfg +4 -1
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_data.py +14 -15
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_date_time.py +195 -22
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_email_phone.py +20 -10
- invesytoolbox-0.0.22/src/invesytoolbox/itb_html.py +63 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_locales.py +52 -21
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_restricted_python.py +1 -2
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_security.py +0 -1
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_text_name.py +26 -11
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/itb_www.py +4 -4
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22/src/invesytoolbox.egg-info}/PKG-INFO +26 -4
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox.egg-info/SOURCES.txt +1 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/test_date_time.py +48 -1
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/test_html.py +7 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/test_locales.py +79 -22
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/test_text_name.py +48 -1
- invesytoolbox-0.0.20/src/invesytoolbox/itb_html.py +0 -63
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/LICENSE.txt +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/README.md +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/setup.py +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox/__init__.py +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox.egg-info/dependency_links.txt +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox.egg-info/requires.txt +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/invesytoolbox.egg-info/top_level.txt +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/__init__.py +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/test_data.py +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/test_email_phone.py +0 -0
- {invesytoolbox-0.0.20 → invesytoolbox-0.0.22}/src/tests/test_www.py +0 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
# History
|
2
|
+
## 0.0.22 (2023-10-14)
|
3
|
+
* **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
|
4
|
+
* **get_locale**: locale can be only the language, without the country.
|
5
|
+
* if get_locale fails to get the locale from the system (because invesytoolbox may be imported from a program called from an applicatio rather than the termial), it defaults to `language: 'de'` and `country: 'AT'`.
|
6
|
+
* added support for pendulum DateTime
|
7
|
+
* **create_email_message**: fixed MIMEMultipart settings
|
8
|
+
|
9
|
+
## 0.0.21 (2023-05-21)
|
10
|
+
* New function **unravel_duration**: turns a duration string (for ex. '5T23:05:20') into a list or dictionary.
|
11
|
+
|
12
|
+
## 0.0.20 (2023-05-06)
|
13
|
+
* New function **add_time**: adds (or subtracts) time to (or from) datetime and DateTime
|
14
|
+
|
15
|
+
## 0.0.19 (2023-04-07)
|
16
|
+
* Corrected bug in **fetch_holidays** (argument *length* resulted in an error)
|
17
|
+
|
18
|
+
## 0.0.18 (2023-04-07)
|
19
|
+
* new function **change_h_tags** (in new module **itb_html**)
|
20
|
+
|
21
|
+
## 0.0.17 (2023-03-01)
|
22
|
+
* new function *compare_phonenumbers*: compares two phone numbers after normalizing them (*process_phonenumber*).
|
23
|
+
|
24
|
+
## 0.0.16
|
25
|
+
* *map_special_chars*: works now.
|
26
|
+
|
27
|
+
## 0.0.15
|
28
|
+
* *could_be_a_name*: can now handle names with multiple parts like "Robert De La Mancha". Per word, 2 capitals are allowed (i.e. "MacArthur" or "DeLa Cruz)
|
29
|
+
|
30
|
+
## 0.0.14
|
31
|
+
* *could_be_a_name* and *sort_names*: now working also with prename-only and prenames including a hyphen.
|
32
|
+
|
33
|
+
## 0.0.13
|
34
|
+
* *get_dateformat* now also processes time
|
35
|
+
* *str_to_dt* now checks for valid string
|
36
|
+
* *is_valid_datetime_string*: wrapper for checking with *str_to_dt*
|
37
|
+
* *remove_time* from datetime or DateTime
|
38
|
+
|
39
|
+
## 0.0.12
|
40
|
+
* removed documentation from the README file, instead a link to the gitlab pages
|
41
|
+
* *map_specialChars* now recognizes Unicode character U+0308 (UTF-8 cc 88 = "COMBINING DIAERESIS").
|
42
|
+
* *any_2boolean*
|
43
|
+
* *get_dateformat*: argument *checkonly*
|
44
|
+
* data functions: argument *json_data* changed to *metadata* (it's a dictionary)
|
45
|
+
|
46
|
+
## 0.0.11
|
47
|
+
* BeautifulSoup and nameparser added to requirements.txt
|
48
|
+
* Removed "Date" conversions (modified DateTime) because it's a bad idea
|
49
|
+
* *normalize_name* (using nameparser) added to *itb_text_name*
|
50
|
+
* *capitalize_name* rewritten (now quasi a wrapper for normalize_name)
|
51
|
+
* *could_be_a_name* rewritten using *normalize_name* (nameparser)
|
52
|
+
|
53
|
+
## 0.0.10
|
54
|
+
* *check_spam* for web forms
|
55
|
+
* *dictlist_2datatypes*: iterates through a list of dictionaries and applies *dict_2datatypes*
|
56
|
+
* *prettify_html*: provides *prettify* from BeautifulSoup, because it can't be used directly from restricted Python.
|
57
|
+
* *could_be_a_name*: checks if a string could be a name
|
58
|
+
|
59
|
+
## 0.0.9
|
60
|
+
* *change\_query\_string* respects Zope parameter converters (like `paramname:int`)
|
61
|
+
|
62
|
+
## 0.0.8
|
63
|
+
* New submodule www
|
64
|
+
* *change\_query\_string*
|
65
|
+
|
66
|
+
## 0.0.7
|
67
|
+
* shorter submodule names (no _tools suffix)
|
68
|
+
* *is_holiday* works without argument
|
69
|
+
* *create\_email\_message*: new argument **encoding**
|
70
|
+
* *process_phonenumbers*: cleaned up arguments
|
71
|
+
* *DT_date*: strip a DateTime of its time (get a "naked" date)
|
72
|
+
* *could\_be\_a\_name*: check if a string could possibly be a name
|
73
|
+
|
74
|
+
## 0.0.6
|
75
|
+
* renamed *capitalize\_text* to *capitalize_name* and removed name argument
|
76
|
+
* added Sphinx documentation
|
77
|
+
|
78
|
+
## 0.0.5 (2022-06-11)
|
79
|
+
* removed **terminal_tools** (will be included in a separate package)
|
80
|
+
|
81
|
+
## 0.0.4 (2022-06-09)
|
82
|
+
* better formatted README
|
83
|
+
|
84
|
+
## 0.0.3 (2022-06-09)
|
85
|
+
* updated README (list of functions and a short description)
|
86
|
+
|
87
|
+
## 0.0.2 (2022-06-09)
|
88
|
+
* removed VERSION file
|
89
|
+
|
90
|
+
## 0.0.1 (2022-06-09)
|
91
|
+
* first version
|
@@ -1,22 +1,35 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: invesytoolbox
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.22
|
4
4
|
Summary: Tools for Python scripts or terminal
|
5
5
|
Home-page: https://gitlab.com/Rastaf/invesytoolbox
|
6
6
|
Author: Georg Pfolz
|
7
7
|
Author-email: georg.pfolz@invesy.at
|
8
8
|
License: MIT License
|
9
9
|
Project-URL: Bug Tracker, https://gitlab.com/Rastaf/invesytoolbox/-/issues
|
10
|
-
|
10
|
+
Project-URL: Documentation, https://rastaf.gitlab.io/invesytoolbox/
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
12
12
|
Classifier: Programming Language :: Python :: 3.7
|
13
13
|
Classifier: Programming Language :: Python :: 3.8
|
14
14
|
Classifier: Programming Language :: Python :: 3.9
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
15
17
|
Classifier: License :: OSI Approved :: MIT License
|
16
18
|
Classifier: Operating System :: OS Independent
|
17
19
|
Requires-Python: >=3.7
|
18
20
|
Description-Content-Type: text/markdown
|
19
21
|
License-File: LICENSE.txt
|
22
|
+
Requires-Dist: bs4
|
23
|
+
Requires-Dist: babel
|
24
|
+
Requires-Dist: colorama
|
25
|
+
Requires-Dist: DateTime
|
26
|
+
Requires-Dist: gender_guesser
|
27
|
+
Requires-Dist: holidays>0.13
|
28
|
+
Requires-Dist: nameparser
|
29
|
+
Requires-Dist: phonenumbers
|
30
|
+
Requires-Dist: pycountry
|
31
|
+
Requires-Dist: unidecode
|
32
|
+
Requires-Dist: vobject
|
20
33
|
|
21
34
|
# invesytoolbox
|
22
35
|
|
@@ -31,8 +44,18 @@ That's also why all date and time functions also take into account the old DateT
|
|
31
44
|
The documentation can be found [here](https://rastaf.gitlab.io/invesytoolbox/).
|
32
45
|
|
33
46
|
# History
|
47
|
+
## 0.0.22 (2023-10-14)
|
48
|
+
* **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
|
49
|
+
* **get_locale**: locale can be only the language, without the country.
|
50
|
+
* if get_locale fails to get the locale from the system (because invesytoolbox may be imported from a program called from an applicatio rather than the termial), it defaults to `language: 'de'` and `country: 'AT'`.
|
51
|
+
* added support for pendulum DateTime
|
52
|
+
* **create_email_message**: fixed MIMEMultipart settings
|
53
|
+
|
54
|
+
## 0.0.21 (2023-05-21)
|
55
|
+
* New function **unravel_duration**: turns a duration string (for ex. '5T23:05:20') into a list or dictionary.
|
56
|
+
|
34
57
|
## 0.0.20 (2023-05-06)
|
35
|
-
* **add_time**: adds time to datetime and DateTime
|
58
|
+
* New function **add_time**: adds (or subtracts) time to (or from) datetime and DateTime
|
36
59
|
|
37
60
|
## 0.0.19 (2023-04-07)
|
38
61
|
* Corrected bug in **fetch_holidays** (argument *length* resulted in an error)
|
@@ -111,4 +134,3 @@ The documentation can be found [here](https://rastaf.gitlab.io/invesytoolbox/).
|
|
111
134
|
|
112
135
|
## 0.0.1 (2022-06-09)
|
113
136
|
* first version
|
114
|
-
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[metadata]
|
2
2
|
name = invesytoolbox
|
3
|
-
version = 0.0.
|
3
|
+
version = 0.0.22
|
4
4
|
author = Georg Pfolz
|
5
5
|
author_email = georg.pfolz@invesy.at
|
6
6
|
description = Tools for Python scripts or terminal
|
@@ -11,11 +11,14 @@ license_files = LICENSE.txt
|
|
11
11
|
url = https://gitlab.com/Rastaf/invesytoolbox
|
12
12
|
project_urls =
|
13
13
|
Bug Tracker = https://gitlab.com/Rastaf/invesytoolbox/-/issues
|
14
|
+
Documentation = https://rastaf.gitlab.io/invesytoolbox/
|
14
15
|
classifiers =
|
15
16
|
Programming Language :: Python :: 3
|
16
17
|
Programming Language :: Python :: 3.7
|
17
18
|
Programming Language :: Python :: 3.8
|
18
19
|
Programming Language :: Python :: 3.9
|
20
|
+
Programming Language :: Python :: 3.10
|
21
|
+
Programming Language :: Python :: 3.11
|
19
22
|
License :: OSI Approved :: MIT License
|
20
23
|
Operating System :: OS Independent
|
21
24
|
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
"""
|
3
|
-
==========
|
4
3
|
data_tools
|
5
4
|
==========
|
6
5
|
"""
|
7
6
|
|
8
|
-
from typing import Union
|
7
|
+
from typing import Union, List, Dict, Any, Optional
|
9
8
|
import DateTime
|
10
9
|
import datetime
|
11
10
|
import vobject
|
@@ -19,7 +18,7 @@ time_chars = set('1234567890:')
|
|
19
18
|
|
20
19
|
|
21
20
|
def any_2boolean(
|
22
|
-
value
|
21
|
+
value: Any
|
23
22
|
) -> bool:
|
24
23
|
""" return a boolean for any value
|
25
24
|
|
@@ -34,10 +33,10 @@ def any_2boolean(
|
|
34
33
|
|
35
34
|
|
36
35
|
def dict_from_dict_list(
|
37
|
-
dict_list:
|
38
|
-
key,
|
39
|
-
single_value: bool = None,
|
40
|
-
include_key:
|
36
|
+
dict_list: List[dict],
|
37
|
+
key: str,
|
38
|
+
single_value: Optional[bool] = None,
|
39
|
+
include_key: Optional[bool] = False
|
41
40
|
) -> dict:
|
42
41
|
"""Create a dictionary from a list of dictionaries
|
43
42
|
|
@@ -68,7 +67,7 @@ def dict_from_dict_list(
|
|
68
67
|
|
69
68
|
|
70
69
|
def create_vcard(
|
71
|
-
data:
|
70
|
+
data: Dict[str, str],
|
72
71
|
returning: str = 'vcard'
|
73
72
|
) -> str:
|
74
73
|
"""create a vCard from a dictionary
|
@@ -132,7 +131,7 @@ def create_vcard(
|
|
132
131
|
|
133
132
|
|
134
133
|
def dict_2unicode(
|
135
|
-
d:
|
134
|
+
d: Dict[Union[str, bytes], Any],
|
136
135
|
encoding: str = 'utf-8'
|
137
136
|
) -> dict:
|
138
137
|
""" Converts all keys and values in a dictionary from bytes to unicode
|
@@ -153,12 +152,12 @@ def dict_2unicode(
|
|
153
152
|
|
154
153
|
def dict_2datatypes(
|
155
154
|
d: dict,
|
156
|
-
metadata:
|
155
|
+
metadata: Dict[str, str] = {},
|
157
156
|
convert_keys: bool = False,
|
158
157
|
convert_to_unicode: bool = False,
|
159
158
|
dt: str = 'datetime',
|
160
159
|
fmt: str = '%d.%m.%Y'
|
161
|
-
) ->
|
160
|
+
) -> Dict[Any, Any]:
|
162
161
|
""" convert all data of a dictionary to specific (json metadata) or guessed types """
|
163
162
|
|
164
163
|
if convert_keys:
|
@@ -193,8 +192,8 @@ def dict_2datatypes(
|
|
193
192
|
|
194
193
|
|
195
194
|
def dictlist_2datatypes(
|
196
|
-
dictlist:
|
197
|
-
metadata:
|
195
|
+
dictlist: List[Dict[Any, Any]],
|
196
|
+
metadata: Dict[Any, Any] = {},
|
198
197
|
convert_keys: bool = False,
|
199
198
|
convert_to_unicode: bool = False,
|
200
199
|
dt: str = 'datetime',
|
@@ -214,8 +213,8 @@ def dictlist_2datatypes(
|
|
214
213
|
|
215
214
|
|
216
215
|
def sort_dictlist(
|
217
|
-
dictlist:
|
218
|
-
keys:
|
216
|
+
dictlist: List[dict],
|
217
|
+
keys: Union[str, List[str]],
|
219
218
|
reverse: bool = False
|
220
219
|
) -> list:
|
221
220
|
""" Sorts a list of dictionaries
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# coding=utf-8
|
2
2
|
"""
|
3
|
-
===============
|
4
3
|
date_time_tools
|
5
4
|
===============
|
6
5
|
"""
|
7
|
-
|
6
|
+
from typing import Union
|
8
7
|
import datetime
|
9
8
|
import DateTime
|
9
|
+
import pendulum
|
10
10
|
from dateutil.parser import parse
|
11
11
|
from dateutil.parser._parser import ParserError
|
12
12
|
|
@@ -123,7 +123,7 @@ def get_dateformat(
|
|
123
123
|
|
124
124
|
def str_to_dt(
|
125
125
|
datestring: str,
|
126
|
-
fmt:
|
126
|
+
fmt: str = None,
|
127
127
|
checkonly: bool = False,
|
128
128
|
returning: str = None
|
129
129
|
) -> datetime.datetime:
|
@@ -191,7 +191,7 @@ def str_to_dt(
|
|
191
191
|
|
192
192
|
def str_to_DT(
|
193
193
|
datestring: str,
|
194
|
-
fmt:
|
194
|
+
fmt: str = None
|
195
195
|
) -> DateTime.DateTime:
|
196
196
|
""" Convert a string to Datetime.Datetime
|
197
197
|
|
@@ -213,9 +213,24 @@ def str_to_DT(
|
|
213
213
|
)
|
214
214
|
|
215
215
|
|
216
|
+
def str_to_pendulum(
|
217
|
+
datestring: str,
|
218
|
+
fmt: str = None
|
219
|
+
) -> pendulum.DateTime:
|
220
|
+
""" Convert a string to pendulum.DateTime
|
221
|
+
|
222
|
+
:param fmt: default: %d.%m.%Y (if not specified, the datestring is assumed to be valid ISO8601)
|
223
|
+
"""
|
224
|
+
|
225
|
+
if not fmt:
|
226
|
+
return pendulum.parse(datestring)
|
227
|
+
else:
|
228
|
+
return pendulum.datetime.strptime(datestring, fmt)
|
229
|
+
|
230
|
+
|
216
231
|
def str_to_date(
|
217
232
|
datestring: str,
|
218
|
-
fmt:
|
233
|
+
fmt: str = None
|
219
234
|
) -> datetime.date:
|
220
235
|
""" Convert a string to datetime.date
|
221
236
|
|
@@ -256,6 +271,38 @@ def DT_to_date(
|
|
256
271
|
)
|
257
272
|
|
258
273
|
|
274
|
+
def DT_to_pendulum(
|
275
|
+
DT: DateTime.DateTime
|
276
|
+
) -> pendulum.DateTime:
|
277
|
+
""" Convert DateTime.DateTime to pendulum.DateTime """
|
278
|
+
|
279
|
+
tz = DT.timezone()
|
280
|
+
|
281
|
+
offset = None
|
282
|
+
|
283
|
+
if '+' in tz:
|
284
|
+
offset = int(tz.split('+')[-1])
|
285
|
+
elif '-' in tz:
|
286
|
+
offset = - int(tz.split('-')[-1])
|
287
|
+
|
288
|
+
if offset is not None:
|
289
|
+
if not offset:
|
290
|
+
tz = 'UTC'
|
291
|
+
else:
|
292
|
+
tz = pendulum.FixedTimezone(offset * 3600)
|
293
|
+
else:
|
294
|
+
tz = pendulum.timezone(tz)
|
295
|
+
|
296
|
+
return pendulum.datetime(
|
297
|
+
DT.year(),
|
298
|
+
DT.month(),
|
299
|
+
DT.day(),
|
300
|
+
DT.hour(),
|
301
|
+
DT.minute(),
|
302
|
+
tz = tz
|
303
|
+
)
|
304
|
+
|
305
|
+
|
259
306
|
def date_to_dt(
|
260
307
|
date: datetime.date,
|
261
308
|
H=None, M=None
|
@@ -290,16 +337,46 @@ def date_to_DT(
|
|
290
337
|
return DateTime.DateTime(date_to_dt(date))
|
291
338
|
|
292
339
|
|
340
|
+
def dt_to_pendulum(
|
341
|
+
dt: datetime.datetime
|
342
|
+
) -> pendulum.DateTime:
|
343
|
+
""" Convert datetime.datetime to pendulum.DateTime """
|
344
|
+
|
345
|
+
return pendulum.datetime(
|
346
|
+
dt.year,
|
347
|
+
dt.month,
|
348
|
+
dt.day,
|
349
|
+
dt.hour,
|
350
|
+
dt.minute
|
351
|
+
|
352
|
+
)
|
353
|
+
|
354
|
+
|
355
|
+
def pendulum_to_DT(
|
356
|
+
pendulum_dt: pendulum.DateTime
|
357
|
+
) -> DateTime.DateTime:
|
358
|
+
""" Convert pendulum.DateTime to DateTime.DateTime """
|
359
|
+
|
360
|
+
return DateTime.DateTime(
|
361
|
+
pendulum_dt.year,
|
362
|
+
pendulum_dt.month,
|
363
|
+
pendulum_dt.day,
|
364
|
+
pendulum_dt.hour,
|
365
|
+
pendulum_dt.minute
|
366
|
+
).toZone(pendulum_dt.timezone.name)
|
367
|
+
|
368
|
+
|
293
369
|
def convert_datetime(
|
294
|
-
date:
|
370
|
+
date: Union[
|
295
371
|
str,
|
296
372
|
datetime.datetime,
|
297
373
|
datetime.date,
|
298
|
-
DateTime.DateTime
|
299
|
-
|
374
|
+
DateTime.DateTime,
|
375
|
+
pendulum.DateTime
|
376
|
+
],
|
300
377
|
convert_to: str,
|
301
378
|
fmt: str = None
|
302
|
-
) ->
|
379
|
+
) -> Union[datetime.datetime, datetime.date, DateTime.DateTime, str]:
|
303
380
|
""" Conversion of date and time formats
|
304
381
|
|
305
382
|
:param convertTo:
|
@@ -321,6 +398,17 @@ def convert_datetime(
|
|
321
398
|
return DT_to_dt(date)
|
322
399
|
elif convert_to in ('str', 'string'):
|
323
400
|
return date.strftime(fmt)
|
401
|
+
elif convert_to == 'pendulum':
|
402
|
+
return DT_to_pendulum(date)
|
403
|
+
elif isinstance(date, pendulum.DateTime):
|
404
|
+
if convert_to == 'datetime':
|
405
|
+
return date
|
406
|
+
elif convert_to == 'date':
|
407
|
+
return date.date()
|
408
|
+
elif convert_to == 'DateTime':
|
409
|
+
return pendulum_to_DT(date)
|
410
|
+
elif convert_to in ('str', 'string'):
|
411
|
+
return date.strftime(fmt)
|
324
412
|
elif isinstance(date, str):
|
325
413
|
if convert_to in ('str', 'string'):
|
326
414
|
if str_to_date(date, checkonly=True, fmt=fmt):
|
@@ -333,6 +421,8 @@ def convert_datetime(
|
|
333
421
|
return str_to_dt(date, fmt=fmt)
|
334
422
|
elif convert_to == 'DateTime':
|
335
423
|
return str_to_DT(date, fmt=fmt)
|
424
|
+
elif convert_to == 'pendulum':
|
425
|
+
return pendulum.parse(date)
|
336
426
|
elif isinstance(date, datetime.datetime):
|
337
427
|
# datetime.datetime has to be checked BEFORE
|
338
428
|
# datetime.date (isinstance(datetime.datetime, datetime.date) == True !)
|
@@ -344,6 +434,8 @@ def convert_datetime(
|
|
344
434
|
return DateTime.DateTime(date)
|
345
435
|
elif convert_to in ('str', 'string'):
|
346
436
|
return date.strftime(fmt)
|
437
|
+
elif convert_to == 'pendulum':
|
438
|
+
return dt_to_pendulum(date)
|
347
439
|
elif type(date) is datetime.date: # isinstance does not work
|
348
440
|
if convert_to == 'date':
|
349
441
|
return date
|
@@ -353,15 +445,21 @@ def convert_datetime(
|
|
353
445
|
return date_to_DT(date)
|
354
446
|
elif convert_to in ('str', 'string'):
|
355
447
|
return date.strftime(fmt)
|
448
|
+
elif convert_to == 'pendulum':
|
449
|
+
return pendulum.datetime(
|
450
|
+
date.year,
|
451
|
+
date.month,
|
452
|
+
date.day
|
453
|
+
)
|
356
454
|
|
357
455
|
|
358
456
|
def get_calendar_week(
|
359
|
-
date:
|
457
|
+
date: Union[
|
360
458
|
str,
|
361
459
|
datetime.datetime,
|
362
460
|
datetime.date,
|
363
461
|
DateTime.DateTime
|
364
|
-
|
462
|
+
]
|
365
463
|
) -> int:
|
366
464
|
""" Get calendard week (week nb of year) from a date
|
367
465
|
|
@@ -374,12 +472,12 @@ def get_calendar_week(
|
|
374
472
|
|
375
473
|
|
376
474
|
def get_dow_number(
|
377
|
-
date:
|
475
|
+
date: Union[
|
378
476
|
str,
|
379
477
|
datetime.datetime,
|
380
478
|
datetime.date,
|
381
479
|
DateTime.DateTime
|
382
|
-
|
480
|
+
]
|
383
481
|
) -> int:
|
384
482
|
""" Get day of week number from a date
|
385
483
|
|
@@ -392,12 +490,12 @@ def get_dow_number(
|
|
392
490
|
|
393
491
|
|
394
492
|
def get_isocalendar(
|
395
|
-
date:
|
493
|
+
date: Union[
|
396
494
|
str,
|
397
495
|
datetime.datetime,
|
398
496
|
datetime.date,
|
399
497
|
DateTime.DateTime
|
400
|
-
|
498
|
+
]
|
401
499
|
) -> datetime.date:
|
402
500
|
""" Returns the isocalendar tuple: (year, woy, downb)
|
403
501
|
|
@@ -451,6 +549,7 @@ def daterange_from_week(
|
|
451
549
|
- DateTime
|
452
550
|
- datetime (default)
|
453
551
|
- date
|
552
|
+
- pendulum
|
454
553
|
:param fmt: default: '%d.%m.%Y'
|
455
554
|
|
456
555
|
.. note:: Author: Andreas Bruhn, https://groups.google.com/forum/#!topic/de.comp.lang.python/p8LfbNMIJ5c
|
@@ -488,6 +587,7 @@ def dates_from_week(
|
|
488
587
|
- DateTime (default)
|
489
588
|
- datetime
|
490
589
|
- date
|
590
|
+
- pendulum
|
491
591
|
|
492
592
|
:param fmt: default: '%d.%m.%Y'
|
493
593
|
:param endofweek: 1 to 7 (monday to sunday)
|
@@ -525,7 +625,7 @@ def day_from_week(
|
|
525
625
|
weekday: int = 1,
|
526
626
|
returning: str = 'date',
|
527
627
|
fmt: str = '%d.%m.%Y'
|
528
|
-
) ->
|
628
|
+
) -> Union[datetime.datetime, datetime.date, DateTime.DateTime, pendulum.DateTime]:
|
529
629
|
"""Get day from a week
|
530
630
|
|
531
631
|
:param weekday: begins with 1 = monday
|
@@ -534,6 +634,7 @@ def day_from_week(
|
|
534
634
|
- DateTime
|
535
635
|
- datetime (default)
|
536
636
|
- date
|
637
|
+
- pendulum
|
537
638
|
|
538
639
|
:param fmt: default: '%d.%m.%Y'
|
539
640
|
"""
|
@@ -551,7 +652,7 @@ def monday_from_week(
|
|
551
652
|
week: int,
|
552
653
|
returning: str = 'date',
|
553
654
|
fmt='%d.%m.%Y'
|
554
|
-
) ->
|
655
|
+
) -> Union[datetime.datetime, datetime.date, DateTime.DateTime, pendulum.DateTime]:
|
555
656
|
"""Get monday from a week
|
556
657
|
|
557
658
|
:param: weekday begins with 1 = monday
|
@@ -582,7 +683,7 @@ def last_week_of_year(
|
|
582
683
|
|
583
684
|
|
584
685
|
def remove_time(
|
585
|
-
date_time:
|
686
|
+
date_time: Union[datetime.datetime, DateTime.DateTime, pendulum.DateTime],
|
586
687
|
returning: str = None
|
587
688
|
):
|
588
689
|
"""Remove time from a datetime or DateTime object
|
@@ -627,6 +728,19 @@ def remove_time(
|
|
627
728
|
date_time.month(),
|
628
729
|
date_time.day()
|
629
730
|
)
|
731
|
+
elif isinstance(date_time, pendulum.DateTime):
|
732
|
+
if not returning or returning == 'pendulum':
|
733
|
+
return date_time.start_of('day')
|
734
|
+
elif returning == 'date':
|
735
|
+
return date_time.date()
|
736
|
+
elif returning == 'datetime':
|
737
|
+
return date_time.datetime()
|
738
|
+
elif returning == 'DateTime':
|
739
|
+
return DateTime.DateTime(
|
740
|
+
date_time.year,
|
741
|
+
date_time.month,
|
742
|
+
date_time.day
|
743
|
+
)
|
630
744
|
|
631
745
|
|
632
746
|
def create_timedelta(
|
@@ -636,7 +750,7 @@ def create_timedelta(
|
|
636
750
|
"""create a timedelta or a number from a time string
|
637
751
|
|
638
752
|
:param timestr: format or H:M or H:M:S or H:M:S:ms, possibly with day: 4T03:12:31
|
639
|
-
:param returningdt: dt or
|
753
|
+
:param returningdt: dt, DT or pendulum
|
640
754
|
"""
|
641
755
|
days_time = timestr.split('T')
|
642
756
|
timestr = days_time[-1]
|
@@ -665,13 +779,22 @@ def create_timedelta(
|
|
665
779
|
t_delta = days
|
666
780
|
for el in TIME_ELS:
|
667
781
|
t_delta += time_elements[el] * time_dividers[el]
|
782
|
+
elif dt == 'pendulum':
|
783
|
+
t_delta = pendulum.duration(
|
784
|
+
days=days,
|
785
|
+
hours=time_elements['h'],
|
786
|
+
minutes=time_elements['m'],
|
787
|
+
seconds=time_elements['s'],
|
788
|
+
milliseconds=time_elements['ms'],
|
789
|
+
)
|
668
790
|
|
669
791
|
return t_delta
|
670
792
|
|
671
793
|
|
672
794
|
def add_time(
|
673
|
-
date_time:
|
795
|
+
date_time: Union[datetime.datetime, DateTime.DateTime, pendulum.DateTime],
|
674
796
|
timestr: str,
|
797
|
+
subtract: bool = False,
|
675
798
|
returning: str = None
|
676
799
|
):
|
677
800
|
"""add or remove time from a datetime or DateTime object
|
@@ -685,8 +808,7 @@ def add_time(
|
|
685
808
|
or
|
686
809
|
dTH:M etc... (ex.: 4T03:12:31)
|
687
810
|
"""
|
688
|
-
subtract
|
689
|
-
if timestr.startswith('-'):
|
811
|
+
if subtract or timestr.startswith('-'):
|
690
812
|
subtract = True
|
691
813
|
timestr = timestr[1:]
|
692
814
|
|
@@ -697,6 +819,8 @@ def add_time(
|
|
697
819
|
dt = 'datetime'
|
698
820
|
elif isinstance(date_time, DateTime.DateTime):
|
699
821
|
dt = 'DateTime'
|
822
|
+
elif isinstance(date_time, pendulum.DateTime):
|
823
|
+
dt = 'pendulum'
|
700
824
|
else:
|
701
825
|
raise Exception('date_time has to be datetime.datetime, datetime.date or DateTime.DateTime!')
|
702
826
|
|
@@ -709,3 +833,52 @@ def add_time(
|
|
709
833
|
return date_time - t_delta
|
710
834
|
else:
|
711
835
|
return date_time + t_delta
|
836
|
+
|
837
|
+
|
838
|
+
def unravel_duration(
|
839
|
+
duration: str,
|
840
|
+
returning: str = 'list'
|
841
|
+
):
|
842
|
+
"""split a duration string into components
|
843
|
+
|
844
|
+
duration comes in the format nTH:M:S:ms
|
845
|
+
(days, hours, minutes, seconds, milliseconds)
|
846
|
+
|
847
|
+
@param returning: list, tuple oder dictionary
|
848
|
+
"""
|
849
|
+
if 'T' in duration:
|
850
|
+
days, rest = duration.split('T')
|
851
|
+
days = int(days)
|
852
|
+
else:
|
853
|
+
days = 0
|
854
|
+
rest = duration
|
855
|
+
|
856
|
+
elements = rest.split(':')
|
857
|
+
|
858
|
+
hours = minutes = seconds = milliseconds = 0
|
859
|
+
time_elements = [hours, minutes, seconds, milliseconds]
|
860
|
+
|
861
|
+
for i, (element, time_measure) in enumerate(zip(elements, time_elements.copy())):
|
862
|
+
time_elements[i] = int(element)
|
863
|
+
|
864
|
+
time_elements.insert(0, days)
|
865
|
+
|
866
|
+
for i, divider in zip(
|
867
|
+
range(4, -1, -1),
|
868
|
+
(1000, 60, 60, 24)
|
869
|
+
):
|
870
|
+
time_element = int(time_elements[i] / divider)
|
871
|
+
time_elements[i] -= time_element * divider
|
872
|
+
time_elements[i - 1] += time_element
|
873
|
+
|
874
|
+
if returning == 'list':
|
875
|
+
return time_elements
|
876
|
+
elif returning in ('dict', 'dictionary'):
|
877
|
+
d = {}
|
878
|
+
for name, val in zip(('days', 'hours', 'minutes', 'seconds', 'milliseconds'), time_elements):
|
879
|
+
d[name] = val
|
880
|
+
return d
|
881
|
+
elif returning == 'tuple':
|
882
|
+
tuple(time_elements)
|
883
|
+
else:
|
884
|
+
raise Exception('argument returning must be list, dictionary or tuple.')
|