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.
Files changed (57) hide show
  1. ovos_date_parser/__init__.py +600 -0
  2. ovos_date_parser/dates_az.py +840 -0
  3. ovos_date_parser/dates_ca.py +1300 -0
  4. ovos_date_parser/dates_cs.py +1187 -0
  5. ovos_date_parser/dates_da.py +783 -0
  6. ovos_date_parser/dates_de.py +817 -0
  7. ovos_date_parser/dates_en.py +1190 -0
  8. ovos_date_parser/dates_es.py +965 -0
  9. ovos_date_parser/dates_eu.py +927 -0
  10. ovos_date_parser/dates_fa.py +280 -0
  11. ovos_date_parser/dates_fr.py +674 -0
  12. ovos_date_parser/dates_hu.py +80 -0
  13. ovos_date_parser/dates_it.py +793 -0
  14. ovos_date_parser/dates_nl.py +943 -0
  15. ovos_date_parser/dates_pl.py +1075 -0
  16. ovos_date_parser/dates_pt.py +990 -0
  17. ovos_date_parser/dates_ru.py +1252 -0
  18. ovos_date_parser/dates_sl.py +89 -0
  19. ovos_date_parser/dates_sv.py +799 -0
  20. ovos_date_parser/dates_uk.py +1464 -0
  21. ovos_date_parser/res/az/date_time.json +130 -0
  22. ovos_date_parser/res/az/date_time_test.json +43 -0
  23. ovos_date_parser/res/ca/date_time.json +130 -0
  24. ovos_date_parser/res/ca/date_time_test.json +43 -0
  25. ovos_date_parser/res/cs/date_time.json +129 -0
  26. ovos_date_parser/res/cs/date_time_test.json +43 -0
  27. ovos_date_parser/res/da/date_time.json +132 -0
  28. ovos_date_parser/res/da/date_time_test.json +32 -0
  29. ovos_date_parser/res/de/date_time.json +136 -0
  30. ovos_date_parser/res/de/date_time_test.json +43 -0
  31. ovos_date_parser/res/en/date_time.json +129 -0
  32. ovos_date_parser/res/en/date_time_test.json +43 -0
  33. ovos_date_parser/res/eu/date_time.json +112 -0
  34. ovos_date_parser/res/fa/date_time.json +180 -0
  35. ovos_date_parser/res/fa/date_time_test.json +36 -0
  36. ovos_date_parser/res/fr/date_time.json +147 -0
  37. ovos_date_parser/res/fr/date_time_test.json +43 -0
  38. ovos_date_parser/res/hu/date_time.json +132 -0
  39. ovos_date_parser/res/hu/date_time_test.json +43 -0
  40. ovos_date_parser/res/it/date_time.json +153 -0
  41. ovos_date_parser/res/it/date_time_test.json +42 -0
  42. ovos_date_parser/res/nl/date_time.json +136 -0
  43. ovos_date_parser/res/nl/date_time_test.json +43 -0
  44. ovos_date_parser/res/pl/date_time.json +129 -0
  45. ovos_date_parser/res/ru/date_time.json +149 -0
  46. ovos_date_parser/res/ru/date_time_test.json +43 -0
  47. ovos_date_parser/res/sl/date_time.json +123 -0
  48. ovos_date_parser/res/sl/date_time_test.json +43 -0
  49. ovos_date_parser/res/sv/date_time.json +129 -0
  50. ovos_date_parser/res/sv/date_time_test.json +43 -0
  51. ovos_date_parser/res/uk/date_time.json +150 -0
  52. ovos_date_parser/res/uk/date_time_test.json +23 -0
  53. ovos_date_parser/version.py +6 -0
  54. ovos_date_parser-0.0.1.dist-info/METADATA +159 -0
  55. ovos_date_parser-0.0.1.dist-info/RECORD +57 -0
  56. ovos_date_parser-0.0.1.dist-info/WHEEL +5 -0
  57. 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
+ }