python-intl 0.1.0__py3-none-any.whl → 0.2.0__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.
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import dataclasses
4
4
  import datetime as dt
5
5
  from functools import cache, cached_property
6
- from typing import TYPE_CHECKING, Literal, overload
6
+ from typing import TYPE_CHECKING, Literal
7
7
 
8
8
  import icu # type: ignore[import-untyped]
9
9
 
@@ -13,6 +13,10 @@ if TYPE_CHECKING:
13
13
 
14
14
 
15
15
  if TYPE_CHECKING:
16
+ type LocaleMatcherT = Literal["best fit", "lookup"]
17
+ type Hour12T = bool | None
18
+ type HourCycleT = Literal["h11", "h12", "h23", "h24"] | None
19
+
16
20
  type EraFormatT = Literal["long", "short", "narrow"]
17
21
  type YearFormatT = Literal["numeric", "2-digit"]
18
22
  type MonthFormatT = Literal["numeric", "2-digit", "long", "short", "narrow"]
@@ -45,46 +49,14 @@ if TYPE_CHECKING:
45
49
  | TimezoneNameFormatT
46
50
  )
47
51
 
48
- type EraPatternT = Literal["G", "GGGG", "GGGGG"]
49
- type YearPatternT = Literal["y", "yy", "yyyy"]
50
- type MonthPatternT = Literal["M", "MM", "MMM", "MMMM", "MMMMM"]
51
- type WeekdayPatternT = Literal["E", "EEEE", "EEEEE"]
52
- type DayPatternT = Literal["d", "dd"]
53
- type DayPeriodPatternT = Literal["a", "aaaa", "aaaaa", "b", "bbbb", "bbbbb", "B", "BBBB", "BBBBB"]
54
- type HourPatternT = Literal["j", "jj", "H", "h", "HH", "hh"]
55
- type MinutePatternT = Literal["m", "mm"]
56
- type SecondPatternT = Literal["s", "ss"]
57
- type FractionSecondDigitsPatternT = Literal["S", "SS", "SSS"]
58
- type TimezoneNamePatternT = Literal["z", "zzzz", "Z", "ZZZZ", "v", "vvvv"]
59
- type AnyPatternT = (
60
- EraPatternT
61
- | YearPatternT
62
- | MonthPatternT
63
- | WeekdayPatternT
64
- | DayPatternT
65
- | DayPeriodPatternT
66
- | HourPatternT
67
- | MinutePatternT
68
- | SecondPatternT
69
- | FractionSecondDigitsPatternT
70
- | TimezoneNamePatternT
71
- )
72
-
73
- type DatetimeFieldT = Literal[
74
- "era",
75
- "year",
76
- "month",
77
- "weekday",
78
- "day",
79
- "day_period",
80
- "hour",
81
- "minute",
82
- "second",
83
- "fraction_second_digits",
84
- "time_zone_name",
85
- ]
86
-
52
+ # Important: Must be the same as DateTimeFormatOptions
53
+ # (nothing is required, as this will be used to construct a
54
+ # DateTimeFormatOptions instance, so default values apply then)
87
55
  class DateTimeFormatOptionsDictT(TypedDict):
56
+ locale_matcher: NotRequired[LocaleMatcherT]
57
+ hour12: NotRequired[Hour12T]
58
+ hour_cycle: NotRequired[HourCycleT]
59
+
88
60
  era: NotRequired[EraFormatT]
89
61
  year: NotRequired[YearFormatT]
90
62
  month: NotRequired[MonthFormatT]
@@ -97,25 +69,21 @@ if TYPE_CHECKING:
97
69
  fraction_second_digits: NotRequired[FractionSecondDigitsFormatT]
98
70
  time_zone_name: NotRequired[TimezoneNameFormatT]
99
71
 
100
- class OptionsToSkeletonT(TypedDict):
101
- era: dict[EraFormatT, EraPatternT | tuple[EraPatternT, ...]]
102
- year: dict[YearFormatT, YearPatternT | tuple[YearPatternT, ...]]
103
- month: dict[MonthFormatT, MonthPatternT | tuple[MonthPatternT, ...]]
104
- weekday: dict[WeekdayFormatT, WeekdayPatternT | tuple[WeekdayPatternT, ...]]
105
- day: dict[DayFormatT, DayPatternT | tuple[DayPatternT, ...]]
106
- day_period: dict[DayPeriodFormatT, DayPeriodPatternT | tuple[DayPeriodPatternT, ...]]
107
- hour: dict[HourFormatT, HourPatternT | tuple[HourPatternT, ...]]
108
- minute: dict[MinuteFormatT, MinutePatternT | tuple[MinutePatternT, ...]]
109
- second: dict[SecondFormatT, SecondPatternT | tuple[SecondPatternT, ...]]
110
- fraction_second_digits: dict[
111
- FractionSecondDigitsFormatT,
112
- FractionSecondDigitsPatternT | tuple[FractionSecondDigitsPatternT, ...],
113
- ]
114
- time_zone_name: dict[TimezoneNameFormatT, TimezoneNamePatternT | tuple[TimezoneNamePatternT, ...]]
72
+
73
+ _TIMEZONE_NAME_JS_MAPPING: dict[str | None, str] = {
74
+ "short_offset": "shortOffset",
75
+ "long_offset": "longOffset",
76
+ "short_generic": "shortGeneric",
77
+ "long_generic": "longGeneric",
78
+ }
115
79
 
116
80
 
117
81
  @dataclasses.dataclass(frozen=True, kw_only=True)
118
82
  class DateTimeFormatOptions:
83
+ locale_matcher: LocaleMatcherT = "best fit"
84
+ hour12: Hour12T = None
85
+ hour_cycle: HourCycleT = None
86
+
119
87
  era: EraFormatT | None = None
120
88
  year: YearFormatT | None = None
121
89
  month: MonthFormatT | None = None
@@ -137,133 +105,128 @@ class DateTimeFormatOptions:
137
105
  ("month", self.month),
138
106
  ("weekday", self.weekday),
139
107
  ("day", self.day),
140
- ("day_period", self.day_period),
108
+ ("dayPeriod", self.day_period),
141
109
  ("hour", self.hour),
142
110
  ("minute", self.minute),
143
111
  ("second", self.second),
144
- ("fraction_second_digits", self.fraction_second_digits),
145
- ("time_zone_name", self.time_zone_name),
112
+ ("fractionalSecondDigits", self.fraction_second_digits),
113
+ ("timeZoneName", _TIMEZONE_NAME_JS_MAPPING.get(self.time_zone_name, self.time_zone_name)),
146
114
  )
147
115
  if v is not None
148
116
  }
149
117
 
150
118
 
151
- OPTIONS_TO_SKELETON: OptionsToSkeletonT = {
152
- "era": {"short": "G", "long": "GGGG", "narrow": "GGGGG"},
153
- "year": {"numeric": ("yyyy", "y"), "2-digit": "yy"},
154
- "month": {"numeric": "M", "2-digit": "MM", "short": "MMM", "long": "MMMM", "narrow": "MMMMM"},
155
- "weekday": {"short": "E", "long": "EEEE", "narrow": "EEEEE"},
156
- "day": {"numeric": "d", "2-digit": "dd"},
157
- "day_period": {
158
- "short": ("a", "b", "B"),
159
- "long": ("aaaa", "bbbb", "BBBB"),
160
- "narrow": ("aaaaa", "bbbbb", "BBBBB"),
161
- },
162
- "hour": {"numeric": ("j", "H", "h"), "2-digit": ("jj", "HH", "hh")},
163
- "minute": {"numeric": "m", "2-digit": "mm"},
164
- "second": {"numeric": "s", "2-digit": "ss"},
165
- "fraction_second_digits": {1: "S", 2: "SS", 3: "SSS"},
166
- "time_zone_name": {
167
- "short": "z",
168
- "long": "zzzz",
169
- "short_offset": "Z",
170
- "long_offset": "ZZZZ",
171
- "short_generic": "v",
172
- "long_generic": "vvvv",
173
- },
174
- }
175
-
176
-
177
- @overload
178
- def _option_to_skeleton_bit(
179
- field: Literal["era"],
180
- format: EraFormatT,
181
- ) -> EraPatternT | tuple[EraPatternT, ...]: ...
182
- @overload
183
- def _option_to_skeleton_bit(
184
- field: Literal["year"],
185
- format: YearFormatT,
186
- ) -> YearPatternT | tuple[YearPatternT, ...]: ...
187
- @overload
188
- def _option_to_skeleton_bit(
189
- field: Literal["month"],
190
- format: MonthFormatT,
191
- ) -> MonthPatternT | tuple[MonthPatternT, ...]: ...
192
- @overload
193
- def _option_to_skeleton_bit(
194
- field: Literal["weekday"],
195
- format: WeekdayFormatT,
196
- ) -> WeekdayPatternT | tuple[WeekdayPatternT, ...]: ...
197
- @overload
198
- def _option_to_skeleton_bit(
199
- field: Literal["day"],
200
- format: DayFormatT,
201
- ) -> DayPatternT | tuple[DayPatternT, ...]: ...
202
- @overload
203
- def _option_to_skeleton_bit(
204
- field: Literal["day_period"],
205
- format: DayPeriodFormatT,
206
- ) -> DayPeriodPatternT | tuple[DayPeriodPatternT, ...]: ...
207
- @overload
208
- def _option_to_skeleton_bit(
209
- field: Literal["hour"],
210
- format: HourFormatT,
211
- ) -> HourPatternT | tuple[HourPatternT, ...]: ...
212
- @overload
213
- def _option_to_skeleton_bit(
214
- field: Literal["minute"],
215
- format: MinuteFormatT,
216
- ) -> MinutePatternT | tuple[MinutePatternT, ...]: ...
217
- @overload
218
- def _option_to_skeleton_bit(
219
- field: Literal["second"],
220
- format: SecondFormatT,
221
- ) -> SecondPatternT | tuple[SecondPatternT, ...]: ...
222
- @overload
223
- def _option_to_skeleton_bit(
224
- field: Literal["fraction_second_digits"],
225
- format: FractionSecondDigitsFormatT,
226
- ) -> FractionSecondDigitsPatternT | tuple[FractionSecondDigitsPatternT, ...]: ...
227
- @overload
228
- def _option_to_skeleton_bit(
229
- field: Literal["time_zone_name"],
230
- format: TimezoneNameFormatT,
231
- ) -> TimezoneNamePatternT | tuple[TimezoneNamePatternT, ...]: ...
232
- def _option_to_skeleton_bit(
233
- field,
234
- format,
235
- ):
236
- return OPTIONS_TO_SKELETON[field][format]
237
-
238
-
239
119
  def _options_to_possible_skeletons(options: DateTimeFormatOptions) -> Iterable[str]:
240
- datetime_code_bits: list[str | tuple[str, ...]] = []
241
-
242
- # For order see babel.dates.PATTERN_CHAR_ORDER
243
- if options.era:
244
- datetime_code_bits.append(_option_to_skeleton_bit("era", options.era))
245
- if options.year:
246
- datetime_code_bits.append(_option_to_skeleton_bit("year", options.year))
247
- if options.month:
248
- datetime_code_bits.append(_option_to_skeleton_bit("month", options.month))
249
- if options.weekday:
250
- datetime_code_bits.append(_option_to_skeleton_bit("weekday", options.weekday))
251
- if options.day:
252
- datetime_code_bits.append(_option_to_skeleton_bit("day", options.day))
253
- if options.day_period:
254
- datetime_code_bits.append(_option_to_skeleton_bit("day_period", options.day_period))
255
- if options.hour:
256
- datetime_code_bits.append(_option_to_skeleton_bit("hour", options.hour))
257
- if options.minute:
258
- datetime_code_bits.append(_option_to_skeleton_bit("minute", options.minute))
259
- if options.second:
260
- datetime_code_bits.append(_option_to_skeleton_bit("second", options.second))
120
+ skeleton_parts: list[str | tuple[str, ...]] = []
121
+
122
+ # Note: The parts should be ordered from big to small.
123
+
124
+ match options.era:
125
+ case "short":
126
+ skeleton_parts.append("G")
127
+ case "long":
128
+ skeleton_parts.append("GGGG")
129
+ case "narrow":
130
+ skeleton_parts.append("GGGGG")
131
+
132
+ match options.year:
133
+ case "numeric":
134
+ skeleton_parts.append(("yyyy", "y"))
135
+ case "2-digit":
136
+ skeleton_parts.append("yy")
137
+
138
+ match options.month:
139
+ case "numeric":
140
+ skeleton_parts.append("M")
141
+ case "2-digit":
142
+ skeleton_parts.append("MM")
143
+ case "short":
144
+ skeleton_parts.append("MMM")
145
+ case "long":
146
+ skeleton_parts.append("MMMM")
147
+ case "narrow":
148
+ skeleton_parts.append("MMMMM")
149
+
150
+ match options.weekday:
151
+ case "short":
152
+ skeleton_parts.append("E")
153
+ case "long":
154
+ skeleton_parts.append("EEEE")
155
+ case "narrow":
156
+ skeleton_parts.append("EEEEE")
157
+
158
+ match options.day:
159
+ case "numeric":
160
+ skeleton_parts.append("d")
161
+ case "2-digit":
162
+ skeleton_parts.append("dd")
163
+
164
+ match options.day_period:
165
+ case "short":
166
+ skeleton_parts.append("B")
167
+ case "long":
168
+ skeleton_parts.append("BBBB")
169
+ case "narrow":
170
+ skeleton_parts.append("BBBBB")
171
+
172
+ match (options.hour_cycle, options.hour12, options.hour):
173
+ case ("h11", _, "numeric"):
174
+ skeleton_parts.append(("aK", "K"))
175
+ case ("h11", _, "2-digit"):
176
+ skeleton_parts.append(("aKK", "KK"))
177
+ case ("h12", _, "numeric"):
178
+ skeleton_parts.append(("ah", "h"))
179
+ case ("h12", _, "2-digit"):
180
+ skeleton_parts.append(("ahh", "hh"))
181
+ case ("h23", _, "numeric"):
182
+ skeleton_parts.append("H")
183
+ case ("h23", _, "2-digit"):
184
+ skeleton_parts.append("HH")
185
+ case ("h24", _, "numeric"):
186
+ skeleton_parts.append("k")
187
+ case ("h24", _, "2-digit"):
188
+ skeleton_parts.append("kk")
189
+ case (_, True, "numeric"):
190
+ skeleton_parts.append(("ah", "h"))
191
+ case (_, True, "2-digit"):
192
+ skeleton_parts.append(("ahh", "hh"))
193
+ case (_, False, "numeric"):
194
+ skeleton_parts.append("H")
195
+ case (_, False, "2-digit"):
196
+ skeleton_parts.append("HH")
197
+ case (_, _, "numeric"):
198
+ skeleton_parts.append("j")
199
+ case (_, _, "2-digit"):
200
+ skeleton_parts.append(("jj", "j"))
201
+
202
+ match options.minute:
203
+ case "numeric":
204
+ skeleton_parts.append("m")
205
+ case "2-digit":
206
+ skeleton_parts.append("mm")
207
+
208
+ match options.second:
209
+ case "numeric":
210
+ skeleton_parts.append("s")
211
+ case "2-digit":
212
+ skeleton_parts.append("ss")
213
+
261
214
  if options.fraction_second_digits:
262
- datetime_code_bits.append(
263
- _option_to_skeleton_bit("fraction_second_digits", options.fraction_second_digits),
264
- )
265
- if options.time_zone_name:
266
- datetime_code_bits.append(_option_to_skeleton_bit("time_zone_name", options.time_zone_name))
215
+ skeleton_parts.append("S" * options.fraction_second_digits)
216
+
217
+ match options.time_zone_name:
218
+ case "short":
219
+ skeleton_parts.append("z")
220
+ case "long":
221
+ skeleton_parts.append("zzzz")
222
+ case "short_offset":
223
+ skeleton_parts.append("Z")
224
+ case "long_offset":
225
+ skeleton_parts.append("ZZZZ")
226
+ case "short_generic":
227
+ skeleton_parts.append("v")
228
+ case "long_generic":
229
+ skeleton_parts.append("vvvv")
267
230
 
268
231
  def generate_skeletons(prefix: str, remaining: list[str | tuple[str, ...]]) -> Iterable[str]:
269
232
  if not remaining:
@@ -277,7 +240,7 @@ def _options_to_possible_skeletons(options: DateTimeFormatOptions) -> Iterable[s
277
240
  else:
278
241
  yield from generate_skeletons(prefix + next_bit, remaining[1:])
279
242
 
280
- yield from generate_skeletons("", datetime_code_bits)
243
+ yield from generate_skeletons("", skeleton_parts)
281
244
 
282
245
 
283
246
  @dataclasses.dataclass(frozen=True, kw_only=True)
@@ -312,13 +275,14 @@ def _options_to_format_pattern(
312
275
  )
313
276
 
314
277
  # Try to find best match
315
- for skeleton in possible_skeletons:
316
- pattern = generator.getBestPattern(skeleton)
317
- if pattern:
318
- return MatchedFormatPattern(
319
- skeleton=skeleton,
320
- pattern=pattern,
321
- )
278
+ if options.locale_matcher == "best fit":
279
+ for skeleton in possible_skeletons:
280
+ pattern = generator.getBestPattern(skeleton)
281
+ if pattern:
282
+ return MatchedFormatPattern(
283
+ skeleton=skeleton,
284
+ pattern=pattern,
285
+ )
322
286
 
323
287
  raise FormatPatternNotFoundException("Didn't find pattern for desired options")
324
288
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-intl
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Add your description here
5
5
  Author: David Danier
6
6
  Author-email: David Danier <david.danier@gmail.com>
@@ -0,0 +1,6 @@
1
+ python_intl/__init__.py,sha256=eLk_lPxY8d3up4pTUCx94otMig3X2yxcE3qIYWXAIlI,61
2
+ python_intl/datetimeformat.py,sha256=P4DdEhArGbV5trXnJ0wENUjXHRwgdggoEMHMLkHC51s,10469
3
+ python_intl-0.2.0.dist-info/licenses/LICENSE,sha256=vBhn8BZCY-G3D6-ca99f4FU3ggdJSrKBTa3Yk8ZIAN8,1078
4
+ python_intl-0.2.0.dist-info/WHEEL,sha256=4OL6Foqnnp3xRY5wMkjgc25_i5YJC6dKsC6LPcjqEoU,80
5
+ python_intl-0.2.0.dist-info/METADATA,sha256=p1sROEE0bkG9nguk0SyGMAkO27Tpf83bB2qkbkV3r1I,1370
6
+ python_intl-0.2.0.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- python_intl/__init__.py,sha256=eLk_lPxY8d3up4pTUCx94otMig3X2yxcE3qIYWXAIlI,61
2
- python_intl/datetimeformat.py,sha256=twbFkPeG_3TzZXlJ4VT4NrwkjfOXwwGWe_pdMSZw1BQ,12547
3
- python_intl-0.1.0.dist-info/licenses/LICENSE,sha256=vBhn8BZCY-G3D6-ca99f4FU3ggdJSrKBTa3Yk8ZIAN8,1078
4
- python_intl-0.1.0.dist-info/WHEEL,sha256=4OL6Foqnnp3xRY5wMkjgc25_i5YJC6dKsC6LPcjqEoU,80
5
- python_intl-0.1.0.dist-info/METADATA,sha256=juS5u5Q5jsM7Xuckx9UqxkqikX-VdaEACQvxXYACjfE,1370
6
- python_intl-0.1.0.dist-info/RECORD,,