Skip to content

Commit 12183f5

Browse files
committed
Temporary commit: reproducer for a bug. To be squashed with the following commit.
1 parent 560b52d commit 12183f5

21 files changed

+261
-191
lines changed

core/common/src/Instant.kt

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -153,29 +153,28 @@ public expect class Instant : Comparable<Instant> {
153153
public fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant
154154

155155
/**
156-
* A shortcut for calling [parse] with [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET].
156+
* A shortcut for calling [DateTimeFormat.parse], followed by [DateTimeComponents.toInstantUsingOffset].
157157
*
158-
* Parses a string that represents an instant in ISO 8601 format including date and time components and
159-
* the mandatory time zone offset and returns the parsed [Instant] value.
158+
* Parses a string that represents an instant including date and time components and a mandatory
159+
* time zone offset and returns the parsed [Instant] value.
160160
*
161-
* Examples of instants in the ISO 8601 format:
162-
* - `2020-08-30T18:43:00Z`
163-
* - `2020-08-30T18:43:00.50Z`
164-
* - `2020-08-30T18:43:00.123456789Z`
165-
* - `2020-08-30T18:40:00+03:00`
166-
* - `2020-08-30T18:40:00+03:30:20`
167-
* * `2020-01-01T23:59:59.123456789+01`
168-
* * `+12020-01-31T23:59:59Z`
161+
* The string is considered to represent time on the UTC-SLS time scale instead of UTC.
162+
* In practice, this means that, even if there is a leap second on the given day, it will not affect how the
163+
* time is parsed, even if it's in the last 1000 seconds of the day.
164+
* Instead, even if there is a negative leap second on the given day, 23:59:59 is still considered valid time.
165+
* 23:59:60 is invalid on UTC-SLS, so parsing it will fail.
169166
*
170-
* Guaranteed to parse all strings that [Instant.toString] produces.
167+
* If the format is not specified, [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is used.
171168
*
172169
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [Instant] are exceeded.
173170
*
174-
* @see Instant.toString
175-
* @see DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET
171+
* @see Instant.toString for formatting using the default format.
172+
* @see Instant.format for formatting using a custom format.
176173
*/
177-
public fun parse(isoString: String): Instant
178-
174+
public fun parse(
175+
input: CharSequence,
176+
format: DateTimeFormat<DateTimeComponents> = DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET
177+
): Instant
179178

180179
/**
181180
* An instant value that is far in the past.
@@ -513,34 +512,17 @@ public fun Instant.minus(other: Instant, unit: DateTimeUnit.TimeBased): Long =
513512
/**
514513
* Formats this value using the given [format] using the given [offset].
515514
*
516-
* [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is the format very similar to the one used by [toString] and
517-
* [Instant.Companion.parse]. The only difference is that [Instant.toString] adds trailing zeros to the
518-
* fraction-of-second component so that the number of digits after a dot is a multiple of three.
515+
* Equivalent to calling [DateTimeFormat.format] on [format] and using [DateTimeComponents.setDateTimeOffset] in
516+
* the lambda.
517+
*
518+
* [DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET] is a format very similar to the one used by [toString].
519+
* The only difference is that [Instant.toString] adds trailing zeros to the fraction-of-second component so that the
520+
* number of digits after a dot is a multiple of three.
519521
*/
520522
public fun Instant.format(format: DateTimeFormat<DateTimeComponents>, offset: UtcOffset = UtcOffset.ZERO): String {
521523
val instant = this
522524
return format.format { setDateTimeOffset(instant, offset) }
523525
}
524526

525-
/**
526-
* Parses an [Instant] value using the given [format].
527-
*
528-
* Equivalent to calling [DateTimeFormat.parse] on [format] with [input] and obtaining the resulting [Instant] using
529-
* [DateTimeComponents.toInstantUsingOffset].
530-
*
531-
* The string is considered to represent time on the UTC-SLS time scale instead of UTC.
532-
* In practice, this means that, even if there is a leap second on the given day, it will not affect how the
533-
* time is parsed, even if it's in the last 1000 seconds of the day.
534-
* Instead, even if there is a negative leap second on the given day, 23:59:59 is still considered valid time.
535-
* 23:59:60 is invalid on UTC-SLS, so parsing it will fail.
536-
*
537-
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [Instant] are exceeded.
538-
*/
539-
public fun Instant.Companion.parse(input: CharSequence, format: DateTimeFormat<DateTimeComponents>): Instant = try {
540-
format.parse(input).toInstantUsingOffset()
541-
} catch (e: IllegalArgumentException) {
542-
throw DateTimeFormatException("Failed to parse an instant from '$input'", e)
543-
}
544-
545527
internal const val DISTANT_PAST_SECONDS = -3217862419201
546528
internal const val DISTANT_FUTURE_SECONDS = 3093527980800

core/common/src/LocalDate.kt

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,18 @@ import kotlinx.serialization.Serializable
2424
public expect class LocalDate : Comparable<LocalDate> {
2525
public companion object {
2626
/**
27-
* Parses a string that represents a date in ISO-8601 format
28-
* and returns the parsed [LocalDate] value.
27+
* A shortcut for calling [DateTimeFormat.parse].
2928
*
30-
* An example of a local date in ISO-8601 format: `2020-08-30`.
29+
* Parses a string that represents a date and returns the parsed [LocalDate] value.
30+
*
31+
* If [format] is not specified, [Formats.ISO] is used.
3132
*
3233
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [LocalDate] are exceeded.
34+
*
35+
* @see LocalDate.toString for formatting using the default format.
36+
* @see LocalDate.format for formatting using a custom format.
3337
*/
34-
public fun parse(isoString: String): LocalDate
38+
public fun parse(input: CharSequence, format: DateTimeFormat<LocalDate> = Formats.ISO): LocalDate
3539

3640
/**
3741
* Returns a [LocalDate] that is [epochDays] number of days from the epoch day `1970-01-01`.
@@ -169,9 +173,11 @@ public expect class LocalDate : Comparable<LocalDate> {
169173
public override fun compareTo(other: LocalDate): Int
170174

171175
/**
172-
* Converts this date to the ISO-8601 string representation.
176+
* Converts this date to the extended ISO-8601 string representation.
173177
*
174-
* @see LocalDate.parse
178+
* @see Formats.ISO for the format details.
179+
* @see parse for the dual operation: obtaining [LocalDate] from a string.
180+
* @see LocalDate.format for formatting using a custom format.
175181
*/
176182
public override fun toString(): String
177183
}
@@ -182,15 +188,6 @@ public expect class LocalDate : Comparable<LocalDate> {
182188
*/
183189
public fun LocalDate.format(format: DateTimeFormat<LocalDate>): String = format.format(this)
184190

185-
/**
186-
* Parses a [LocalDate] value using the given [format].
187-
* Equivalent to calling [DateTimeFormat.parse] on [format] with [input].
188-
*
189-
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [LocalDate] are exceeded.
190-
*/
191-
public fun LocalDate.Companion.parse(input: CharSequence, format: DateTimeFormat<LocalDate>): LocalDate =
192-
format.parse(input)
193-
194191
/**
195192
* @suppress
196193
*/

core/common/src/LocalDateTime.kt

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package kotlinx.datetime
77

8+
import kotlinx.datetime.LocalDate.*
9+
import kotlinx.datetime.LocalDate.Companion.parse
810
import kotlinx.datetime.format.*
911
import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer
1012
import kotlinx.serialization.Serializable
@@ -28,23 +30,17 @@ public expect class LocalDateTime : Comparable<LocalDateTime> {
2830
public companion object {
2931

3032
/**
31-
* A shortcut for calling [parse] with [Formats.ISO].
33+
* A shortcut for calling [DateTimeFormat.parse].
3234
*
33-
* Parses a string that represents a date/time value in ISO 8601 format including date and time components
35+
* Parses a string that represents a date/time value including date and time components
3436
* but without any time zone component and returns the parsed [LocalDateTime] value.
3537
*
36-
* Examples of date/time in ISO 8601 format:
37-
* - `2020-08-30T18:43`
38-
* - `2020-08-30T18:43:00`
39-
* - `2020-08-30T18:43:00.5`
40-
* - `2020-08-30T18:43:00.123456789`
41-
*
42-
* Guaranteed to parse all strings that [LocalDateTime.toString] produces.
38+
* If [format] is not specified, [Formats.ISO] is used.
4339
*
4440
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [LocalDateTime] are
4541
* exceeded.
4642
*/
47-
public fun parse(isoString: String): LocalDateTime
43+
public fun parse(input: CharSequence, format: DateTimeFormat<LocalDateTime> = Formats.ISO): LocalDateTime
4844

4945
/**
5046
* Creates a new format for parsing and formatting [LocalDateTime] values.
@@ -95,6 +91,8 @@ public expect class LocalDateTime : Comparable<LocalDateTime> {
9591
*
9692
* When formatting, seconds are always included, even if they are zero.
9793
* Fractional parts of the second are included if non-zero.
94+
*
95+
* Guaranteed to parse all strings that [LocalDateTime.toString] produces.
9896
*/
9997
public val ISO: DateTimeFormat<LocalDateTime>
10098
}
@@ -206,15 +204,21 @@ public expect class LocalDateTime : Comparable<LocalDateTime> {
206204
/**
207205
* Converts this date/time value to the ISO 8601 string representation.
208206
*
209-
* Examples of date/time in ISO 8601 format:
207+
* For readability, if the time represents a round minute (without seconds or fractional seconds),
208+
* the string representation will not include seconds. Also, fractions of seconds will add trailing zeros to
209+
* the fractional part until its length is a multiple of three.
210+
*
211+
* Examples of output:
210212
* - `2020-08-30T18:43`
211213
* - `2020-08-30T18:43:00`
212214
* - `2020-08-30T18:43:00.500`
213215
* - `2020-08-30T18:43:00.123456789`
214216
*
217+
* @see LocalTime.toString for details of how the time part is formatted.
215218
* @see Formats.ISO for a very similar format. The difference is that [Formats.ISO] will always include seconds,
216219
* even if they are zero, and will not add trailing zeros to the fractional part of the second for readability.
217-
* @see LocalDateTime.parse
220+
* @see parse for the dual operation: obtaining [LocalDateTime] from a string.
221+
* @see LocalDateTime.format for formatting using a custom format.
218222
*/
219223
public override fun toString(): String
220224
}
@@ -225,15 +229,6 @@ public expect class LocalDateTime : Comparable<LocalDateTime> {
225229
*/
226230
public fun LocalDateTime.format(format: DateTimeFormat<LocalDateTime>): String = format.format(this)
227231

228-
/**
229-
* Parses a [LocalDateTime] value using the given [format].
230-
* Equivalent to calling [DateTimeFormat.parse] on [format] with [input].
231-
*
232-
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [LocalDateTime] are exceeded.
233-
*/
234-
public fun LocalDateTime.Companion.parse(input: CharSequence, format: DateTimeFormat<LocalDateTime>): LocalDateTime =
235-
format.parse(input)
236-
237232
/**
238233
* @suppress
239234
*/

core/common/src/LocalTime.kt

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package kotlinx.datetime
77

8+
import kotlinx.datetime.LocalDate.*
9+
import kotlinx.datetime.LocalDate.Companion.parse
810
import kotlinx.datetime.format.*
911
import kotlinx.datetime.serializers.LocalTimeIso8601Serializer
1012
import kotlinx.serialization.Serializable
@@ -28,22 +30,17 @@ public expect class LocalTime : Comparable<LocalTime> {
2830
public companion object {
2931

3032
/**
31-
* A shortcut for calling [parse] with [Formats.ISO].
33+
* A shortcut for calling [DateTimeFormat.parse].
3234
*
33-
* Parses a string that represents a time value in ISO 8601 and returns the parsed [LocalTime] value.
34-
*
35-
* Examples of time in ISO 8601 format:
36-
* - `18:43`
37-
* - `18:43:00`
38-
* - `18:43:00.5`
39-
* - `18:43:00.123456789`
40-
*
41-
* Guaranteed to parse all strings that [LocalTime.toString] produces.
35+
* Parses a string that represents time-of-day and returns the parsed [LocalTime] value.
4236
*
4337
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [LocalTime] are
4438
* exceeded.
39+
*
40+
* @see LocalTime.toString for formatting using the default format.
41+
* @see LocalTime.format for formatting using a custom format.
4542
*/
46-
public fun parse(isoString: String): LocalTime
43+
public fun parse(input: CharSequence, format: DateTimeFormat<LocalTime> = Formats.ISO): LocalTime
4744

4845
/**
4946
* Constructs a [LocalTime] that represents the specified number of seconds since the start of a calendar day.
@@ -124,6 +121,8 @@ public expect class LocalTime : Comparable<LocalTime> {
124121
*
125122
* When formatting, seconds are always included, even if they are zero.
126123
* Fractional parts of the second are included if non-zero.
124+
*
125+
* Guaranteed to parse all strings that [LocalTime.toString] produces.
127126
*/
128127
public val ISO: DateTimeFormat<LocalTime>
129128
}
@@ -175,14 +174,22 @@ public expect class LocalTime : Comparable<LocalTime> {
175174
public override operator fun compareTo(other: LocalTime): Int
176175

177176
/**
178-
* Converts this time value to the ISO 8601 string representation:
179-
* * `12:34`
180-
* * `12:34:56.5`
181-
* * `12:34:56.123456789`
177+
* Converts this time value to the extended ISO-8601 string representation.
178+
*
179+
* For readability, if the time represents a round minute (without seconds or fractional seconds),
180+
* the string representation will not include seconds. Also, fractions of seconds will add trailing zeros to
181+
* the fractional part until the number of digits after the dot is a multiple of three.
182+
*
183+
* Examples of output:
184+
* - `18:43`
185+
* - `18:43:00`
186+
* - `18:43:00.500`
187+
* - `18:43:00.123456789`
182188
*
183189
* @see Formats.ISO for a very similar format. The difference is that [Formats.ISO] will always include seconds,
184190
* even if they are zero, and will not add trailing zeros to the fractional part of the second for readability.
185-
* @see LocalTime.parse
191+
* @see parse for the dual operation: obtaining [LocalTime] from a string.
192+
* @see LocalTime.format for formatting using a custom format.
186193
*/
187194
public override fun toString(): String
188195
}
@@ -193,15 +200,6 @@ public expect class LocalTime : Comparable<LocalTime> {
193200
*/
194201
public fun LocalTime.format(format: DateTimeFormat<LocalTime>): String = format.format(this)
195202

196-
/**
197-
* Parses a [LocalTime] value using the given [format].
198-
* Equivalent to calling [DateTimeFormat.parse] on [format] with [input].
199-
*
200-
* @throws IllegalArgumentException if the text cannot be parsed or the boundaries of [LocalTime] are exceeded.
201-
*/
202-
public fun LocalTime.Companion.parse(input: CharSequence, format: DateTimeFormat<LocalTime>): LocalTime =
203-
format.parse(input)
204-
205203
/**
206204
* @suppress
207205
*/

0 commit comments

Comments
 (0)