Edit on GitHub

sqlglot.time

  1from __future__ import annotations
  2import datetime
  3import typing as t
  4
  5# The generic time format is based on python time.strftime.
  6# https://docs.python.org/3/library/time.html#time.strftime
  7from sqlglot.trie import TrieResult, in_trie, new_trie
  8
  9
 10def format_time(
 11    string: str, mapping: dict[str, str], trie: dict[t.Any, t.Any] | None = None
 12) -> str | None:
 13    """
 14    Converts a time string given a mapping.
 15
 16    Examples:
 17        >>> format_time("%Y", {"%Y": "YYYY"})
 18        'YYYY'
 19
 20        Args:
 21            mapping: dictionary of time format to target time format.
 22            trie: optional trie, can be passed in for performance.
 23
 24        Returns:
 25            The converted time string.
 26    """
 27    if not string:
 28        return None
 29
 30    start = 0
 31    end = 1
 32    size = len(string)
 33    trie = trie or new_trie(mapping)
 34    current = trie
 35    chunks: list[str] = []
 36    sym = None
 37
 38    while end <= size:
 39        chars = string[start:end]
 40        result, current = in_trie(current, chars[-1])
 41
 42        if result == TrieResult.FAILED:
 43            if sym:
 44                end -= 1
 45                chars = sym
 46                sym = None
 47            else:
 48                chars = chars[0]
 49                end = start + 1
 50
 51            start += len(chars)
 52            chunks.append(chars)
 53            current = trie
 54        elif result == TrieResult.EXISTS:
 55            sym = chars
 56
 57        end += 1
 58
 59        if result != TrieResult.FAILED and end > size:
 60            chunks.append(chars)
 61
 62    return "".join(mapping.get(chars, chars) for chars in chunks)
 63
 64
 65TIMEZONES: set[str] = {
 66    tz.lower()
 67    for tz in (
 68        "Africa/Abidjan",
 69        "Africa/Accra",
 70        "Africa/Addis_Ababa",
 71        "Africa/Algiers",
 72        "Africa/Asmara",
 73        "Africa/Asmera",
 74        "Africa/Bamako",
 75        "Africa/Bangui",
 76        "Africa/Banjul",
 77        "Africa/Bissau",
 78        "Africa/Blantyre",
 79        "Africa/Brazzaville",
 80        "Africa/Bujumbura",
 81        "Africa/Cairo",
 82        "Africa/Casablanca",
 83        "Africa/Ceuta",
 84        "Africa/Conakry",
 85        "Africa/Dakar",
 86        "Africa/Dar_es_Salaam",
 87        "Africa/Djibouti",
 88        "Africa/Douala",
 89        "Africa/El_Aaiun",
 90        "Africa/Freetown",
 91        "Africa/Gaborone",
 92        "Africa/Harare",
 93        "Africa/Johannesburg",
 94        "Africa/Juba",
 95        "Africa/Kampala",
 96        "Africa/Khartoum",
 97        "Africa/Kigali",
 98        "Africa/Kinshasa",
 99        "Africa/Lagos",
100        "Africa/Libreville",
101        "Africa/Lome",
102        "Africa/Luanda",
103        "Africa/Lubumbashi",
104        "Africa/Lusaka",
105        "Africa/Malabo",
106        "Africa/Maputo",
107        "Africa/Maseru",
108        "Africa/Mbabane",
109        "Africa/Mogadishu",
110        "Africa/Monrovia",
111        "Africa/Nairobi",
112        "Africa/Ndjamena",
113        "Africa/Niamey",
114        "Africa/Nouakchott",
115        "Africa/Ouagadougou",
116        "Africa/Porto-Novo",
117        "Africa/Sao_Tome",
118        "Africa/Timbuktu",
119        "Africa/Tripoli",
120        "Africa/Tunis",
121        "Africa/Windhoek",
122        "America/Adak",
123        "America/Anchorage",
124        "America/Anguilla",
125        "America/Antigua",
126        "America/Araguaina",
127        "America/Argentina/Buenos_Aires",
128        "America/Argentina/Catamarca",
129        "America/Argentina/ComodRivadavia",
130        "America/Argentina/Cordoba",
131        "America/Argentina/Jujuy",
132        "America/Argentina/La_Rioja",
133        "America/Argentina/Mendoza",
134        "America/Argentina/Rio_Gallegos",
135        "America/Argentina/Salta",
136        "America/Argentina/San_Juan",
137        "America/Argentina/San_Luis",
138        "America/Argentina/Tucuman",
139        "America/Argentina/Ushuaia",
140        "America/Aruba",
141        "America/Asuncion",
142        "America/Atikokan",
143        "America/Atka",
144        "America/Bahia",
145        "America/Bahia_Banderas",
146        "America/Barbados",
147        "America/Belem",
148        "America/Belize",
149        "America/Blanc-Sablon",
150        "America/Boa_Vista",
151        "America/Bogota",
152        "America/Boise",
153        "America/Buenos_Aires",
154        "America/Cambridge_Bay",
155        "America/Campo_Grande",
156        "America/Cancun",
157        "America/Caracas",
158        "America/Catamarca",
159        "America/Cayenne",
160        "America/Cayman",
161        "America/Chicago",
162        "America/Chihuahua",
163        "America/Ciudad_Juarez",
164        "America/Coral_Harbour",
165        "America/Cordoba",
166        "America/Costa_Rica",
167        "America/Creston",
168        "America/Cuiaba",
169        "America/Curacao",
170        "America/Danmarkshavn",
171        "America/Dawson",
172        "America/Dawson_Creek",
173        "America/Denver",
174        "America/Detroit",
175        "America/Dominica",
176        "America/Edmonton",
177        "America/Eirunepe",
178        "America/El_Salvador",
179        "America/Ensenada",
180        "America/Fort_Nelson",
181        "America/Fort_Wayne",
182        "America/Fortaleza",
183        "America/Glace_Bay",
184        "America/Godthab",
185        "America/Goose_Bay",
186        "America/Grand_Turk",
187        "America/Grenada",
188        "America/Guadeloupe",
189        "America/Guatemala",
190        "America/Guayaquil",
191        "America/Guyana",
192        "America/Halifax",
193        "America/Havana",
194        "America/Hermosillo",
195        "America/Indiana/Indianapolis",
196        "America/Indiana/Knox",
197        "America/Indiana/Marengo",
198        "America/Indiana/Petersburg",
199        "America/Indiana/Tell_City",
200        "America/Indiana/Vevay",
201        "America/Indiana/Vincennes",
202        "America/Indiana/Winamac",
203        "America/Indianapolis",
204        "America/Inuvik",
205        "America/Iqaluit",
206        "America/Jamaica",
207        "America/Jujuy",
208        "America/Juneau",
209        "America/Kentucky/Louisville",
210        "America/Kentucky/Monticello",
211        "America/Knox_IN",
212        "America/Kralendijk",
213        "America/La_Paz",
214        "America/Lima",
215        "America/Los_Angeles",
216        "America/Louisville",
217        "America/Lower_Princes",
218        "America/Maceio",
219        "America/Managua",
220        "America/Manaus",
221        "America/Marigot",
222        "America/Martinique",
223        "America/Matamoros",
224        "America/Mazatlan",
225        "America/Mendoza",
226        "America/Menominee",
227        "America/Merida",
228        "America/Metlakatla",
229        "America/Mexico_City",
230        "America/Miquelon",
231        "America/Moncton",
232        "America/Monterrey",
233        "America/Montevideo",
234        "America/Montreal",
235        "America/Montserrat",
236        "America/Nassau",
237        "America/New_York",
238        "America/Nipigon",
239        "America/Nome",
240        "America/Noronha",
241        "America/North_Dakota/Beulah",
242        "America/North_Dakota/Center",
243        "America/North_Dakota/New_Salem",
244        "America/Nuuk",
245        "America/Ojinaga",
246        "America/Panama",
247        "America/Pangnirtung",
248        "America/Paramaribo",
249        "America/Phoenix",
250        "America/Port-au-Prince",
251        "America/Port_of_Spain",
252        "America/Porto_Acre",
253        "America/Porto_Velho",
254        "America/Puerto_Rico",
255        "America/Punta_Arenas",
256        "America/Rainy_River",
257        "America/Rankin_Inlet",
258        "America/Recife",
259        "America/Regina",
260        "America/Resolute",
261        "America/Rio_Branco",
262        "America/Rosario",
263        "America/Santa_Isabel",
264        "America/Santarem",
265        "America/Santiago",
266        "America/Santo_Domingo",
267        "America/Sao_Paulo",
268        "America/Scoresbysund",
269        "America/Shiprock",
270        "America/Sitka",
271        "America/St_Barthelemy",
272        "America/St_Johns",
273        "America/St_Kitts",
274        "America/St_Lucia",
275        "America/St_Thomas",
276        "America/St_Vincent",
277        "America/Swift_Current",
278        "America/Tegucigalpa",
279        "America/Thule",
280        "America/Thunder_Bay",
281        "America/Tijuana",
282        "America/Toronto",
283        "America/Tortola",
284        "America/Vancouver",
285        "America/Virgin",
286        "America/Whitehorse",
287        "America/Winnipeg",
288        "America/Yakutat",
289        "America/Yellowknife",
290        "Antarctica/Casey",
291        "Antarctica/Davis",
292        "Antarctica/DumontDUrville",
293        "Antarctica/Macquarie",
294        "Antarctica/Mawson",
295        "Antarctica/McMurdo",
296        "Antarctica/Palmer",
297        "Antarctica/Rothera",
298        "Antarctica/South_Pole",
299        "Antarctica/Syowa",
300        "Antarctica/Troll",
301        "Antarctica/Vostok",
302        "Arctic/Longyearbyen",
303        "Asia/Aden",
304        "Asia/Almaty",
305        "Asia/Amman",
306        "Asia/Anadyr",
307        "Asia/Aqtau",
308        "Asia/Aqtobe",
309        "Asia/Ashgabat",
310        "Asia/Ashkhabad",
311        "Asia/Atyrau",
312        "Asia/Baghdad",
313        "Asia/Bahrain",
314        "Asia/Baku",
315        "Asia/Bangkok",
316        "Asia/Barnaul",
317        "Asia/Beirut",
318        "Asia/Bishkek",
319        "Asia/Brunei",
320        "Asia/Calcutta",
321        "Asia/Chita",
322        "Asia/Choibalsan",
323        "Asia/Chongqing",
324        "Asia/Chungking",
325        "Asia/Colombo",
326        "Asia/Dacca",
327        "Asia/Damascus",
328        "Asia/Dhaka",
329        "Asia/Dili",
330        "Asia/Dubai",
331        "Asia/Dushanbe",
332        "Asia/Famagusta",
333        "Asia/Gaza",
334        "Asia/Harbin",
335        "Asia/Hebron",
336        "Asia/Ho_Chi_Minh",
337        "Asia/Hong_Kong",
338        "Asia/Hovd",
339        "Asia/Irkutsk",
340        "Asia/Istanbul",
341        "Asia/Jakarta",
342        "Asia/Jayapura",
343        "Asia/Jerusalem",
344        "Asia/Kabul",
345        "Asia/Kamchatka",
346        "Asia/Karachi",
347        "Asia/Kashgar",
348        "Asia/Kathmandu",
349        "Asia/Katmandu",
350        "Asia/Khandyga",
351        "Asia/Kolkata",
352        "Asia/Krasnoyarsk",
353        "Asia/Kuala_Lumpur",
354        "Asia/Kuching",
355        "Asia/Kuwait",
356        "Asia/Macao",
357        "Asia/Macau",
358        "Asia/Magadan",
359        "Asia/Makassar",
360        "Asia/Manila",
361        "Asia/Muscat",
362        "Asia/Nicosia",
363        "Asia/Novokuznetsk",
364        "Asia/Novosibirsk",
365        "Asia/Omsk",
366        "Asia/Oral",
367        "Asia/Phnom_Penh",
368        "Asia/Pontianak",
369        "Asia/Pyongyang",
370        "Asia/Qatar",
371        "Asia/Qostanay",
372        "Asia/Qyzylorda",
373        "Asia/Rangoon",
374        "Asia/Riyadh",
375        "Asia/Saigon",
376        "Asia/Sakhalin",
377        "Asia/Samarkand",
378        "Asia/Seoul",
379        "Asia/Shanghai",
380        "Asia/Singapore",
381        "Asia/Srednekolymsk",
382        "Asia/Taipei",
383        "Asia/Tashkent",
384        "Asia/Tbilisi",
385        "Asia/Tehran",
386        "Asia/Tel_Aviv",
387        "Asia/Thimbu",
388        "Asia/Thimphu",
389        "Asia/Tokyo",
390        "Asia/Tomsk",
391        "Asia/Ujung_Pandang",
392        "Asia/Ulaanbaatar",
393        "Asia/Ulan_Bator",
394        "Asia/Urumqi",
395        "Asia/Ust-Nera",
396        "Asia/Vientiane",
397        "Asia/Vladivostok",
398        "Asia/Yakutsk",
399        "Asia/Yangon",
400        "Asia/Yekaterinburg",
401        "Asia/Yerevan",
402        "Atlantic/Azores",
403        "Atlantic/Bermuda",
404        "Atlantic/Canary",
405        "Atlantic/Cape_Verde",
406        "Atlantic/Faeroe",
407        "Atlantic/Faroe",
408        "Atlantic/Jan_Mayen",
409        "Atlantic/Madeira",
410        "Atlantic/Reykjavik",
411        "Atlantic/South_Georgia",
412        "Atlantic/St_Helena",
413        "Atlantic/Stanley",
414        "Australia/ACT",
415        "Australia/Adelaide",
416        "Australia/Brisbane",
417        "Australia/Broken_Hill",
418        "Australia/Canberra",
419        "Australia/Currie",
420        "Australia/Darwin",
421        "Australia/Eucla",
422        "Australia/Hobart",
423        "Australia/LHI",
424        "Australia/Lindeman",
425        "Australia/Lord_Howe",
426        "Australia/Melbourne",
427        "Australia/NSW",
428        "Australia/North",
429        "Australia/Perth",
430        "Australia/Queensland",
431        "Australia/South",
432        "Australia/Sydney",
433        "Australia/Tasmania",
434        "Australia/Victoria",
435        "Australia/West",
436        "Australia/Yancowinna",
437        "Brazil/Acre",
438        "Brazil/DeNoronha",
439        "Brazil/East",
440        "Brazil/West",
441        "CET",
442        "CST6CDT",
443        "Canada/Atlantic",
444        "Canada/Central",
445        "Canada/Eastern",
446        "Canada/Mountain",
447        "Canada/Newfoundland",
448        "Canada/Pacific",
449        "Canada/Saskatchewan",
450        "Canada/Yukon",
451        "Chile/Continental",
452        "Chile/EasterIsland",
453        "Cuba",
454        "EET",
455        "EST",
456        "EST5EDT",
457        "Egypt",
458        "Eire",
459        "Etc/GMT",
460        "Etc/GMT+0",
461        "Etc/GMT+1",
462        "Etc/GMT+10",
463        "Etc/GMT+11",
464        "Etc/GMT+12",
465        "Etc/GMT+2",
466        "Etc/GMT+3",
467        "Etc/GMT+4",
468        "Etc/GMT+5",
469        "Etc/GMT+6",
470        "Etc/GMT+7",
471        "Etc/GMT+8",
472        "Etc/GMT+9",
473        "Etc/GMT-0",
474        "Etc/GMT-1",
475        "Etc/GMT-10",
476        "Etc/GMT-11",
477        "Etc/GMT-12",
478        "Etc/GMT-13",
479        "Etc/GMT-14",
480        "Etc/GMT-2",
481        "Etc/GMT-3",
482        "Etc/GMT-4",
483        "Etc/GMT-5",
484        "Etc/GMT-6",
485        "Etc/GMT-7",
486        "Etc/GMT-8",
487        "Etc/GMT-9",
488        "Etc/GMT0",
489        "Etc/Greenwich",
490        "Etc/UCT",
491        "Etc/UTC",
492        "Etc/Universal",
493        "Etc/Zulu",
494        "Europe/Amsterdam",
495        "Europe/Andorra",
496        "Europe/Astrakhan",
497        "Europe/Athens",
498        "Europe/Belfast",
499        "Europe/Belgrade",
500        "Europe/Berlin",
501        "Europe/Bratislava",
502        "Europe/Brussels",
503        "Europe/Bucharest",
504        "Europe/Budapest",
505        "Europe/Busingen",
506        "Europe/Chisinau",
507        "Europe/Copenhagen",
508        "Europe/Dublin",
509        "Europe/Gibraltar",
510        "Europe/Guernsey",
511        "Europe/Helsinki",
512        "Europe/Isle_of_Man",
513        "Europe/Istanbul",
514        "Europe/Jersey",
515        "Europe/Kaliningrad",
516        "Europe/Kiev",
517        "Europe/Kirov",
518        "Europe/Kyiv",
519        "Europe/Lisbon",
520        "Europe/Ljubljana",
521        "Europe/London",
522        "Europe/Luxembourg",
523        "Europe/Madrid",
524        "Europe/Malta",
525        "Europe/Mariehamn",
526        "Europe/Minsk",
527        "Europe/Monaco",
528        "Europe/Moscow",
529        "Europe/Nicosia",
530        "Europe/Oslo",
531        "Europe/Paris",
532        "Europe/Podgorica",
533        "Europe/Prague",
534        "Europe/Riga",
535        "Europe/Rome",
536        "Europe/Samara",
537        "Europe/San_Marino",
538        "Europe/Sarajevo",
539        "Europe/Saratov",
540        "Europe/Simferopol",
541        "Europe/Skopje",
542        "Europe/Sofia",
543        "Europe/Stockholm",
544        "Europe/Tallinn",
545        "Europe/Tirane",
546        "Europe/Tiraspol",
547        "Europe/Ulyanovsk",
548        "Europe/Uzhgorod",
549        "Europe/Vaduz",
550        "Europe/Vatican",
551        "Europe/Vienna",
552        "Europe/Vilnius",
553        "Europe/Volgograd",
554        "Europe/Warsaw",
555        "Europe/Zagreb",
556        "Europe/Zaporozhye",
557        "Europe/Zurich",
558        "GB",
559        "GB-Eire",
560        "GMT",
561        "GMT+0",
562        "GMT-0",
563        "GMT0",
564        "Greenwich",
565        "HST",
566        "Hongkong",
567        "Iceland",
568        "Indian/Antananarivo",
569        "Indian/Chagos",
570        "Indian/Christmas",
571        "Indian/Cocos",
572        "Indian/Comoro",
573        "Indian/Kerguelen",
574        "Indian/Mahe",
575        "Indian/Maldives",
576        "Indian/Mauritius",
577        "Indian/Mayotte",
578        "Indian/Reunion",
579        "Iran",
580        "Israel",
581        "Jamaica",
582        "Japan",
583        "Kwajalein",
584        "Libya",
585        "MET",
586        "MST",
587        "MST7MDT",
588        "Mexico/BajaNorte",
589        "Mexico/BajaSur",
590        "Mexico/General",
591        "NZ",
592        "NZ-CHAT",
593        "Navajo",
594        "PRC",
595        "PST8PDT",
596        "Pacific/Apia",
597        "Pacific/Auckland",
598        "Pacific/Bougainville",
599        "Pacific/Chatham",
600        "Pacific/Chuuk",
601        "Pacific/Easter",
602        "Pacific/Efate",
603        "Pacific/Enderbury",
604        "Pacific/Fakaofo",
605        "Pacific/Fiji",
606        "Pacific/Funafuti",
607        "Pacific/Galapagos",
608        "Pacific/Gambier",
609        "Pacific/Guadalcanal",
610        "Pacific/Guam",
611        "Pacific/Honolulu",
612        "Pacific/Johnston",
613        "Pacific/Kanton",
614        "Pacific/Kiritimati",
615        "Pacific/Kosrae",
616        "Pacific/Kwajalein",
617        "Pacific/Majuro",
618        "Pacific/Marquesas",
619        "Pacific/Midway",
620        "Pacific/Nauru",
621        "Pacific/Niue",
622        "Pacific/Norfolk",
623        "Pacific/Noumea",
624        "Pacific/Pago_Pago",
625        "Pacific/Palau",
626        "Pacific/Pitcairn",
627        "Pacific/Pohnpei",
628        "Pacific/Ponape",
629        "Pacific/Port_Moresby",
630        "Pacific/Rarotonga",
631        "Pacific/Saipan",
632        "Pacific/Samoa",
633        "Pacific/Tahiti",
634        "Pacific/Tarawa",
635        "Pacific/Tongatapu",
636        "Pacific/Truk",
637        "Pacific/Wake",
638        "Pacific/Wallis",
639        "Pacific/Yap",
640        "Poland",
641        "Portugal",
642        "ROC",
643        "ROK",
644        "Singapore",
645        "Turkey",
646        "UCT",
647        "US/Alaska",
648        "US/Aleutian",
649        "US/Arizona",
650        "US/Central",
651        "US/East-Indiana",
652        "US/Eastern",
653        "US/Hawaii",
654        "US/Indiana-Starke",
655        "US/Michigan",
656        "US/Mountain",
657        "US/Pacific",
658        "US/Samoa",
659        "UTC",
660        "Universal",
661        "W-SU",
662        "WET",
663        "Zulu",
664    )
665}
666
667
668def subsecond_precision(timestamp_literal: str) -> int:
669    """
670    Given an ISO-8601 timestamp literal, eg '2023-01-01 12:13:14.123456+00:00'
671    figure out its subsecond precision so we can construct types like DATETIME(6)
672
673    Note that in practice, this is either 3 or 6 digits (3 = millisecond precision, 6 = microsecond precision)
674    - 6 is the maximum because strftime's '%f' formats to microseconds and almost every database supports microsecond precision in timestamps
675    - Except Presto/Trino which in most cases only supports millisecond precision but will still honour '%f' and format to microseconds (replacing the remaining 3 digits with 0's)
676    - Python prior to 3.11 only supports 0, 3 or 6 digits in a timestamp literal. Any other amounts will throw a 'ValueError: Invalid isoformat string:' error
677    """
678    try:
679        parsed = datetime.datetime.fromisoformat(timestamp_literal)
680        subsecond_digit_count = len(str(parsed.microsecond).rstrip("0"))
681        precision = 0
682        if subsecond_digit_count > 3:
683            precision = 6
684        elif subsecond_digit_count > 0:
685            precision = 3
686        return precision
687    except ValueError:
688        return 0
def format_time( string: str, mapping: dict[str, str], trie: dict[typing.Any, typing.Any] | None = None) -> str | None:
11def format_time(
12    string: str, mapping: dict[str, str], trie: dict[t.Any, t.Any] | None = None
13) -> str | None:
14    """
15    Converts a time string given a mapping.
16
17    Examples:
18        >>> format_time("%Y", {"%Y": "YYYY"})
19        'YYYY'
20
21        Args:
22            mapping: dictionary of time format to target time format.
23            trie: optional trie, can be passed in for performance.
24
25        Returns:
26            The converted time string.
27    """
28    if not string:
29        return None
30
31    start = 0
32    end = 1
33    size = len(string)
34    trie = trie or new_trie(mapping)
35    current = trie
36    chunks: list[str] = []
37    sym = None
38
39    while end <= size:
40        chars = string[start:end]
41        result, current = in_trie(current, chars[-1])
42
43        if result == TrieResult.FAILED:
44            if sym:
45                end -= 1
46                chars = sym
47                sym = None
48            else:
49                chars = chars[0]
50                end = start + 1
51
52            start += len(chars)
53            chunks.append(chars)
54            current = trie
55        elif result == TrieResult.EXISTS:
56            sym = chars
57
58        end += 1
59
60        if result != TrieResult.FAILED and end > size:
61            chunks.append(chars)
62
63    return "".join(mapping.get(chars, chars) for chars in chunks)

Converts a time string given a mapping.

Examples:
>>> format_time("%Y", {"%Y": "YYYY"})
'YYYY'

Args: mapping: dictionary of time format to target time format. trie: optional trie, can be passed in for performance.

Returns: The converted time string.

TIMEZONES: set[str] = {'africa/mogadishu', 'africa/asmera', 'indian/reunion', 'asia/yakutsk', 'pacific/guadalcanal', 'etc/gmt+12', 'europe/saratov', 'america/dawson_creek', 'asia/anadyr', 'australia/adelaide', 'antarctica/dumontdurville', 'us/samoa', 'africa/luanda', 'asia/choibalsan', 'america/goose_bay', 'canada/central', 'pacific/guam', 'america/chihuahua', 'etc/zulu', 'indian/maldives', 'asia/calcutta', 'etc/gmt-5', 'america/caracas', 'antarctica/troll', 'asia/srednekolymsk', 'mexico/general', 'brazil/denoronha', 'antarctica/mcmurdo', 'america/grand_turk', 'europe/kaliningrad', 'australia/eucla', 'america/denver', 'europe/isle_of_man', 'asia/barnaul', 'asia/aden', 'europe/prague', 'america/argentina/mendoza', 'america/fort_wayne', 'america/campo_grande', 'canada/atlantic', 'america/martinique', 'atlantic/south_georgia', 'etc/gmt+5', 'antarctica/rothera', 'pacific/honolulu', 'etc/gmt-9', 'america/iqaluit', 'iceland', 'asia/pontianak', 'asia/kabul', 'asia/kuwait', 'america/inuvik', 'europe/gibraltar', 'africa/kigali', 'africa/mbabane', 'pacific/truk', 'asia/macao', 'pacific/fiji', 'asia/bishkek', 'america/indiana/vevay', 'asia/harbin', 'europe/dublin', 'europe/sarajevo', 'asia/phnom_penh', 'australia/queensland', 'asia/oral', 'europe/tirane', 'america/godthab', 'america/matamoros', 'america/st_barthelemy', 'america/resolute', 'america/guadeloupe', 'asia/ujung_pandang', 'asia/brunei', 'europe/copenhagen', 'us/arizona', 'asia/vladivostok', 'europe/podgorica', 'america/jujuy', 'singapore', 'asia/gaza', 'africa/conakry', 'europe/warsaw', 'asia/magadan', 'africa/bujumbura', 'cst6cdt', 'pacific/wake', 'america/guayaquil', 'asia/chita', 'indian/mahe', 'brazil/acre', 'africa/lusaka', 'america/bogota', 'poland', 'pacific/pohnpei', 'wet', 'mexico/bajasur', 'mst', 'europe/zurich', 'atlantic/stanley', 'america/shiprock', 'etc/gmt', 'america/port-au-prince', 'atlantic/canary', 'europe/vienna', 'america/marigot', 'america/rankin_inlet', 'asia/kolkata', 'est5edt', 'america/creston', 'america/nassau', 'europe/amsterdam', 'pacific/norfolk', 'asia/makassar', 'us/aleutian', 'us/hawaii', 'australia/sydney', 'america/havana', 'asia/hong_kong', 'africa/douala', 'europe/lisbon', 'etc/gmt-0', 'europe/paris', 'america/santiago', 'atlantic/reykjavik', 'australia/darwin', 'etc/gmt-14', 'nz-chat', 'america/argentina/rio_gallegos', 'asia/ashkhabad', 'africa/el_aaiun', 'america/santa_isabel', 'indian/kerguelen', 'africa/banjul', 'australia/broken_hill', 'pacific/rarotonga', 'america/el_salvador', 'africa/djibouti', 'europe/kyiv', 'europe/simferopol', 'america/st_kitts', 'africa/freetown', 'europe/oslo', 'japan', 'america/virgin', 'africa/lubumbashi', 'antarctica/south_pole', 'europe/budapest', 'america/moncton', 'america/boa_vista', 'australia/canberra', 'asia/qostanay', 'europe/san_marino', 'america/thule', 'america/montevideo', 'pacific/niue', 'asia/chungking', 'america/north_dakota/beulah', 'atlantic/jan_mayen', 'asia/jerusalem', 'america/curacao', 'africa/porto-novo', 'asia/ashgabat', 'america/eirunepe', 'asia/dili', 'australia/brisbane', 'antarctica/palmer', 'europe/berlin', 'africa/asmara', 'asia/colombo', 'america/managua', 'europe/chisinau', 'uct', 'chile/easterisland', 'etc/gmt+8', 'africa/maputo', 'america/rosario', 'asia/singapore', 'asia/almaty', 'pacific/enderbury', 'europe/stockholm', 'atlantic/madeira', 'europe/guernsey', 'europe/busingen', 'europe/rome', 'australia/tasmania', 'etc/gmt-1', 'africa/cairo', 'europe/andorra', 'us/alaska', 'america/lima', 'america/toronto', 'asia/yerevan', 'america/santarem', 'asia/yekaterinburg', 'america/grenada', 'africa/lome', 'pacific/tarawa', 'america/yellowknife', 'africa/accra', 'america/los_angeles', 'pacific/galapagos', 'america/chicago', 'egypt', 'hongkong', 'w-su', 'america/fortaleza', 'australia/lhi', 'pacific/chatham', 'cuba', 'asia/macau', 'gb', 'america/boise', 'america/belize', 'australia/yancowinna', 'africa/monrovia', 'etc/gmt+1', 'africa/abidjan', 'america/coral_harbour', 'australia/melbourne', 'gmt-0', 'asia/tbilisi', 'america/catamarca', 'asia/krasnoyarsk', 'europe/mariehamn', 'america/bahia', 'asia/bahrain', 'pacific/noumea', 'gb-eire', 'africa/brazzaville', 'asia/shanghai', 'etc/gmt+6', 'europe/luxembourg', 'us/east-indiana', 'europe/kiev', 'america/porto_velho', 'europe/madrid', 'america/edmonton', 'america/indiana/indianapolis', 'pacific/kiritimati', 'eire', 'europe/bratislava', 'eet', 'us/pacific', 'america/maceio', 'australia/currie', 'america/cancun', 'america/argentina/salta', 'america/monterrey', 'asia/tashkent', 'gmt', 'est', 'asia/kuching', 'indian/mauritius', 'pacific/kanton', 'america/metlakatla', 'australia/west', 'europe/bucharest', 'asia/sakhalin', 'europe/volgograd', 'atlantic/azores', 'indian/mayotte', 'america/argentina/la_rioja', 'africa/lagos', 'africa/gaborone', 'europe/minsk', 'pacific/tongatapu', 'europe/ulyanovsk', 'asia/hebron', 'europe/skopje', 'america/dawson', 'asia/kamchatka', 'asia/karachi', 'canada/newfoundland', 'indian/christmas', 'america/lower_princes', 'europe/belfast', 'africa/dakar', 'america/knox_in', 'america/st_thomas', 'asia/dushanbe', 'etc/gmt+0', 'africa/kinshasa', 'america/merida', 'pacific/majuro', 'chile/continental', 'europe/tallinn', 'africa/tripoli', 'america/punta_arenas', 'pacific/johnston', 'america/nuuk', 'america/argentina/buenos_aires', 'arctic/longyearbyen', 'africa/sao_tome', 'portugal', 'etc/gmt-11', 'asia/istanbul', 'pacific/ponape', 'europe/monaco', 'america/indiana/petersburg', 'america/vancouver', 'asia/irkutsk', 'etc/gmt-6', 'europe/jersey', 'america/kralendijk', 'atlantic/bermuda', 'nz', 'etc/utc', 'america/cuiaba', 'antarctica/vostok', 'america/kentucky/monticello', 'america/juneau', 'australia/south', 'america/sitka', 'america/atka', 'canada/mountain', 'pacific/wallis', 'america/montreal', 'atlantic/st_helena', 'asia/kashgar', 'europe/brussels', 'africa/johannesburg', 'africa/timbuktu', 'pacific/chuuk', 'us/michigan', 'etc/gmt+7', 'america/ensenada', 'antarctica/casey', 'asia/beirut', 'africa/ndjamena', 'europe/astrakhan', 'europe/nicosia', 'indian/chagos', 'us/central', 'america/argentina/catamarca', 'indian/antananarivo', 'america/argentina/comodrivadavia', 'africa/khartoum', 'america/phoenix', 'america/indiana/vincennes', 'australia/hobart', 'africa/maseru', 'america/rainy_river', 'asia/dhaka', 'asia/muscat', 'europe/athens', 'asia/ust-nera', 'canada/saskatchewan', 'asia/urumqi', 'america/fort_nelson', 'america/noronha', 'asia/tehran', 'canada/pacific', 'asia/jakarta', 'asia/khandyga', 'pacific/samoa', 'etc/gmt+9', 'etc/uct', 'africa/bissau', 'asia/tokyo', 'antarctica/syowa', 'america/atikokan', 'america/north_dakota/new_salem', 'etc/gmt-10', 'utc', 'europe/istanbul', 'america/jamaica', 'america/indiana/marengo', 'america/ciudad_juarez', 'pacific/kosrae', 'asia/hovd', 'asia/manila', 'asia/pyongyang', 'etc/gmt-13', 'asia/vientiane', 'jamaica', 'australia/perth', 'antarctica/davis', 'america/mendoza', 'america/recife', 'america/paramaribo', 'etc/gmt+4', 'africa/juba', 'america/anchorage', 'europe/zaporozhye', 'pst8pdt', 'africa/niamey', 'america/guatemala', 'america/miquelon', 'asia/thimbu', 'asia/thimphu', 'etc/gmt+10', 'iran', 'america/port_of_spain', 'pacific/bougainville', 'universal', 'america/glace_bay', 'europe/moscow', 'america/indiana/tell_city', 'etc/gmt-7', 'america/tegucigalpa', 'asia/dacca', 'asia/samarkand', 'asia/ulaanbaatar', 'us/indiana-starke', 'etc/gmt-8', 'america/st_vincent', 'gmt+0', 'america/bahia_banderas', 'america/aruba', 'america/anguilla', 'america/argentina/san_luis', 'america/nipigon', 'asia/aqtau', 'etc/greenwich', 'america/asuncion', 'america/indiana/winamac', 'hst', 'africa/nairobi', 'brazil/west', 'america/la_paz', 'asia/rangoon', 'asia/amman', 'america/regina', 'etc/gmt-12', 'asia/novokuznetsk', 'asia/famagusta', 'europe/zagreb', 'etc/gmt-4', 'pacific/auckland', 'america/ojinaga', 'prc', 'africa/blantyre', 'america/panama', 'africa/dar_es_salaam', 'america/argentina/cordoba', 'america/argentina/tucuman', 'america/hermosillo', 'asia/taipei', 'etc/gmt+11', 'africa/bangui', 'pacific/marquesas', 'america/whitehorse', 'indian/comoro', 'pacific/port_moresby', 'america/rio_branco', 'america/yakutat', 'asia/nicosia', 'america/st_lucia', 'africa/windhoek', 'america/winnipeg', 'etc/gmt+3', 'mexico/bajanorte', 'europe/belgrade', 'etc/gmt-2', 'asia/katmandu', 'asia/kuala_lumpur', 'america/barbados', 'america/mexico_city', 'america/sao_paulo', 'canada/yukon', 'australia/lindeman', 'libya', 'pacific/fakaofo', 'asia/tomsk', 'africa/nouakchott', 'asia/chongqing', 'greenwich', 'atlantic/faroe', 'asia/aqtobe', 'asia/ho_chi_minh', 'australia/victoria', 'europe/vatican', 'australia/act', 'europe/tiraspol', 'turkey', 'europe/malta', 'america/swift_current', 'asia/ulan_bator', 'america/argentina/jujuy', 'africa/malabo', 'america/thunder_bay', 'zulu', 'america/argentina/san_juan', 'pacific/palau', 'roc', 'africa/bamako', 'america/halifax', 'europe/samara', 'africa/algiers', 'asia/qatar', 'brazil/east', 'europe/helsinki', 'asia/atyrau', 'america/north_dakota/center', 'us/eastern', 'indian/cocos', 'pacific/midway', 'america/manaus', 'gmt0', 'asia/baghdad', 'asia/novosibirsk', 'australia/north', 'america/cambridge_bay', 'etc/gmt-3', 'asia/riyadh', 'america/belem', 'america/guyana', 'pacific/pitcairn', 'america/detroit', 'pacific/apia', 'america/araguaina', 'america/new_york', 'navajo', 'america/dominica', 'africa/ouagadougou', 'america/tortola', 'africa/libreville', 'asia/saigon', 'america/pangnirtung', 'pacific/tahiti', 'america/indiana/knox', 'europe/london', 'pacific/pago_pago', 'cet', 'asia/dubai', 'africa/tunis', 'america/cayenne', 'america/costa_rica', 'america/menominee', 'rok', 'asia/bangkok', 'america/danmarkshavn', 'pacific/yap', 'etc/gmt+2', 'america/argentina/ushuaia', 'asia/yangon', 'america/nome', 'antarctica/macquarie', 'etc/universal', 'africa/harare', 'africa/kampala', 'asia/tel_aviv', 'america/mazatlan', 'asia/omsk', 'europe/vilnius', 'pacific/nauru', 'america/blanc-sablon', 'israel', 'africa/ceuta', 'pacific/efate', 'us/mountain', 'america/louisville', 'antarctica/mawson', 'kwajalein', 'pacific/easter', 'pacific/kwajalein', 'america/cordoba', 'asia/jayapura', 'asia/damascus', 'america/puerto_rico', 'atlantic/faeroe', 'america/tijuana', 'met', 'africa/addis_ababa', 'etc/gmt0', 'america/porto_acre', 'america/adak', 'atlantic/cape_verde', 'pacific/gambier', 'europe/uzhgorod', 'america/santo_domingo', 'america/kentucky/louisville', 'australia/lord_howe', 'pacific/funafuti', 'america/indianapolis', 'america/st_johns', 'america/buenos_aires', 'europe/sofia', 'australia/nsw', 'canada/eastern', 'asia/baku', 'europe/kirov', 'africa/casablanca', 'america/antigua', 'america/cayman', 'europe/vaduz', 'pacific/saipan', 'asia/qyzylorda', 'america/montserrat', 'asia/seoul', 'europe/ljubljana', 'mst7mdt', 'asia/kathmandu', 'europe/riga', 'america/scoresbysund'}
def subsecond_precision(timestamp_literal: str) -> int:
669def subsecond_precision(timestamp_literal: str) -> int:
670    """
671    Given an ISO-8601 timestamp literal, eg '2023-01-01 12:13:14.123456+00:00'
672    figure out its subsecond precision so we can construct types like DATETIME(6)
673
674    Note that in practice, this is either 3 or 6 digits (3 = millisecond precision, 6 = microsecond precision)
675    - 6 is the maximum because strftime's '%f' formats to microseconds and almost every database supports microsecond precision in timestamps
676    - Except Presto/Trino which in most cases only supports millisecond precision but will still honour '%f' and format to microseconds (replacing the remaining 3 digits with 0's)
677    - Python prior to 3.11 only supports 0, 3 or 6 digits in a timestamp literal. Any other amounts will throw a 'ValueError: Invalid isoformat string:' error
678    """
679    try:
680        parsed = datetime.datetime.fromisoformat(timestamp_literal)
681        subsecond_digit_count = len(str(parsed.microsecond).rstrip("0"))
682        precision = 0
683        if subsecond_digit_count > 3:
684            precision = 6
685        elif subsecond_digit_count > 0:
686            precision = 3
687        return precision
688    except ValueError:
689        return 0

Given an ISO-8601 timestamp literal, eg '2023-01-01 12:13:14.123456+00:00' figure out its subsecond precision so we can construct types like DATETIME(6)

Note that in practice, this is either 3 or 6 digits (3 = millisecond precision, 6 = microsecond precision)

  • 6 is the maximum because strftime's '%f' formats to microseconds and almost every database supports microsecond precision in timestamps
  • Except Presto/Trino which in most cases only supports millisecond precision but will still honour '%f' and format to microseconds (replacing the remaining 3 digits with 0's)
  • Python prior to 3.11 only supports 0, 3 or 6 digits in a timestamp literal. Any other amounts will throw a 'ValueError: Invalid isoformat string:' error