@@ -8,6 +8,8 @@ package kotlinx.datetime
8
8
import kotlinx.datetime.internal.*
9
9
import kotlinx.datetime.serializers.DatePeriodIso8601Serializer
10
10
import kotlinx.datetime.serializers.DateTimePeriodIso8601Serializer
11
+ import kotlinx.datetime.serializers.DatePeriodComponentSerializer
12
+ import kotlinx.datetime.serializers.DateTimePeriodComponentSerializer
11
13
import kotlin.math.*
12
14
import kotlin.time.Duration
13
15
import kotlinx.serialization.Serializable
@@ -19,10 +21,29 @@ import kotlinx.serialization.Serializable
19
21
*
20
22
* The time components are: [hours], [minutes], [seconds], [nanoseconds].
21
23
*
22
- * A `DateTimePeriod` can be constructed using the same-named constructor function,
23
- * [parsed][DateTimePeriod.parse] from a string, or returned as the result of instant arithmetic operations (see [Instant.periodUntil]).
24
- * All these functions can return a [DatePeriod] value, which is a subtype of `DateTimePeriod`,
25
- * a special case that only stores date components, if all time components of the result happen to be zero.
24
+ * ### Interaction with other entities
25
+ *
26
+ * [DateTimePeriod] can be returned from [Instant.periodUntil], representing the difference between two instants.
27
+ * Conversely, there is an [Instant.plus] overload that accepts a [DateTimePeriod] and returns a new instant.
28
+ *
29
+ * [DatePeriod] is a subtype of [DateTimePeriod] that only stores the date components and has all time components equal
30
+ * to zero.
31
+ *
32
+ * ### Construction, serialization, and deserialization
33
+ *
34
+ * When a [DateTimePeriod] is constructed in any way, a [DatePeriod] value, which is a subtype of [DateTimePeriod],
35
+ * will be returned if all time components happen to be zero.
36
+ *
37
+ * A `DateTimePeriod` can be constructed using the constructor function with the same name.
38
+ *
39
+ * [parse] and [toString] methods can be used to obtain a [DateTimePeriod] from and convert it to a string in the
40
+ * ISO 8601 extended format (for example, `P1Y2M6DT13H`).
41
+ *
42
+ * or returned as the result of instant arithmetic operations (see [Instant.periodUntil]).
43
+ *
44
+ * Additionally, there are several `kotlinx-serialization` serializers for [DateTimePeriod]:
45
+ * - [DateTimePeriodIso8601Serializer] for the ISO 8601 format;
46
+ * - [DateTimePeriodComponentSerializer] for an object with components.
26
47
*/
27
48
@Serializable(with = DateTimePeriodIso8601Serializer ::class )
28
49
// TODO: could be error-prone without explicitly named params
@@ -33,6 +54,7 @@ public sealed class DateTimePeriod {
33
54
* The number of calendar days.
34
55
*
35
56
* Note that a calendar day is not identical to 24 hours, see [DateTimeUnit.DayBased] for details.
57
+ * Also, this field does not overflow into months, so values larger than 30 can be present.
36
58
*/
37
59
public abstract val days: Int
38
60
internal abstract val totalNanoseconds: Long
@@ -49,6 +71,8 @@ public sealed class DateTimePeriod {
49
71
50
72
/* *
51
73
* The number of whole hours in this period.
74
+ *
75
+ * This field does not overflow into days, so values larger than 23 can be present.
52
76
*/
53
77
public open val hours: Int get() = (totalNanoseconds / 3_600_000_000_000 ).toInt()
54
78
@@ -72,7 +96,7 @@ public sealed class DateTimePeriod {
72
96
totalMonths <= 0 && days <= 0 && totalNanoseconds <= 0 && (totalMonths or days != 0 || totalNanoseconds != 0L )
73
97
74
98
/* *
75
- * Converts this period to the ISO-8601 string representation for durations.
99
+ * Converts this period to the ISO-8601 string representation for durations, for example, `P2M1DT3H` .
76
100
*
77
101
* @see DateTimePeriod.parse
78
102
*/
@@ -304,10 +328,13 @@ public sealed class DateTimePeriod {
304
328
public fun String.toDateTimePeriod (): DateTimePeriod = DateTimePeriod .parse(this )
305
329
306
330
/* *
307
- * A special case of [DateTimePeriod] that only stores date components and has all time components equal to zero.
331
+ * A special case of [DateTimePeriod] that only stores the date components and has all time components equal to zero.
308
332
*
309
333
* A `DatePeriod` is automatically returned from all constructor functions for [DateTimePeriod] if it turns out that
310
334
* the time components are zero.
335
+ * Additionally, [DatePeriod] has its own constructor, the [parse] function that will fail if any of the time components
336
+ * are not zero, and [DatePeriodIso8601Serializer] and [DatePeriodComponentSerializer], mirroring those of
337
+ * [DateTimePeriod].
311
338
*
312
339
* `DatePeriod` values are used in operations on [LocalDates][LocalDate] and are returned from operations on [LocalDates][LocalDate],
313
340
* but they also can be passed anywhere a [DateTimePeriod] is expected.
@@ -317,6 +344,17 @@ public class DatePeriod internal constructor(
317
344
internal override val totalMonths : Int ,
318
345
override val days : Int ,
319
346
) : DateTimePeriod() {
347
+ /* *
348
+ * Constructs a new [DatePeriod].
349
+ *
350
+ * It is recommended to always explicitly name the arguments when constructing this manually,
351
+ * like `DatePeriod(years = 1, months = 12)`.
352
+ *
353
+ * The passed numbers are not stored as is but are normalized instead for human readability, so, for example,
354
+ * `DateTimePeriod(months = 24)` becomes `DateTimePeriod(years = 2)`.
355
+ *
356
+ * @throws IllegalArgumentException if the total number of months in [years] and [months] overflows an [Int].
357
+ */
320
358
public constructor (years: Int = 0 , months: Int = 0 , days: Int = 0 ): this (totalMonths(years, months), days)
321
359
// avoiding excessive computations
322
360
/* * The number of whole hours in this period. Always equal to zero. */
@@ -334,7 +372,7 @@ public class DatePeriod internal constructor(
334
372
335
373
public companion object {
336
374
/* *
337
- * Parses the ISO-8601 duration representation as a [DatePeriod].
375
+ * Parses the ISO-8601 duration representation as a [DatePeriod], for example, `P1Y2M30D` .
338
376
*
339
377
* This function is equivalent to [DateTimePeriod.parse], but will fail if any of the time components are not
340
378
* zero.
@@ -422,6 +460,11 @@ public fun DateTimePeriod(
422
460
*
423
461
* If the duration value is too big to be represented as a [Long] number of nanoseconds,
424
462
* the result will be [Long.MAX_VALUE] nanoseconds.
463
+ *
464
+ * **Pitfall**: a [DateTimePeriod] obtained this way will always have zero date components.
465
+ * The reason is that even a [Duration] obtained via [Duration.Companion.days] just means a multiple of 24 hours,
466
+ * whereas in `kotlinx-datetime`, a day is a calendar day, which can be different from 24 hours.
467
+ * See [DateTimeUnit.DayBased] for details.
425
468
*/
426
469
// TODO: maybe it's more consistent to throw here on overflow?
427
470
public fun Duration.toDateTimePeriod (): DateTimePeriod = buildDateTimePeriod(totalNanoseconds = inWholeNanoseconds)
0 commit comments