ovos-date-parser 0.0.1__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.
- ovos_date_parser/__init__.py +600 -0
- ovos_date_parser/dates_az.py +840 -0
- ovos_date_parser/dates_ca.py +1300 -0
- ovos_date_parser/dates_cs.py +1187 -0
- ovos_date_parser/dates_da.py +783 -0
- ovos_date_parser/dates_de.py +817 -0
- ovos_date_parser/dates_en.py +1190 -0
- ovos_date_parser/dates_es.py +965 -0
- ovos_date_parser/dates_eu.py +927 -0
- ovos_date_parser/dates_fa.py +280 -0
- ovos_date_parser/dates_fr.py +674 -0
- ovos_date_parser/dates_hu.py +80 -0
- ovos_date_parser/dates_it.py +793 -0
- ovos_date_parser/dates_nl.py +943 -0
- ovos_date_parser/dates_pl.py +1075 -0
- ovos_date_parser/dates_pt.py +990 -0
- ovos_date_parser/dates_ru.py +1252 -0
- ovos_date_parser/dates_sl.py +89 -0
- ovos_date_parser/dates_sv.py +799 -0
- ovos_date_parser/dates_uk.py +1464 -0
- ovos_date_parser/res/az/date_time.json +130 -0
- ovos_date_parser/res/az/date_time_test.json +43 -0
- ovos_date_parser/res/ca/date_time.json +130 -0
- ovos_date_parser/res/ca/date_time_test.json +43 -0
- ovos_date_parser/res/cs/date_time.json +129 -0
- ovos_date_parser/res/cs/date_time_test.json +43 -0
- ovos_date_parser/res/da/date_time.json +132 -0
- ovos_date_parser/res/da/date_time_test.json +32 -0
- ovos_date_parser/res/de/date_time.json +136 -0
- ovos_date_parser/res/de/date_time_test.json +43 -0
- ovos_date_parser/res/en/date_time.json +129 -0
- ovos_date_parser/res/en/date_time_test.json +43 -0
- ovos_date_parser/res/eu/date_time.json +112 -0
- ovos_date_parser/res/fa/date_time.json +180 -0
- ovos_date_parser/res/fa/date_time_test.json +36 -0
- ovos_date_parser/res/fr/date_time.json +147 -0
- ovos_date_parser/res/fr/date_time_test.json +43 -0
- ovos_date_parser/res/hu/date_time.json +132 -0
- ovos_date_parser/res/hu/date_time_test.json +43 -0
- ovos_date_parser/res/it/date_time.json +153 -0
- ovos_date_parser/res/it/date_time_test.json +42 -0
- ovos_date_parser/res/nl/date_time.json +136 -0
- ovos_date_parser/res/nl/date_time_test.json +43 -0
- ovos_date_parser/res/pl/date_time.json +129 -0
- ovos_date_parser/res/ru/date_time.json +149 -0
- ovos_date_parser/res/ru/date_time_test.json +43 -0
- ovos_date_parser/res/sl/date_time.json +123 -0
- ovos_date_parser/res/sl/date_time_test.json +43 -0
- ovos_date_parser/res/sv/date_time.json +129 -0
- ovos_date_parser/res/sv/date_time_test.json +43 -0
- ovos_date_parser/res/uk/date_time.json +150 -0
- ovos_date_parser/res/uk/date_time_test.json +23 -0
- ovos_date_parser/version.py +6 -0
- ovos_date_parser-0.0.1.dist-info/METADATA +159 -0
- ovos_date_parser-0.0.1.dist-info/RECORD +57 -0
- ovos_date_parser-0.0.1.dist-info/WHEEL +5 -0
- ovos_date_parser-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from collections import namedtuple
|
|
5
|
+
from datetime import datetime, timedelta, time
|
|
6
|
+
from typing import Optional, Tuple, Union
|
|
7
|
+
|
|
8
|
+
from ovos_utils.lang import standardize_lang_tag
|
|
9
|
+
|
|
10
|
+
from ovos_date_parser.dates_az import (
|
|
11
|
+
extract_datetime_az,
|
|
12
|
+
extract_duration_az,
|
|
13
|
+
nice_duration_az,
|
|
14
|
+
nice_time_az,
|
|
15
|
+
)
|
|
16
|
+
from ovos_date_parser.dates_ca import (
|
|
17
|
+
TimeVariantCA,
|
|
18
|
+
extract_datetime_ca,
|
|
19
|
+
nice_time_ca,
|
|
20
|
+
)
|
|
21
|
+
from ovos_date_parser.dates_cs import (
|
|
22
|
+
extract_duration_cs,
|
|
23
|
+
extract_datetime_cs,
|
|
24
|
+
nice_time_cs
|
|
25
|
+
)
|
|
26
|
+
from ovos_date_parser.dates_da import (
|
|
27
|
+
extract_datetime_da,
|
|
28
|
+
nice_time_da,
|
|
29
|
+
)
|
|
30
|
+
from ovos_date_parser.dates_de import (
|
|
31
|
+
extract_datetime_de,
|
|
32
|
+
extract_duration_de,
|
|
33
|
+
nice_time_de,
|
|
34
|
+
)
|
|
35
|
+
from ovos_date_parser.dates_en import (
|
|
36
|
+
extract_datetime_en,
|
|
37
|
+
extract_duration_en,
|
|
38
|
+
nice_time_en
|
|
39
|
+
)
|
|
40
|
+
from ovos_date_parser.dates_es import (
|
|
41
|
+
extract_datetime_es,
|
|
42
|
+
extract_duration_es,
|
|
43
|
+
nice_time_es,
|
|
44
|
+
)
|
|
45
|
+
from ovos_date_parser.dates_eu import (
|
|
46
|
+
extract_datetime_eu,
|
|
47
|
+
nice_time_eu,
|
|
48
|
+
nice_relative_time_eu,
|
|
49
|
+
)
|
|
50
|
+
from ovos_date_parser.dates_fa import (
|
|
51
|
+
extract_datetime_fa,
|
|
52
|
+
nice_time_fa,
|
|
53
|
+
extract_duration_fa,
|
|
54
|
+
)
|
|
55
|
+
from ovos_date_parser.dates_fr import (
|
|
56
|
+
extract_datetime_fr,
|
|
57
|
+
nice_time_fr
|
|
58
|
+
)
|
|
59
|
+
from ovos_date_parser.dates_hu import nice_time_hu
|
|
60
|
+
from ovos_date_parser.dates_it import (
|
|
61
|
+
extract_datetime_it,
|
|
62
|
+
nice_time_it
|
|
63
|
+
)
|
|
64
|
+
from ovos_date_parser.dates_nl import (
|
|
65
|
+
extract_datetime_nl,
|
|
66
|
+
nice_part_of_day_nl,
|
|
67
|
+
extract_duration_nl,
|
|
68
|
+
nice_time_nl
|
|
69
|
+
)
|
|
70
|
+
from ovos_date_parser.dates_pl import (
|
|
71
|
+
extract_datetime_pl,
|
|
72
|
+
extract_duration_pl,
|
|
73
|
+
nice_time_pl,
|
|
74
|
+
nice_duration_pl
|
|
75
|
+
)
|
|
76
|
+
from ovos_date_parser.dates_pt import (
|
|
77
|
+
extract_datetime_pt,
|
|
78
|
+
extract_duration_pt,
|
|
79
|
+
nice_time_pt
|
|
80
|
+
)
|
|
81
|
+
from ovos_date_parser.dates_ru import (
|
|
82
|
+
extract_datetime_ru,
|
|
83
|
+
extract_duration_ru,
|
|
84
|
+
nice_time_ru,
|
|
85
|
+
nice_duration_ru
|
|
86
|
+
)
|
|
87
|
+
from ovos_date_parser.dates_sv import (
|
|
88
|
+
extract_datetime_sv,
|
|
89
|
+
extract_duration_sv,
|
|
90
|
+
nice_time_sv
|
|
91
|
+
)
|
|
92
|
+
from ovos_date_parser.dates_sl import (
|
|
93
|
+
nice_time_sl
|
|
94
|
+
)
|
|
95
|
+
from ovos_date_parser.dates_uk import (
|
|
96
|
+
extract_datetime_uk,
|
|
97
|
+
extract_duration_uk,
|
|
98
|
+
nice_time_uk,
|
|
99
|
+
nice_duration_uk
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def nice_time(
|
|
104
|
+
dt: datetime,
|
|
105
|
+
lang: str,
|
|
106
|
+
speech: bool = True,
|
|
107
|
+
use_24hour: bool = False,
|
|
108
|
+
use_ampm: bool = False,
|
|
109
|
+
variant: Optional[TimeVariantCA] = None,
|
|
110
|
+
) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Format a time to a comfortable human format.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
dt: date to format (assumes already in local timezone).
|
|
116
|
+
lang: A BCP-47 language code.
|
|
117
|
+
speech: Format for speech (default is True) or display (False).
|
|
118
|
+
use_24hour: Output in 24-hour/military or 12-hour format.
|
|
119
|
+
use_ampm: Include the am/pm for 12-hour format.
|
|
120
|
+
variant: Optional variant for Catalan (ca).
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
The formatted time string.
|
|
124
|
+
"""
|
|
125
|
+
if lang.startswith("az"):
|
|
126
|
+
return nice_time_az(dt, speech, use_24hour, use_ampm)
|
|
127
|
+
if lang.startswith("ca"):
|
|
128
|
+
return nice_time_ca(dt, speech, use_24hour, use_ampm, variant=variant)
|
|
129
|
+
if lang.startswith("cs"):
|
|
130
|
+
return nice_time_cs(dt, speech, use_24hour, use_ampm)
|
|
131
|
+
if lang.startswith("da"):
|
|
132
|
+
return nice_time_da(dt, speech, use_24hour, use_ampm)
|
|
133
|
+
if lang.startswith("de"):
|
|
134
|
+
return nice_time_de(dt, speech, use_24hour, use_ampm)
|
|
135
|
+
if lang.startswith("en"):
|
|
136
|
+
return nice_time_en(dt, speech, use_24hour, use_ampm)
|
|
137
|
+
if lang.startswith("es"):
|
|
138
|
+
return nice_time_es(dt, speech, use_24hour, use_ampm)
|
|
139
|
+
if lang.startswith("eu"):
|
|
140
|
+
return nice_time_eu(dt, speech, use_24hour, use_ampm)
|
|
141
|
+
if lang.startswith("fa"):
|
|
142
|
+
return nice_time_fa(dt, speech, use_24hour, use_ampm)
|
|
143
|
+
if lang.startswith("fr"):
|
|
144
|
+
return nice_time_fr(dt, speech, use_24hour, use_ampm)
|
|
145
|
+
if lang.startswith("hu"):
|
|
146
|
+
return nice_time_hu(dt, speech, use_24hour, use_ampm)
|
|
147
|
+
if lang.startswith("it"):
|
|
148
|
+
return nice_time_it(dt, speech, use_24hour, use_ampm)
|
|
149
|
+
if lang.startswith("nl"):
|
|
150
|
+
return nice_time_nl(dt, speech, use_24hour, use_ampm)
|
|
151
|
+
if lang.startswith("pl"):
|
|
152
|
+
return nice_time_pl(dt, speech, use_24hour, use_ampm)
|
|
153
|
+
if lang.startswith("pt"):
|
|
154
|
+
return nice_time_pt(dt, speech, use_24hour, use_ampm)
|
|
155
|
+
if lang.startswith("ru"):
|
|
156
|
+
return nice_time_ru(dt, speech, use_24hour, use_ampm)
|
|
157
|
+
if lang.startswith("sv"):
|
|
158
|
+
return nice_time_sv(dt, speech, use_24hour, use_ampm)
|
|
159
|
+
if lang.startswith("sl"):
|
|
160
|
+
return nice_time_sl(dt, speech, use_24hour, use_ampm)
|
|
161
|
+
if lang.startswith("uk"):
|
|
162
|
+
return nice_time_uk(dt, speech, use_24hour, use_ampm)
|
|
163
|
+
raise NotImplementedError(f"Unsupported language: {lang}")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def nice_relative_time(when, relative_to, lang):
|
|
167
|
+
"""Create a relative phrase to roughly describe a datetime
|
|
168
|
+
|
|
169
|
+
Examples are "25 seconds", "tomorrow", "7 days".
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
when (datetime): Local timezone
|
|
173
|
+
relative_to (datetime): Baseline for relative time, default is now()
|
|
174
|
+
lang (str, optional): Defaults to "en-us".
|
|
175
|
+
Returns:
|
|
176
|
+
str: Relative description of the given time
|
|
177
|
+
"""
|
|
178
|
+
if lang.startswith("eu"):
|
|
179
|
+
return nice_relative_time_eu(when, relative_to)
|
|
180
|
+
raise NotImplementedError(f"Unsupported language: {lang}")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def nice_duration(
|
|
184
|
+
duration: Union[int, float], lang: str, speech: bool = True
|
|
185
|
+
) -> str:
|
|
186
|
+
"""
|
|
187
|
+
Convert duration in seconds to a nice spoken timespan.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
duration: Time in seconds.
|
|
191
|
+
lang: A BCP-47 language code.
|
|
192
|
+
speech: Format for speech (True) or display (False).
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Timespan as a string.
|
|
196
|
+
"""
|
|
197
|
+
if lang.startswith("az"):
|
|
198
|
+
return nice_duration_az(duration, speech)
|
|
199
|
+
if lang.startswith("pl"):
|
|
200
|
+
return nice_duration_pl(duration, speech)
|
|
201
|
+
if lang.startswith("ru"):
|
|
202
|
+
return nice_duration_ru(duration, speech)
|
|
203
|
+
if lang.startswith("uk"):
|
|
204
|
+
return nice_duration_uk(duration, speech)
|
|
205
|
+
raise NotImplementedError(f"Unsupported language: {lang}")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def extract_duration(
|
|
209
|
+
text: str, lang: str
|
|
210
|
+
) -> Tuple[Optional[timedelta], str]:
|
|
211
|
+
"""
|
|
212
|
+
Convert a phrase into a number of seconds and return the remainder text.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
text: String containing a duration.
|
|
216
|
+
lang: A BCP-47 language code.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
A tuple containing the duration as timedelta and the remaining text.
|
|
220
|
+
"""
|
|
221
|
+
if lang.startswith("az"):
|
|
222
|
+
return extract_duration_az(text)
|
|
223
|
+
if lang.startswith("cs"):
|
|
224
|
+
return extract_duration_cs(text)
|
|
225
|
+
if lang.startswith("de"):
|
|
226
|
+
return extract_duration_de(text)
|
|
227
|
+
if lang.startswith("en"):
|
|
228
|
+
return extract_duration_en(text)
|
|
229
|
+
if lang.startswith("es"):
|
|
230
|
+
return extract_duration_es(text)
|
|
231
|
+
if lang.startswith("fa"):
|
|
232
|
+
return extract_duration_fa(text)
|
|
233
|
+
if lang.startswith("nl"):
|
|
234
|
+
return extract_duration_nl(text)
|
|
235
|
+
if lang.startswith("pl"):
|
|
236
|
+
return extract_duration_pl(text)
|
|
237
|
+
if lang.startswith("pt"):
|
|
238
|
+
return extract_duration_pt(text)
|
|
239
|
+
if lang.startswith("ru"):
|
|
240
|
+
return extract_duration_ru(text)
|
|
241
|
+
if lang.startswith("sv"):
|
|
242
|
+
return extract_duration_sv(text)
|
|
243
|
+
if lang.startswith("uk"):
|
|
244
|
+
return extract_duration_uk(text)
|
|
245
|
+
raise NotImplementedError(f"Unsupported language: {lang}")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def extract_datetime(
|
|
249
|
+
text: str,
|
|
250
|
+
lang: str,
|
|
251
|
+
anchorDate: Optional[datetime] = None,
|
|
252
|
+
default_time: Optional[time] = None,
|
|
253
|
+
) -> Optional[Tuple[datetime, str]]:
|
|
254
|
+
"""
|
|
255
|
+
Extract date and time information from a sentence.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
text: The text to be interpreted.
|
|
259
|
+
lang: The BCP-47 code for the language to use.
|
|
260
|
+
anchorDate: Date to use for relative dating.
|
|
261
|
+
default_time: Time to use if none was found in the input string.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
A tuple with the extracted date as datetime and the leftover string,
|
|
265
|
+
or None if no date or time related text is found.
|
|
266
|
+
"""
|
|
267
|
+
if lang.startswith("az"):
|
|
268
|
+
return extract_datetime_az(text, anchorDate=anchorDate, default_time=default_time)
|
|
269
|
+
if lang.startswith("ca"):
|
|
270
|
+
return extract_datetime_ca(text, anchorDate=anchorDate, default_time=default_time)
|
|
271
|
+
if lang.startswith("cs"):
|
|
272
|
+
return extract_datetime_cs(text, anchorDate=anchorDate, default_time=default_time)
|
|
273
|
+
if lang.startswith("da"):
|
|
274
|
+
return extract_datetime_da(text, anchorDate=anchorDate, default_time=default_time)
|
|
275
|
+
if lang.startswith("de"):
|
|
276
|
+
return extract_datetime_de(text, anchorDate=anchorDate, default_time=default_time)
|
|
277
|
+
if lang.startswith("en"):
|
|
278
|
+
return extract_datetime_en(text, anchorDate=anchorDate, default_time=default_time)
|
|
279
|
+
if lang.startswith("es"):
|
|
280
|
+
return extract_datetime_es(text, anchorDate=anchorDate, default_time=default_time)
|
|
281
|
+
if lang.startswith("fa"):
|
|
282
|
+
return extract_datetime_fa(text, anchorDate=anchorDate, default_time=default_time)
|
|
283
|
+
if lang.startswith("fr"):
|
|
284
|
+
return extract_datetime_fr(text, anchorDate=anchorDate, default_time=default_time)
|
|
285
|
+
if lang.startswith("it"):
|
|
286
|
+
return extract_datetime_it(text, anchorDate=anchorDate, default_time=default_time)
|
|
287
|
+
if lang.startswith("nl"):
|
|
288
|
+
return extract_datetime_nl(text, anchorDate=anchorDate, default_time=default_time)
|
|
289
|
+
if lang.startswith("pl"):
|
|
290
|
+
return extract_datetime_pl(text, anchorDate=anchorDate, default_time=default_time)
|
|
291
|
+
if lang.startswith("pt"):
|
|
292
|
+
return extract_datetime_pl(text, anchorDate=anchorDate, default_time=default_time)
|
|
293
|
+
if lang.startswith("ru"):
|
|
294
|
+
return extract_datetime_ru(text, anchorDate=anchorDate, default_time=default_time)
|
|
295
|
+
if lang.startswith("sv"):
|
|
296
|
+
return extract_datetime_sv(text, anchorDate=anchorDate, default_time=default_time)
|
|
297
|
+
if lang.startswith("uk"):
|
|
298
|
+
return extract_datetime_uk(text, anchorDate=anchorDate, default_time=default_time)
|
|
299
|
+
raise NotImplementedError(f"Unsupported language: {lang}")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
NUMBER_TUPLE = namedtuple(
|
|
303
|
+
'number',
|
|
304
|
+
('x, xx, x0, x_in_x0, xxx, x00, x_in_x00, xx00, xx_in_xx00, x000, ' +
|
|
305
|
+
'x_in_x000, x0_in_x000, x_in_0x00'))
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class DateTimeFormat:
|
|
309
|
+
def __init__(self, config_path):
|
|
310
|
+
self.lang_config = {}
|
|
311
|
+
self.config_path = config_path
|
|
312
|
+
|
|
313
|
+
def cache(self, lang):
|
|
314
|
+
lang = lang.split("-")[0]
|
|
315
|
+
# TODO - find closest lang code
|
|
316
|
+
if lang not in self.lang_config:
|
|
317
|
+
with open(self.config_path + '/' + lang + '/date_time.json',
|
|
318
|
+
'r', encoding='utf8') as lang_config_file:
|
|
319
|
+
self.lang_config[lang] = json.loads(
|
|
320
|
+
lang_config_file.read())
|
|
321
|
+
|
|
322
|
+
for x in ['decade_format', 'hundreds_format', 'thousand_format',
|
|
323
|
+
'year_format']:
|
|
324
|
+
i = 1
|
|
325
|
+
while self.lang_config[lang][x].get(str(i)):
|
|
326
|
+
self.lang_config[lang][x][str(i)]['re'] = (
|
|
327
|
+
re.compile(self.lang_config[lang][x][str(i)]['match']
|
|
328
|
+
))
|
|
329
|
+
i = i + 1
|
|
330
|
+
|
|
331
|
+
def _number_strings(self, number, lang):
|
|
332
|
+
x = (self.lang_config[lang]['number'].get(str(number % 10)) or
|
|
333
|
+
str(number % 10))
|
|
334
|
+
xx = (self.lang_config[lang]['number'].get(str(number % 100)) or
|
|
335
|
+
str(number % 100))
|
|
336
|
+
x_in_x0 = self.lang_config[lang]['number'].get(
|
|
337
|
+
str(int(number % 100 / 10))) or str(int(number % 100 / 10))
|
|
338
|
+
x0 = (self.lang_config[lang]['number'].get(
|
|
339
|
+
str(int(number % 100 / 10) * 10)) or
|
|
340
|
+
str(int(number % 100 / 10) * 10))
|
|
341
|
+
xxx = (self.lang_config[lang]['number'].get(str(number % 1000)) or
|
|
342
|
+
str(number % 1000))
|
|
343
|
+
x00 = (self.lang_config[lang]['number'].get(str(int(
|
|
344
|
+
number % 1000 / 100) * 100)) or
|
|
345
|
+
str(int(number % 1000 / 100) * 100))
|
|
346
|
+
x_in_x00 = self.lang_config[lang]['number'].get(str(int(
|
|
347
|
+
number % 1000 / 100))) or str(int(number % 1000 / 100))
|
|
348
|
+
xx00 = self.lang_config[lang]['number'].get(str(int(
|
|
349
|
+
number % 10000 / 100) * 100)) or str(int(number % 10000 / 100) *
|
|
350
|
+
100)
|
|
351
|
+
xx_in_xx00 = self.lang_config[lang]['number'].get(str(int(
|
|
352
|
+
number % 10000 / 100))) or str(int(number % 10000 / 100))
|
|
353
|
+
x000 = (self.lang_config[lang]['number'].get(str(int(
|
|
354
|
+
number % 10000 / 1000) * 1000)) or
|
|
355
|
+
str(int(number % 10000 / 1000) * 1000))
|
|
356
|
+
x_in_x000 = self.lang_config[lang]['number'].get(str(int(
|
|
357
|
+
number % 10000 / 1000))) or str(int(number % 10000 / 1000))
|
|
358
|
+
x0_in_x000 = self.lang_config[lang]['number'].get(str(int(
|
|
359
|
+
number % 10000 / 1000) * 10)) or str(int(number % 10000 / 1000) * 10)
|
|
360
|
+
x_in_0x00 = self.lang_config[lang]['number'].get(str(int(
|
|
361
|
+
number % 1000 / 100)) or str(int(number % 1000 / 100)))
|
|
362
|
+
|
|
363
|
+
return NUMBER_TUPLE(
|
|
364
|
+
x, xx, x0, x_in_x0, xxx, x00, x_in_x00, xx00, xx_in_xx00, x000,
|
|
365
|
+
x_in_x000, x0_in_x000, x_in_0x00)
|
|
366
|
+
|
|
367
|
+
def _format_string(self, number, format_section, lang):
|
|
368
|
+
s = self.lang_config[lang][format_section]['default']
|
|
369
|
+
i = 1
|
|
370
|
+
while self.lang_config[lang][format_section].get(str(i)):
|
|
371
|
+
e = self.lang_config[lang][format_section][str(i)]
|
|
372
|
+
if e['re'].match(str(number)):
|
|
373
|
+
return e['format']
|
|
374
|
+
i = i + 1
|
|
375
|
+
return s
|
|
376
|
+
|
|
377
|
+
def _decade_format(self, number, number_tuple, lang):
|
|
378
|
+
s = self._format_string(number % 100, 'decade_format', lang)
|
|
379
|
+
decade = s.format(x=number_tuple.x, xx=number_tuple.xx,
|
|
380
|
+
x0=number_tuple.x0, x_in_x0=number_tuple.x_in_x0,
|
|
381
|
+
number=str(number % 100))
|
|
382
|
+
return s.format(x=number_tuple.x, xx=number_tuple.xx,
|
|
383
|
+
x0=number_tuple.x0, x_in_x0=number_tuple.x_in_x0,
|
|
384
|
+
number=str(number % 100))
|
|
385
|
+
|
|
386
|
+
def _number_format_hundreds(self, number, number_tuple, lang,
|
|
387
|
+
formatted_decade):
|
|
388
|
+
s = self._format_string(number % 1000, 'hundreds_format', lang)
|
|
389
|
+
hundreds = s.format(xxx=number_tuple.xxx, x00=number_tuple.x00,
|
|
390
|
+
x_in_x00=number_tuple.x_in_x00,
|
|
391
|
+
formatted_decade=formatted_decade,
|
|
392
|
+
number=str(number % 1000))
|
|
393
|
+
return s.format(xxx=number_tuple.xxx, x00=number_tuple.x00,
|
|
394
|
+
x_in_x00=number_tuple.x_in_x00,
|
|
395
|
+
formatted_decade=formatted_decade,
|
|
396
|
+
number=str(number % 1000))
|
|
397
|
+
|
|
398
|
+
def _number_format_thousand(self, number, number_tuple, lang,
|
|
399
|
+
formatted_decade, formatted_hundreds):
|
|
400
|
+
s = self._format_string(number % 10000, 'thousand_format', lang)
|
|
401
|
+
return s.format(x_in_x00=number_tuple.x_in_x00,
|
|
402
|
+
xx00=number_tuple.xx00,
|
|
403
|
+
xx_in_xx00=number_tuple.xx_in_xx00,
|
|
404
|
+
x000=number_tuple.x000,
|
|
405
|
+
x_in_x000=number_tuple.x_in_x000,
|
|
406
|
+
x0_in_x000=number_tuple.x0_in_x000,
|
|
407
|
+
x_in_0x00=number_tuple.x_in_0x00,
|
|
408
|
+
formatted_decade=formatted_decade,
|
|
409
|
+
formatted_hundreds=formatted_hundreds,
|
|
410
|
+
number=str(number % 10000))
|
|
411
|
+
|
|
412
|
+
def date_format(self, dt, lang, now):
|
|
413
|
+
format_str = 'date_full'
|
|
414
|
+
if now:
|
|
415
|
+
if dt.year == now.year:
|
|
416
|
+
format_str = 'date_full_no_year'
|
|
417
|
+
if dt.month == now.month and dt.day > now.day:
|
|
418
|
+
format_str = 'date_full_no_year_month'
|
|
419
|
+
|
|
420
|
+
tomorrow = now + timedelta(days=1)
|
|
421
|
+
yesterday = now - timedelta(days=1)
|
|
422
|
+
if tomorrow.date() == dt.date():
|
|
423
|
+
format_str = 'tomorrow'
|
|
424
|
+
elif now.date() == dt.date():
|
|
425
|
+
format_str = 'today'
|
|
426
|
+
elif yesterday.date() == dt.date():
|
|
427
|
+
format_str = 'yesterday'
|
|
428
|
+
|
|
429
|
+
return self.lang_config[lang]['date_format'][format_str].format(
|
|
430
|
+
weekday=self.lang_config[lang]['weekday'][str(dt.weekday())],
|
|
431
|
+
month=self.lang_config[lang]['month'][str(dt.month)],
|
|
432
|
+
day=self.lang_config[lang]['date'][str(dt.day)],
|
|
433
|
+
formatted_year=self.year_format(dt, lang, False))
|
|
434
|
+
|
|
435
|
+
def date_time_format(self, dt, lang, now, use_24hour, use_ampm):
|
|
436
|
+
date_str = self.date_format(dt, lang, now)
|
|
437
|
+
time_str = nice_time(dt, lang, use_24hour=use_24hour,
|
|
438
|
+
use_ampm=use_ampm)
|
|
439
|
+
return self.lang_config[lang]['date_time_format']['date_time'].format(
|
|
440
|
+
formatted_date=date_str, formatted_time=time_str)
|
|
441
|
+
|
|
442
|
+
def year_format(self, dt, lang, bc):
|
|
443
|
+
number_tuple = self._number_strings(dt.year, lang)
|
|
444
|
+
formatted_bc = (
|
|
445
|
+
self.lang_config[lang]['year_format']['bc'] if bc else '')
|
|
446
|
+
formatted_decade = self._decade_format(
|
|
447
|
+
dt.year, number_tuple, lang)
|
|
448
|
+
formatted_hundreds = self._number_format_hundreds(
|
|
449
|
+
dt.year, number_tuple, lang, formatted_decade)
|
|
450
|
+
formatted_thousand = self._number_format_thousand(
|
|
451
|
+
dt.year, number_tuple, lang, formatted_decade, formatted_hundreds)
|
|
452
|
+
|
|
453
|
+
s = self._format_string(dt.year, 'year_format', lang)
|
|
454
|
+
return re.sub(' +', ' ',
|
|
455
|
+
s.format(
|
|
456
|
+
year=str(dt.year),
|
|
457
|
+
century=str(int(dt.year / 100)),
|
|
458
|
+
decade=str(dt.year % 100),
|
|
459
|
+
formatted_hundreds=formatted_hundreds,
|
|
460
|
+
formatted_decade=formatted_decade,
|
|
461
|
+
formatted_thousand=formatted_thousand,
|
|
462
|
+
bc=formatted_bc)).strip()
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
date_time_format = DateTimeFormat(os.path.join(os.path.dirname(__file__), 'res'))
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def nice_date(dt, lang, now=None):
|
|
469
|
+
"""
|
|
470
|
+
Format a datetime to a pronounceable date
|
|
471
|
+
|
|
472
|
+
For example, generates 'tuesday, june the fifth, 2018'
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
dt (datetime): date to format (assumes already in local timezone)
|
|
476
|
+
lang (str, optional): an optional BCP-47 language code, if omitted
|
|
477
|
+
the default language will be used.
|
|
478
|
+
now (datetime): Current date. If provided, the returned date for speech
|
|
479
|
+
will be shortened accordingly: No year is returned if now is in the
|
|
480
|
+
same year as td, no month is returned if now is in the same month
|
|
481
|
+
as td. If now and td is the same day, 'today' is returned.
|
|
482
|
+
|
|
483
|
+
Returns:
|
|
484
|
+
(str): The formatted date string
|
|
485
|
+
"""
|
|
486
|
+
full_code = standardize_lang_tag(lang)
|
|
487
|
+
date_time_format.cache(full_code)
|
|
488
|
+
|
|
489
|
+
return date_time_format.date_format(dt, full_code, now)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def nice_date_time(dt, lang, now=None, use_24hour=False,
|
|
493
|
+
use_ampm=False):
|
|
494
|
+
"""
|
|
495
|
+
Format a datetime to a pronounceable date and time
|
|
496
|
+
|
|
497
|
+
For example, generate 'tuesday, june the fifth, 2018 at five thirty'
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
dt (datetime): date to format (assumes already in local timezone)
|
|
501
|
+
lang (str, optional): an optional BCP-47 language code, if omitted
|
|
502
|
+
the default language will be used.
|
|
503
|
+
now (datetime): Current date. If provided, the returned date for
|
|
504
|
+
speech will be shortened accordingly: No year is returned if
|
|
505
|
+
now is in the same year as td, no month is returned if now is
|
|
506
|
+
in the same month as td. If now and td is the same day, 'today'
|
|
507
|
+
is returned.
|
|
508
|
+
use_24hour (bool): output in 24-hour/military or 12-hour format
|
|
509
|
+
use_ampm (bool): include the am/pm for 12-hour format
|
|
510
|
+
Returns:
|
|
511
|
+
(str): The formatted date time string
|
|
512
|
+
"""
|
|
513
|
+
|
|
514
|
+
full_code = standardize_lang_tag(lang)
|
|
515
|
+
date_time_format.cache(full_code)
|
|
516
|
+
|
|
517
|
+
return date_time_format.date_time_format(dt, full_code, now, use_24hour,
|
|
518
|
+
use_ampm)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def nice_day(dt, lang, date_format='DMY', include_month=True):
|
|
522
|
+
if include_month:
|
|
523
|
+
month = nice_month(dt, lang, date_format)
|
|
524
|
+
if date_format == 'MDY':
|
|
525
|
+
return "{} {}".format(month, dt.strftime("%d"))
|
|
526
|
+
else:
|
|
527
|
+
return "{} {}".format(dt.strftime("%d"), month)
|
|
528
|
+
return dt.strftime("%d")
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def nice_weekday(dt, lang):
|
|
532
|
+
full_code = standardize_lang_tag(lang)
|
|
533
|
+
date_time_format.cache(full_code)
|
|
534
|
+
|
|
535
|
+
if full_code in date_time_format.lang_config.keys():
|
|
536
|
+
localized_day_names = list(
|
|
537
|
+
date_time_format.lang_config[lang]['weekday'].values())
|
|
538
|
+
weekday = localized_day_names[dt.weekday()]
|
|
539
|
+
else:
|
|
540
|
+
weekday = dt.strftime("%A")
|
|
541
|
+
return weekday.capitalize()
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def nice_month(dt, lang, date_format='MDY'):
|
|
545
|
+
full_code = standardize_lang_tag(lang)
|
|
546
|
+
date_time_format.cache(full_code)
|
|
547
|
+
|
|
548
|
+
if full_code in date_time_format.lang_config.keys():
|
|
549
|
+
localized_month_names = date_time_format.lang_config[lang]['month']
|
|
550
|
+
month = localized_month_names[str(int(dt.strftime("%m")))]
|
|
551
|
+
else:
|
|
552
|
+
month = dt.strftime("%B")
|
|
553
|
+
return month.capitalize()
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def nice_year(dt, lang, bc=False):
|
|
557
|
+
"""
|
|
558
|
+
Format a datetime to a pronounceable year
|
|
559
|
+
|
|
560
|
+
For example, generate 'nineteen-hundred and eighty-four' for year 1984
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
dt (datetime): date to format (assumes already in local timezone)
|
|
564
|
+
lang (str, optional): an optional BCP-47 language code, if omitted
|
|
565
|
+
the default language will be used.
|
|
566
|
+
bc (bool) pust B.C. after the year (python does not support dates
|
|
567
|
+
B.C. in datetime)
|
|
568
|
+
Returns:
|
|
569
|
+
(str): The formatted year string
|
|
570
|
+
"""
|
|
571
|
+
|
|
572
|
+
full_code = standardize_lang_tag(lang)
|
|
573
|
+
date_time_format.cache(full_code)
|
|
574
|
+
return date_time_format.year_format(dt, full_code, bc)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def get_date_strings(dt, lang, date_format='MDY', time_format="full"):
|
|
578
|
+
lang = standardize_lang_tag(lang).split("-")[0]
|
|
579
|
+
timestr = nice_time(dt, lang, speech=False,
|
|
580
|
+
use_24hour=time_format == "full")
|
|
581
|
+
monthstr = nice_month(dt, lang, date_format)
|
|
582
|
+
weekdaystr = nice_weekday(dt, lang)
|
|
583
|
+
yearstr = dt.strftime("%Y")
|
|
584
|
+
daystr = nice_day(dt, date_format=date_format, include_month=False, lang=lang)
|
|
585
|
+
if date_format == 'MDY':
|
|
586
|
+
dtstr = dt.strftime("%-m/%-d/%Y")
|
|
587
|
+
elif date_format == 'DMY':
|
|
588
|
+
dtstr = dt.strftime("%d/%-m/%-Y")
|
|
589
|
+
elif date_format == 'YMD':
|
|
590
|
+
dtstr = dt.strftime("%Y/%-m/%-d")
|
|
591
|
+
else:
|
|
592
|
+
raise ValueError("invalid date_format")
|
|
593
|
+
return {
|
|
594
|
+
"date_string": dtstr,
|
|
595
|
+
"time_string": timestr,
|
|
596
|
+
"month_string": monthstr,
|
|
597
|
+
"day_string": daystr,
|
|
598
|
+
'year_string': yearstr,
|
|
599
|
+
"weekday_string": weekdaystr
|
|
600
|
+
}
|