Skip to content

Commit 3dbcd4b

Browse files
committed
Update the docs for byUnicodePattern
1 parent 606db50 commit 3dbcd4b

File tree

2 files changed

+64
-7
lines changed

2 files changed

+64
-7
lines changed

core/common/src/format/migration/Unicode.kt

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,56 @@ public annotation class FormatStringsInDatetimeFormats
3939
*
4040
* In addition to the standard syntax, this function also supports the following extensions:
4141
* * `[]` denote optional sections. For example, `hh:mm[:ss]` will allow parsing seconds optionally.
42-
* This is similar to what is supported by the Java Time's `DateTimeFormatter` class, but with the difference that,
43-
* for formatting, the optional sections have a different meaning: they are not included in the output if all the
44-
* fields they contain have their default values. See [DateTimeFormatBuilder.optional] for more details.
45-
* * One or more `p` characters before a field specifies that the field should be padded with zeroes to the length
46-
* equal to the number of `p` characters. This is also supported by the Java Time's `DateTimeFormatter` class.
42+
* This is similar to what is supported by the Java Time's `DateTimeFormatter` class.
43+
*
44+
* Usage example:
45+
* ```
46+
* DateTimeComponents.Format {
47+
* // 2023-01-20T23:53:16.312+03:30[Asia/Tehran]
48+
* byUnicodePattern("uuuu-MM-dd'T'HH:mm[:ss[.SSS]]xxxxx'['VV']'")
49+
* }
50+
* ```
51+
*
52+
* The list of supported directives is as follows:
53+
*
54+
* | **Directive** | **Meaning** |
55+
* | `'string'` | literal `string`, without quotes |
56+
* | `'''` | literal char `'` |
57+
* | `[fmt]` | equivalent to `fmt` during formatting, but during parsing also accepts the empty string |
58+
* | `u` | ISO year without padding |
59+
* | `uu` | last two digits of the ISO year, with the base year 2000 |
60+
* | `uuuu` | ISO year, zero-padded to four digits |
61+
* | `M`, `L` | month number (1-12), without padding |
62+
* | `MM`, `LL` | month number (01-12), zero-padded to two digits |
63+
* | `d` | day-of-month (1-31), without padding |
64+
* | `H` | hour-of-day (0-23), without padding |
65+
* | `HH` | hour-of-day (00-23), zero-padded to two digits |
66+
* | `m` | minute-of-hour (0-59), without padding |
67+
* | `mm` | minute-of-hour (00-59), zero-padded to two digits |
68+
* | `s` | second-of-hour (0-59), without padding |
69+
* | `ss` | second-of-hour (00-59), zero-padded to two digits |
70+
* | `S`, `SS`, `SSS`... | fraction-of-second without a leading dot, with as many digits as the format length |
71+
* | `VV` | timezone name (for example, `Europe/Berlin`) |
72+
*
73+
* The UTC offset is formatted using one of the following directives. In every one of these formats, hours, minutes,
74+
* and seconds are zero-padded to two digits. Also, hours are unconditionally present.
75+
*
76+
* | **Directive** | **Minutes** | **Seconds** | **Separator** | **Representation of zero** |
77+
* | `X` | unless zero | never | none | `Z` |
78+
* | `XX` | always | never | none | `Z` |
79+
* | `XXX` | always | never | colon | `Z` |
80+
* | `XXXX` | always | unless zero | none | `Z` |
81+
* | `XXXXX`, `ZZZZZ` | always | unless zero | colon | `Z` |
82+
* | `x` | unless zero | never | none | `+00` |
83+
* | `xx`, `Z`, `ZZ`, `ZZZ` | always | never | none | `+0000` |
84+
* | `xxx` | always | never | colon | `+00:00` |
85+
* | `xxxx` | always | unless zero | none | `+0000` |
86+
* | `xxxxx` | always | unless zero | colon | `+00:00` |
87+
*
88+
* `y` is the most common way to represent the year, but it's not supported by [byUnicodePattern] because its
89+
* interpretation depends on which calendar is being used.
90+
* For the ISO chronology and years 0001 and later, one can obtain results that are equivalent to `yyyy` and `yy` like
91+
* this: `byUnicodePattern(pattern.replace("yyyy", "uuuu").replace("yy", "uu")`.
4792
*
4893
* @throws IllegalArgumentException if the pattern is invalid or contains unsupported directives.
4994
* @throws IllegalArgumentException if the builder is incompatible with the specified directives.
@@ -378,7 +423,7 @@ internal sealed interface UnicodeFormat {
378423
class FractionOfSecond(override val formatLength: Int) : WithSubsecondPrecision() {
379424
override val formatLetter = 'S'
380425
override fun addToFormat(builder: DateTimeFormatBuilder.WithTime) =
381-
builder.secondFraction(minLength = formatLength, maxLength = formatLength)
426+
builder.secondFraction(formatLength)
382427
}
383428

384429
class MilliOfDay(override val formatLength: Int) : WithSubsecondPrecision() {
@@ -407,7 +452,10 @@ internal sealed interface UnicodeFormat {
407452

408453
class TimeZoneId(override val formatLength: Int) : ZoneBased() {
409454
override val formatLetter = 'V'
410-
override fun addToFormat(builder: DateTimeFormatBuilder.WithDateTimeComponents) = builder.timeZoneId()
455+
override fun addToFormat(builder: DateTimeFormatBuilder.WithDateTimeComponents) = when (formatLength) {
456+
2 -> builder.timeZoneId()
457+
else -> unknownLength()
458+
}
411459
}
412460

413461
class GenericTimeZoneName(override val formatLength: Int) : ZoneBased() {

core/common/test/format/DateTimeComponentsFormatTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ class DateTimeComponentsFormatTest {
162162
assertNull(bagWithAlternative.nanosecond)
163163
}
164164

165+
@OptIn(FormatStringsInDatetimeFormats::class)
166+
@Test
167+
fun testByUnicodePatternDoc() {
168+
val format = DateTimeComponents.Format {
169+
byUnicodePattern("uuuu-MM-dd'T'HH:mm[:ss[.SSS]]xxxxx'['VV']'")
170+
}
171+
format.parse("2023-01-20T23:53:16.312+03:30[Asia/Tehran]")
172+
}
173+
165174
private fun test(strings: Map<DateTimeComponents, Pair<String, Set<String>>>, format: DateTimeFormat<DateTimeComponents>) {
166175
for ((value, stringsForValue) in strings) {
167176
val (canonicalString, otherStrings) = stringsForValue

0 commit comments

Comments
 (0)