kisa-utils 0.37.3__py3-none-any.whl → 0.37.4__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.
- kisa_utils/cache.py +62 -24
- kisa_utils/codes.py +9 -1
- kisa_utils/config.py +14 -4
- kisa_utils/dates.py +176 -29
- kisa_utils/db.py +180 -78
- kisa_utils/encryption.py +34 -6
- kisa_utils/enqueue.py +39 -23
- kisa_utils/figures.py +66 -8
- kisa_utils/functionUtils.py +19 -9
- kisa_utils/log.py +20 -8
- kisa_utils/permissions/__init__.py +448 -58
- kisa_utils/queues.py +70 -22
- kisa_utils/remote.py +18 -6
- kisa_utils/response.py +59 -5
- kisa_utils/servers/flask.py +164 -60
- kisa_utils/standardize.py +27 -1
- kisa_utils/storage.py +84 -38
- kisa_utils/structures/utils.py +10 -6
- kisa_utils/structures/validator.py +16 -3
- kisa_utils/threads.py +45 -31
- kisa_utils/token.py +30 -11
- {kisa_utils-0.37.3.dist-info → kisa_utils-0.37.4.dist-info}/METADATA +1 -1
- kisa_utils-0.37.4.dist-info/RECORD +29 -0
- kisa_utils-0.37.3.dist-info/RECORD +0 -29
- {kisa_utils-0.37.3.dist-info → kisa_utils-0.37.4.dist-info}/WHEEL +0 -0
- {kisa_utils-0.37.3.dist-info → kisa_utils-0.37.4.dist-info}/top_level.txt +0 -0
kisa_utils/cache.py
CHANGED
|
@@ -8,6 +8,7 @@ import time
|
|
|
8
8
|
from . import threads
|
|
9
9
|
from . import queues
|
|
10
10
|
from . import db
|
|
11
|
+
from typing import Callable
|
|
11
12
|
|
|
12
13
|
class CacheException(Exception):
|
|
13
14
|
pass
|
|
@@ -17,7 +18,12 @@ class _EmptyCacheValue:
|
|
|
17
18
|
|
|
18
19
|
class Cache:
|
|
19
20
|
__ACTIVE_CACHES = {}
|
|
20
|
-
def __init__(self, name:str):
|
|
21
|
+
def __init__(self, name:str) -> None:
|
|
22
|
+
'''
|
|
23
|
+
create a cache instance
|
|
24
|
+
Args:
|
|
25
|
+
name(str): the cache name
|
|
26
|
+
'''
|
|
21
27
|
self.__cache = {
|
|
22
28
|
# key: {
|
|
23
29
|
# 'lastUpdated': int,
|
|
@@ -75,23 +81,30 @@ class Cache:
|
|
|
75
81
|
self.__cache[key]['value'] = value
|
|
76
82
|
self.__cache[key]['lastUpdated'] = now
|
|
77
83
|
|
|
78
|
-
def addKey(self, name:str, updator:
|
|
84
|
+
def addKey(self, name:str, updator:Callable, autoUpdateAfter:int=3600, duplicateUpdateThreshold:float=30) -> dict:
|
|
79
85
|
'''
|
|
80
86
|
create a new cache key
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
Args:
|
|
89
|
+
name(str): the name of the key
|
|
90
|
+
updator(Callable): the function to call everytime we need to update the key.
|
|
91
|
+
The value of the key is whatever the function returns.
|
|
92
|
+
Also, the `updator` should not take any arguments or keywords
|
|
93
|
+
autoUpdateAfter(int): how many SECONDS to wait before we automatically call the `updator` function
|
|
94
|
+
duplicateUpdateThreshold(float): SECONDS within which if multiple requests to update the key will be ignored as long as one is already activated.
|
|
95
|
+
say we have 20 calls to update a key in 1 minute, only 1 request will be obeyed to avoid strange deadlocks and database contension-resources
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
Returns:
|
|
98
|
+
```
|
|
99
|
+
{
|
|
100
|
+
'status':bool,
|
|
101
|
+
'log':str
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
92
105
|
NB: the result of the `updator` will be ignored if;
|
|
93
106
|
- its `None`
|
|
94
|
-
- its a `dict` with `status
|
|
107
|
+
- its a `dict` with `status` set to `False`
|
|
95
108
|
- the `updator` raised an exception
|
|
96
109
|
'''
|
|
97
110
|
reply = {'status':False, 'log':''}
|
|
@@ -131,10 +144,16 @@ class Cache:
|
|
|
131
144
|
def triggerKeyUpdate(self, key:str) -> dict:
|
|
132
145
|
'''
|
|
133
146
|
trigger update value of a key
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
147
|
+
Args:
|
|
148
|
+
key(str): name of the cache key
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
```
|
|
152
|
+
{
|
|
153
|
+
'status':bool,
|
|
154
|
+
'log':str
|
|
155
|
+
}
|
|
156
|
+
```
|
|
138
157
|
'''
|
|
139
158
|
reply = {'status':False, 'log':''}
|
|
140
159
|
|
|
@@ -147,10 +166,17 @@ class Cache:
|
|
|
147
166
|
def getValue(self, key:str) -> dict:
|
|
148
167
|
'''
|
|
149
168
|
attempt to get the value of a key set in the cache
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
169
|
+
Args:
|
|
170
|
+
key(str): name of the cache key
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
```
|
|
174
|
+
{
|
|
175
|
+
'status':bool,
|
|
176
|
+
'log':str,
|
|
177
|
+
'value':Any
|
|
178
|
+
}
|
|
179
|
+
```
|
|
154
180
|
'''
|
|
155
181
|
|
|
156
182
|
reply = {'status':False, 'log':'', 'value':None}
|
|
@@ -170,10 +196,17 @@ class Cache:
|
|
|
170
196
|
def create(name:str) -> dict:
|
|
171
197
|
'''
|
|
172
198
|
create a new cache object
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
199
|
+
Args:
|
|
200
|
+
name(str): the name of the cache
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
```
|
|
204
|
+
{
|
|
205
|
+
'status':bool,
|
|
206
|
+
'log':str,
|
|
207
|
+
'cache': Cache
|
|
208
|
+
}
|
|
209
|
+
```
|
|
177
210
|
'''
|
|
178
211
|
reply = {'status':False, 'log':'', 'cache':None}
|
|
179
212
|
|
|
@@ -190,6 +223,11 @@ def create(name:str) -> dict:
|
|
|
190
223
|
return reply
|
|
191
224
|
|
|
192
225
|
def get(name:str) -> Cache | None:
|
|
226
|
+
'''
|
|
227
|
+
get cache by its name
|
|
228
|
+
Args:
|
|
229
|
+
name(str): the cache name
|
|
230
|
+
'''
|
|
193
231
|
return Cache._Cache__ACTIVE_CACHES.get(name,None)
|
|
194
232
|
|
|
195
233
|
if __name__=='__main__':
|
kisa_utils/codes.py
CHANGED
|
@@ -7,7 +7,15 @@ __MAX_CODE_LENGTH = 32
|
|
|
7
7
|
__ID_SPACE = string.ascii_lowercase + string.ascii_uppercase + string.digits
|
|
8
8
|
|
|
9
9
|
def new(length:int=12, useSeparator:bool=False, xterSet:str=__ID_SPACE) -> str:
|
|
10
|
-
'
|
|
10
|
+
'''
|
|
11
|
+
generate a new code
|
|
12
|
+
Args:
|
|
13
|
+
length(int): length of the code to generate
|
|
14
|
+
useSeparator(bool): include a separator ('-') in the code
|
|
15
|
+
xterSet(str): the chatacters to use eg '0123456789' ensures the code only has numbers in it
|
|
16
|
+
Note:
|
|
17
|
+
compexity of the code will be len(xterSet)^length
|
|
18
|
+
'''
|
|
11
19
|
|
|
12
20
|
assert(length>0 and length<=__MAX_CODE_LENGTH)
|
|
13
21
|
code = ''
|
kisa_utils/config.py
CHANGED
|
@@ -32,7 +32,9 @@ def _getTopicAndKey(path:str) -> list[str,str]:
|
|
|
32
32
|
|
|
33
33
|
def getValue(path:str) -> Any|None:
|
|
34
34
|
'''
|
|
35
|
-
|
|
35
|
+
get the value of a set topic-key path
|
|
36
|
+
Args:
|
|
37
|
+
path(str): topicName/keyName eg 'topic1/key1'
|
|
36
38
|
'''
|
|
37
39
|
|
|
38
40
|
topic, key = _getTopicAndKey(path)
|
|
@@ -41,7 +43,9 @@ def getValue(path:str) -> Any|None:
|
|
|
41
43
|
|
|
42
44
|
def setValue(path:str, value:Any) -> bool:
|
|
43
45
|
'''
|
|
44
|
-
|
|
46
|
+
set the value of a set topic-key path
|
|
47
|
+
Args:
|
|
48
|
+
path(str): topicName/keyName eg 'topic1/key1'
|
|
45
49
|
'''
|
|
46
50
|
topic, key = _getTopicAndKey(path)
|
|
47
51
|
|
|
@@ -58,6 +62,11 @@ def setValue(path:str, value:Any) -> bool:
|
|
|
58
62
|
return True
|
|
59
63
|
|
|
60
64
|
def getTopic(topic:str) -> dict|None:
|
|
65
|
+
'''
|
|
66
|
+
get topic data
|
|
67
|
+
Args:
|
|
68
|
+
topic(str): the topic name
|
|
69
|
+
'''
|
|
61
70
|
topic,_ = _getTopicAndKey(f'{topic}/_')
|
|
62
71
|
data = None
|
|
63
72
|
|
|
@@ -72,9 +81,10 @@ def getTopic(topic:str) -> dict|None:
|
|
|
72
81
|
return data
|
|
73
82
|
|
|
74
83
|
def getConfigPath() -> str:
|
|
84
|
+
'''get path to the config files root directory'''
|
|
75
85
|
return __config_root_path
|
|
76
86
|
|
|
77
|
-
def
|
|
87
|
+
def __init__() -> None:
|
|
78
88
|
global __config_root_path
|
|
79
89
|
_rootPath = getValue('_sys_/configPath')
|
|
80
90
|
if _rootPath:
|
|
@@ -82,4 +92,4 @@ def init():
|
|
|
82
92
|
else:
|
|
83
93
|
setValue('_sys_/configPath', __config_root_path)
|
|
84
94
|
|
|
85
|
-
|
|
95
|
+
__init__()
|
kisa_utils/dates.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import time
|
|
3
|
-
from typing import Tuple
|
|
4
3
|
|
|
5
4
|
WEEK_DAYS = {
|
|
6
5
|
1:'Monday', 2:'Tuesday', 3:'Wednesday',
|
|
@@ -8,8 +7,16 @@ WEEK_DAYS = {
|
|
|
8
7
|
7: 'Sunday'
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
def weekRange(date:str|datetime.datetime) ->
|
|
12
|
-
'
|
|
10
|
+
def weekRange(date:str|datetime.datetime) -> tuple[str,str]: # assumes standardizedDates to be given
|
|
11
|
+
'''
|
|
12
|
+
return a list containing the monday and sunday that belong to the week
|
|
13
|
+
Args:
|
|
14
|
+
date(str|datetime.datetime): the date whose week-range we need to get
|
|
15
|
+
Returns:
|
|
16
|
+
```
|
|
17
|
+
(monday:str, sunday:str) # each date is in format "YYYY-MM-DD"
|
|
18
|
+
```
|
|
19
|
+
'''
|
|
13
20
|
|
|
14
21
|
if isinstance(date,(str,bytes)):
|
|
15
22
|
date = datetime.datetime.strptime(date, '%Y-%m-%d')
|
|
@@ -22,8 +29,17 @@ def weekRange(date:str|datetime.datetime) -> Tuple[str,str]: # assumes standardi
|
|
|
22
29
|
# assumes standardizedDates to be given
|
|
23
30
|
def humanizeDate(date:str, includeWeekDay:bool=False) -> str:
|
|
24
31
|
'''
|
|
25
|
-
convert
|
|
26
|
-
|
|
32
|
+
convert date into a human-friendly format
|
|
33
|
+
Args:
|
|
34
|
+
date(str): the date to humanize. its in the `YYYY-MM-DD` format
|
|
35
|
+
includeWeekDay(bool): wheather or not to include the week-day
|
|
36
|
+
Returns:
|
|
37
|
+
`str` in format `DD MonthCode YYYY`
|
|
38
|
+
Examples:
|
|
39
|
+
```
|
|
40
|
+
humanizeDate('2021-09-23') -> '23 Sep 2021'
|
|
41
|
+
humanizeDate('2021-09-23', True) -> 'Thu 23 Sep 2021'
|
|
42
|
+
```
|
|
27
43
|
'''
|
|
28
44
|
|
|
29
45
|
if includeWeekDay:
|
|
@@ -35,20 +51,67 @@ def humanizeDate(date:str, includeWeekDay:bool=False) -> str:
|
|
|
35
51
|
#return f'{date[2]} {MONTH_CODES[date[1]]} {date[0]}'
|
|
36
52
|
|
|
37
53
|
def dehumanizeDate(date:str) -> str:
|
|
54
|
+
'''
|
|
55
|
+
convert date from human-friendly format to YYYY-MM-DD
|
|
56
|
+
Args:
|
|
57
|
+
date(str): the humanized date to dehumanize
|
|
58
|
+
Examples:
|
|
59
|
+
```
|
|
60
|
+
dehumanizeDate('23 Sep 2021') -> '2021-09-23'
|
|
61
|
+
dehumanizeDate('Thu 23 Sep 2021') -> '2021-09-23'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
'''
|
|
38
65
|
if 2==date.count(' '):
|
|
39
66
|
return datetime.datetime.strptime(date, '%d %b %Y').strftime('%Y-%m-%d')
|
|
40
67
|
|
|
41
68
|
return datetime.datetime.strptime(date, '%a %d %b %Y').strftime('%Y-%m-%d')
|
|
42
69
|
|
|
43
|
-
def daysBetweenDates(dateFrom, dateTo): # assumes standardizedDates to be given
|
|
44
|
-
'
|
|
70
|
+
def daysBetweenDates(dateFrom:str, dateTo:str) -> int: # assumes standardizedDates to be given
|
|
71
|
+
'''
|
|
72
|
+
get the number of days between dates, not inclusive
|
|
73
|
+
Args:
|
|
74
|
+
dateFrom(str): the start date in `YYYY-MM-DD` format
|
|
75
|
+
dateTo(str): end date in `YYYY-MM-DD` format
|
|
76
|
+
Note:
|
|
77
|
+
the days are not inclusive in the count
|
|
78
|
+
Examples:
|
|
79
|
+
```
|
|
80
|
+
daysBetweenDates(monday, tuesday) -> 1 # both days in the same week
|
|
81
|
+
```
|
|
82
|
+
'''
|
|
83
|
+
|
|
45
84
|
return (datetime.datetime.strptime(dateTo, '%Y-%m-%d') - datetime.datetime.strptime(dateFrom, '%Y-%m-%d')).days
|
|
46
85
|
|
|
47
|
-
def daysBetweenDatesInlusive(dateFrom:str, dateTo:str): # assumes standardizedDates to be given
|
|
86
|
+
def daysBetweenDatesInlusive(dateFrom:str, dateTo:str) -> str: # assumes standardizedDates to be given
|
|
87
|
+
'''
|
|
88
|
+
get the number of days between dates, inclusive
|
|
89
|
+
Args:
|
|
90
|
+
dateFrom(str): the start date in `YYYY-MM-DD` format
|
|
91
|
+
dateTo(str): end date in `YYYY-MM-DD` format
|
|
92
|
+
Note:
|
|
93
|
+
the days are not inclusive in the count
|
|
94
|
+
Examples:
|
|
95
|
+
```
|
|
96
|
+
daysBetweenDatesInlusive(monday, tuesday) -> 2 # both days in the same week, monday is included in the count
|
|
97
|
+
```
|
|
98
|
+
'''
|
|
48
99
|
days = daysBetweenDates(dateFrom, dateTo)
|
|
49
100
|
return (abs(days)+1) * (-1 if days<0 else +1)
|
|
50
101
|
|
|
51
|
-
def howLongAgo(dateFrom, dateTo, shortestVersion:bool=True):
|
|
102
|
+
def howLongAgo(dateFrom:str, dateTo:str, shortestVersion:bool=True) -> str:
|
|
103
|
+
'''
|
|
104
|
+
get a human-friendly how-long-ago eg '3 days', '1 week, 4 days', '3 months, 1 week, 6 days', etc
|
|
105
|
+
Args:
|
|
106
|
+
dateFrom(str): the start date in the YYYY-MM-DD format
|
|
107
|
+
dateTo(str): the end date in the YYYY-MM-DD format
|
|
108
|
+
shortestVersion(bool): return the shorted possible version
|
|
109
|
+
Examples:
|
|
110
|
+
```
|
|
111
|
+
howLongAgo('2025-01-01', '2025-05-18') -> '4 months, 2 weeks'
|
|
112
|
+
howLongAgo('2025-01-01', '2025-05-18', False) -> '4 months, 2 weeks, 3 days'
|
|
113
|
+
```
|
|
114
|
+
'''
|
|
52
115
|
days = daysBetweenDates(dateFrom, dateTo)
|
|
53
116
|
|
|
54
117
|
return_str = ''
|
|
@@ -82,28 +145,78 @@ def howLongAgo(dateFrom, dateTo, shortestVersion:bool=True):
|
|
|
82
145
|
|
|
83
146
|
return return_str
|
|
84
147
|
|
|
85
|
-
def currentTimestamp():
|
|
148
|
+
def currentTimestamp() -> str:
|
|
86
149
|
'''
|
|
87
|
-
get current timestamp in EAT
|
|
150
|
+
get current timestamp in EAT
|
|
151
|
+
Returns:
|
|
152
|
+
string in format `YYYY-MM-DD HH:MM:SS`
|
|
88
153
|
'''
|
|
89
154
|
now = datetime.datetime.utcnow() + datetime.timedelta(hours=3.00)
|
|
90
155
|
return str(now)[:19]
|
|
91
156
|
|
|
92
|
-
def dateFrom(date,days,hours=0):
|
|
93
|
-
'
|
|
94
|
-
date
|
|
95
|
-
|
|
157
|
+
def dateFrom(date:str, days:int, hours:int=0) -> str:
|
|
158
|
+
'''
|
|
159
|
+
get the date thats `days` + `hours` from `date`
|
|
160
|
+
Args:
|
|
161
|
+
date(str): the reference date to use format is `YYYY-MM-DD`
|
|
162
|
+
days(int): days from the reference date
|
|
163
|
+
hours(int): hours to include to the days to ge tthe new date
|
|
164
|
+
Examples:
|
|
165
|
+
```
|
|
166
|
+
dateFrom('2025-06-18', 1) -> '2025-06-19'
|
|
167
|
+
dateFrom('2025-01-01', -2) -> '2024-12-30'
|
|
168
|
+
```
|
|
169
|
+
'''
|
|
170
|
+
_date = datetime.datetime.strptime(date, '%Y-%m-%d')
|
|
171
|
+
return str(_date + datetime.timedelta(days=days, hours=hours))[:10]
|
|
96
172
|
|
|
97
|
-
def dateFromNow(days=0,hours=0):
|
|
173
|
+
def dateFromNow(days=0,hours=0) -> str:
|
|
98
174
|
'''
|
|
99
|
-
get
|
|
175
|
+
get the timestamp(not date) thats `days` + `hours` from the current time
|
|
176
|
+
Args:
|
|
177
|
+
days(int): days from the reference date
|
|
178
|
+
hours(int): hours to include to the days to ge tthe new date
|
|
179
|
+
Examples:
|
|
180
|
+
```
|
|
181
|
+
# assuming the current time is '2025-06-18 10:53:54'
|
|
182
|
+
dateFromNow(1) -> '2025-06-19 10:53:54'
|
|
183
|
+
dateFromNow(-2) -> '2025-06-16 10:53:54'
|
|
184
|
+
```
|
|
100
185
|
'''
|
|
101
186
|
now = datetime.datetime.utcnow() + datetime.timedelta(hours=3.00)
|
|
102
187
|
return str(now+datetime.timedelta(days=days, hours=hours))[:19]
|
|
103
188
|
|
|
104
189
|
# thanks to airtel & MTN strange date formats, we have this
|
|
105
|
-
def standardizedDate(date:str, fmt=''):
|
|
106
|
-
'
|
|
190
|
+
def standardizedDate(date:str, fmt:str=''):
|
|
191
|
+
'''
|
|
192
|
+
standardize date/timestamp into the `YYYY-MM-DD HH:MM:SS` standard
|
|
193
|
+
Args:
|
|
194
|
+
date(str): the date to standardize
|
|
195
|
+
fmt(str): the format in which the date/timestamp is currently in
|
|
196
|
+
Note:
|
|
197
|
+
Supported formats;
|
|
198
|
+
- DD-MM-YYYY HH:MM:SS
|
|
199
|
+
- MM-DD-YYYY HH:MM:SS
|
|
200
|
+
|
|
201
|
+
- DD-MM-YYYY HH:MM:SS AM
|
|
202
|
+
- MM-DD-YYYY HH:MM:SS AM
|
|
203
|
+
|
|
204
|
+
- DD-MM-YYYY HH:MM
|
|
205
|
+
- MM-DD-YYYY HH:MM
|
|
206
|
+
|
|
207
|
+
- DD-MM-YYYY HH:MM AM
|
|
208
|
+
- MM-DD-YYYY HH:MM AM
|
|
209
|
+
|
|
210
|
+
- YYYY-MM-DD
|
|
211
|
+
- DD-MM-YYYY
|
|
212
|
+
- MM-DD-YYYY
|
|
213
|
+
|
|
214
|
+
- YYYY-MM-DD HH:MM:SS
|
|
215
|
+
- YYYY-MM-DD HH:MM:SS AM
|
|
216
|
+
|
|
217
|
+
- YYYY-MM-DD HH:MM
|
|
218
|
+
- YYYY-MM-DD HH:MM AM
|
|
219
|
+
'''
|
|
107
220
|
_date = None
|
|
108
221
|
|
|
109
222
|
date = date.replace('/','-').replace('"','').replace("'",'')
|
|
@@ -112,7 +225,7 @@ def standardizedDate(date:str, fmt=''):
|
|
|
112
225
|
if len(date)<len('YYYY-MM-DD'): raise ValueError('invalid date given')
|
|
113
226
|
if not fmt: raise ValueError('no format given')
|
|
114
227
|
|
|
115
|
-
fmt = fmt.upper().replace('PM','AM')
|
|
228
|
+
fmt:str|None = fmt.upper().replace('PM','AM')
|
|
116
229
|
|
|
117
230
|
fmt = {
|
|
118
231
|
'DD-MM-YYYY HH:MM:SS':'%d-%m-%Y %H:%M:%S',
|
|
@@ -167,10 +280,18 @@ def _testStandardizeTime():
|
|
|
167
280
|
]:
|
|
168
281
|
print(date,'->',standardizedDate(date,fmt))
|
|
169
282
|
|
|
170
|
-
def lastWeekOn(day:int, humanize:bool=False):
|
|
283
|
+
def lastWeekOn(day:int, humanize:bool=False) -> str:
|
|
171
284
|
'''
|
|
172
|
-
|
|
173
|
-
|
|
285
|
+
get the date(YYYY-MM-DD) on `day`, last week
|
|
286
|
+
Args:
|
|
287
|
+
day(int): the week day; Mon=1, Tue=2, ..., Sun=7
|
|
288
|
+
humanize(bool): humanize the date
|
|
289
|
+
Examples:
|
|
290
|
+
```
|
|
291
|
+
# assuming the current date is '2025-06-18'
|
|
292
|
+
lastWeekOn(3) -> '2025-06-11'
|
|
293
|
+
lastWeekOn(3, True) -> '11 Jun 2025'
|
|
294
|
+
```
|
|
174
295
|
'''
|
|
175
296
|
assert isinstance(day,int) and 1<=day<=7
|
|
176
297
|
|
|
@@ -185,25 +306,36 @@ def lastWeekOn(day:int, humanize:bool=False):
|
|
|
185
306
|
return strDate
|
|
186
307
|
|
|
187
308
|
def secondsSinceEpoch()->int:
|
|
309
|
+
'''get the seconds since epoch'''
|
|
188
310
|
return int(time.time())
|
|
189
311
|
|
|
190
312
|
def minutesSinceEpoch()->int:
|
|
313
|
+
'''get minutes since epoch'''
|
|
191
314
|
return secondsSinceEpoch() // 60
|
|
192
315
|
|
|
193
316
|
def today() -> str:
|
|
317
|
+
'''get current date in YYYY-MM-DD format'''
|
|
194
318
|
return currentTimestamp()[:10]
|
|
195
319
|
|
|
196
320
|
def tomorrow() -> str:
|
|
321
|
+
'''get tomorrow's date in YYYY-MM-DD format'''
|
|
197
322
|
return dateFromNow(days=+1)[:10]
|
|
198
323
|
|
|
199
324
|
def yesterday() -> str:
|
|
325
|
+
'''get yesterday's date in YYYY-MM-DD format'''
|
|
200
326
|
return dateFromNow(days=-1)[:10]
|
|
201
327
|
|
|
202
328
|
def endOfMonth(year:str|int, month:str|int) -> str:
|
|
203
329
|
'''
|
|
204
|
-
get the last date of the given month
|
|
205
|
-
|
|
206
|
-
|
|
330
|
+
get the last date(YYYY-MM-DD) of the given month
|
|
331
|
+
Args:
|
|
332
|
+
year(str|int): 4 characters representing the year
|
|
333
|
+
month(str|int): 1|2 characters representing the month
|
|
334
|
+
Examples:
|
|
335
|
+
```
|
|
336
|
+
endOfMonth(2025, 6) -> '2025-06-30'
|
|
337
|
+
endOfMonth(2025, 2) -> '2025-02-28'
|
|
338
|
+
```
|
|
207
339
|
'''
|
|
208
340
|
|
|
209
341
|
year = f'{year}'
|
|
@@ -228,7 +360,7 @@ def endOfMonth(year:str|int, month:str|int) -> str:
|
|
|
228
360
|
|
|
229
361
|
def endOfCurrentMonth() -> str:
|
|
230
362
|
'''
|
|
231
|
-
get last date of the current month
|
|
363
|
+
get last date(YYYY-MM-DD) of the current month
|
|
232
364
|
'''
|
|
233
365
|
|
|
234
366
|
dateToday = today()
|
|
@@ -239,8 +371,8 @@ def endOfCurrentMonth() -> str:
|
|
|
239
371
|
def dateIsValid(date:str) -> bool:
|
|
240
372
|
'''
|
|
241
373
|
check if a date is valid in the format 'YYYY-MM-DD'
|
|
242
|
-
|
|
243
|
-
|
|
374
|
+
Args:
|
|
375
|
+
date(str): the date to check
|
|
244
376
|
'''
|
|
245
377
|
try:
|
|
246
378
|
standardizedDate(date, 'YYYY-MM-DD')
|
|
@@ -250,12 +382,27 @@ def dateIsValid(date:str) -> bool:
|
|
|
250
382
|
return True
|
|
251
383
|
|
|
252
384
|
def isWeekend(date:str) -> bool:
|
|
385
|
+
'''
|
|
386
|
+
check if `date` is in a weekend (Saturday or Sunday)
|
|
387
|
+
Args:
|
|
388
|
+
date(str): the date to check
|
|
389
|
+
'''
|
|
253
390
|
return humanizeDate(date, includeWeekDay=True).split(' ')[0] in ['Sat','Sun']
|
|
254
391
|
|
|
255
392
|
def isSunday(date:str) -> bool:
|
|
393
|
+
'''
|
|
394
|
+
check if `date` is a Sunday
|
|
395
|
+
Args:
|
|
396
|
+
date(str): the date to check
|
|
397
|
+
'''
|
|
256
398
|
return humanizeDate(date, includeWeekDay=True).split(' ')[0] in ['Sun']
|
|
257
399
|
|
|
258
400
|
def isSaturday(date:str) -> bool:
|
|
401
|
+
'''
|
|
402
|
+
check if `date` is a Saturday
|
|
403
|
+
Args:
|
|
404
|
+
date(str): the date to check
|
|
405
|
+
'''
|
|
259
406
|
return humanizeDate(date, includeWeekDay=True).split(' ')[0] in ['Sat']
|
|
260
407
|
|
|
261
408
|
if __name__=='__main__':
|