0
# Periods & Units
1
2
This document covers period representations and arithmetic units for flexible date and time calculations: `DateTimePeriod`, `DatePeriod`, and the `DateTimeUnit` hierarchy.
3
4
## DateTimePeriod
5
6
A period with both date and time components, representing a difference between two instants decomposed into years, months, days, hours, minutes, seconds, and nanoseconds.
7
8
```kotlin { .api }
9
// Constructor
10
data class DateTimePeriod(
11
val years: Int = 0,
12
val months: Int = 0,
13
val days: Int = 0,
14
val hours: Int = 0,
15
val minutes: Int = 0,
16
val seconds: Int = 0,
17
val nanoseconds: Long = 0
18
)
19
20
// Properties
21
val DateTimePeriod.years: Int
22
val DateTimePeriod.months: Int
23
val DateTimePeriod.days: Int
24
val DateTimePeriod.hours: Int
25
val DateTimePeriod.minutes: Int
26
val DateTimePeriod.seconds: Int
27
val DateTimePeriod.nanoseconds: Long
28
29
// Arithmetic operations
30
operator fun DateTimePeriod.plus(other: DateTimePeriod): DateTimePeriod
31
operator fun DateTimePeriod.minus(other: DateTimePeriod): DateTimePeriod
32
operator fun DateTimePeriod.unaryMinus(): DateTimePeriod
33
34
// Companion object functions
35
fun DateTimePeriod.Companion.parse(input: CharSequence): DateTimePeriod
36
37
// String representation (ISO 8601 format)
38
fun DateTimePeriod.toString(): String
39
```
40
41
### Usage Examples
42
43
```kotlin
44
// Create periods
45
val oneYear = DateTimePeriod(years = 1)
46
val twoWeeks = DateTimePeriod(days = 14)
47
val fullPeriod = DateTimePeriod(
48
years = 1,
49
months = 2,
50
days = 15,
51
hours = 3,
52
minutes = 30,
53
seconds = 45
54
)
55
56
// Arithmetic on periods
57
val combined = oneYear + twoWeeks
58
val difference = fullPeriod - oneYear
59
val negated = -fullPeriod
60
61
// Parse ISO 8601 period strings
62
val parsed = DateTimePeriod.parse("P1Y2M15DT3H30M45S")
63
64
// Use with Instant arithmetic (requires timezone)
65
val instant = Clock.System.now()
66
val timeZone = TimeZone.currentSystemDefault()
67
val futureInstant = instant.plus(fullPeriod, timeZone)
68
```
69
70
## DatePeriod
71
72
A period with only date components (years, months, days), representing calendar-based time differences. This is a subtype of `DateTimePeriod` with all time components set to zero.
73
74
```kotlin { .api }
75
// Constructor
76
data class DatePeriod(
77
val years: Int = 0,
78
val months: Int = 0,
79
val days: Int = 0
80
) : DateTimePeriod
81
82
// Properties (inherited from DateTimePeriod)
83
val DatePeriod.years: Int
84
val DatePeriod.months: Int
85
val DatePeriod.days: Int
86
87
// Arithmetic operations (inherited from DateTimePeriod)
88
operator fun DatePeriod.plus(other: DatePeriod): DatePeriod
89
operator fun DatePeriod.minus(other: DatePeriod): DatePeriod
90
operator fun DatePeriod.unaryMinus(): DatePeriod
91
92
// Companion object functions
93
fun DatePeriod.Companion.parse(input: CharSequence): DatePeriod
94
95
// String representation (ISO 8601 format)
96
fun DatePeriod.toString(): String
97
```
98
99
### Usage Examples
100
101
```kotlin
102
// Create date periods
103
val oneYear = DatePeriod(years = 1)
104
val sixMonths = DatePeriod(months = 6)
105
val twoWeeks = DatePeriod(days = 14)
106
107
// Combine periods
108
val quarterAndTwoWeeks = DatePeriod(months = 3, days = 14)
109
val combined = sixMonths + twoWeeks
110
111
// Parse ISO 8601 date period strings
112
val parsed = DatePeriod.parse("P1Y6M14D")
113
114
// Use with LocalDate arithmetic
115
val date = LocalDate(2023, 1, 1)
116
val futureDate = date.plus(quarterAndTwoWeeks)
117
val periodBetween = date.periodUntil(futureDate)
118
```
119
120
## DateTimeUnit
121
122
Sealed class hierarchy representing units for measuring time, providing both duration-based and calendar-based units for datetime arithmetic.
123
124
```kotlin { .api }
125
// Base sealed class
126
sealed class DateTimeUnit {
127
// Duration-based units (independent of calendar/timezone)
128
sealed class TimeBased : DateTimeUnit() {
129
val duration: Duration
130
}
131
132
// Calendar-based units (depend on calendar/timezone)
133
sealed class DateBased : DateTimeUnit()
134
135
// Day-based units (subtype of DateBased)
136
sealed class DayBased : DateBased() {
137
val days: Int
138
}
139
140
// Month-based units (subtype of DateBased)
141
sealed class MonthBased : DateBased() {
142
val months: Int
143
}
144
}
145
```
146
147
### Predefined Units
148
149
```kotlin { .api }
150
// Time-based units (duration-based)
151
val DateTimeUnit.NANOSECOND: DateTimeUnit.TimeBased
152
val DateTimeUnit.MICROSECOND: DateTimeUnit.TimeBased
153
val DateTimeUnit.MILLISECOND: DateTimeUnit.TimeBased
154
val DateTimeUnit.SECOND: DateTimeUnit.TimeBased
155
val DateTimeUnit.MINUTE: DateTimeUnit.TimeBased
156
val DateTimeUnit.HOUR: DateTimeUnit.TimeBased
157
158
// Date-based units (calendar-based)
159
val DateTimeUnit.DAY: DateTimeUnit.DayBased
160
val DateTimeUnit.WEEK: DateTimeUnit.DayBased
161
val DateTimeUnit.MONTH: DateTimeUnit.MonthBased
162
val DateTimeUnit.QUARTER: DateTimeUnit.MonthBased
163
val DateTimeUnit.YEAR: DateTimeUnit.MonthBased
164
```
165
166
### Factory Functions
167
168
```kotlin { .api }
169
// Create custom time-based units
170
fun DateTimeUnit.TimeBased(duration: Duration): DateTimeUnit.TimeBased
171
172
// Create custom day-based units
173
fun DateTimeUnit.DayBased(days: Int): DateTimeUnit.DayBased
174
175
// Create custom month-based units
176
fun DateTimeUnit.MonthBased(months: Int): DateTimeUnit.MonthBased
177
178
// Multiplication operators for creating multiples
179
operator fun DateTimeUnit.times(scalar: Int): DateTimeUnit
180
operator fun Int.times(unit: DateTimeUnit): DateTimeUnit
181
```
182
183
### Usage with Different Types
184
185
#### With Instant (requires TimeZone for DateBased units)
186
187
```kotlin { .api }
188
// TimeBased units - no timezone required
189
fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant
190
fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant
191
fun Instant.minus(value: Int, unit: DateTimeUnit.TimeBased): Instant
192
fun Instant.minus(value: Long, unit: DateTimeUnit.TimeBased): Instant
193
fun Instant.until(other: Instant, unit: DateTimeUnit.TimeBased): Long
194
195
// DateBased units - timezone required
196
fun Instant.plus(value: Int, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
197
fun Instant.plus(value: Long, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
198
fun Instant.minus(value: Int, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
199
fun Instant.minus(value: Long, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Instant
200
fun Instant.until(other: Instant, unit: DateTimeUnit.DateBased, timeZone: TimeZone): Long
201
```
202
203
#### With LocalDate (only DateBased units)
204
205
```kotlin { .api }
206
fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
207
fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate
208
fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
209
fun LocalDate.minus(value: Long, unit: DateTimeUnit.DateBased): LocalDate
210
fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased): Long
211
```
212
213
### Usage Examples
214
215
```kotlin
216
// Using predefined units
217
val instant = Clock.System.now()
218
val timeZone = TimeZone.currentSystemDefault()
219
220
// Time-based units (no timezone needed)
221
val oneHourLater = instant.plus(1, DateTimeUnit.HOUR)
222
val thirtySecondsEarlier = instant.minus(30, DateTimeUnit.SECOND)
223
224
// Date-based units (timezone required for Instant)
225
val oneDayLater = instant.plus(1, DateTimeUnit.DAY, timeZone)
226
val threeMonthsLater = instant.plus(3, DateTimeUnit.MONTH, timeZone)
227
228
// Custom units
229
val tenDays = DateTimeUnit.DayBased(10)
230
val sixMonths = DateTimeUnit.MonthBased(6)
231
val fiveMinutes = DateTimeUnit.TimeBased(5.minutes)
232
233
// Unit multiplication
234
val twoWeeks = DateTimeUnit.WEEK * 2
235
val threeQuarters = 3 * DateTimeUnit.QUARTER
236
237
// With LocalDate (only date-based units)
238
val date = LocalDate(2023, 6, 15)
239
val futureDate = date.plus(2, DateTimeUnit.MONTH)
240
val monthsBetween = date.until(futureDate, DateTimeUnit.MONTH)
241
242
// Calculate differences
243
val daysBetween = date.until(futureDate, DateTimeUnit.DAY)
244
val weeksBetween = date.until(futureDate, DateTimeUnit.WEEK)
245
```
246
247
## Working with Periods vs Units
248
249
### When to Use DateTimePeriod/DatePeriod
250
251
- Representing a combination of different time components (e.g., "2 years, 3 months, and 5 days")
252
- Storing the result of period calculations between two dates/instants
253
- Serializing/deserializing period information
254
- When you need all components preserved separately
255
256
```kotlin
257
// Period example - combination of components
258
val complexPeriod = DateTimePeriod(years = 2, months = 3, days = 5, hours = 4)
259
val instant = Clock.System.now()
260
val futureInstant = instant.plus(complexPeriod, TimeZone.currentSystemDefault())
261
```
262
263
### When to Use DateTimeUnit
264
265
- Performing arithmetic with a single unit type (e.g., "add 5 days")
266
- Measuring differences in specific units (e.g., "how many weeks between these dates?")
267
- Creating regular intervals or recurring events
268
- When you need precise control over the arithmetic operation
269
270
```kotlin
271
// Unit example - single unit arithmetic
272
val date = LocalDate(2023, 1, 1)
273
val futureDate = date.plus(30, DateTimeUnit.DAY)
274
val daysBetween = date.until(futureDate, DateTimeUnit.DAY) // returns 30
275
276
// Custom intervals
277
val biweekly = DateTimeUnit.WEEK * 2
278
val quarterly = DateTimeUnit.MONTH * 3
279
```
280
281
## Edge Cases and Considerations
282
283
### Time Zone Transitions
284
285
Date-based arithmetic accounts for daylight saving time and timezone rule changes:
286
287
```kotlin
288
// DST transition example
289
val timeZone = TimeZone.of("Europe/London")
290
val springForward = LocalDateTime(2023, 3, 26, 1, 30) // Before DST
291
val instant = springForward.toInstant(timeZone)
292
293
// Adding 1 hour might skip over the "missing" hour
294
val oneHourLater = instant.plus(1, DateTimeUnit.HOUR)
295
val localTime = oneHourLater.toLocalDateTime(timeZone) // 03:30, not 02:30
296
```
297
298
### Month Length Variations
299
300
Month arithmetic handles varying month lengths appropriately:
301
302
```kotlin
303
val january31 = LocalDate(2023, 1, 31)
304
val february = january31.plus(1, DateTimeUnit.MONTH) // 2023-02-28 (not 2023-02-31)
305
306
val leap = LocalDate(2024, 2, 29) // Leap year
307
val nextYear = leap.plus(1, DateTimeUnit.YEAR) // 2025-02-28 (leap day doesn't exist)
308
```
309
310
### Normalization
311
312
Periods are automatically normalized:
313
314
```kotlin
315
val period1 = DateTimePeriod(months = 24, hours = 2, minutes = 63)
316
val period2 = DateTimePeriod(years = 2, hours = 3, minutes = 3)
317
// period1 and period2 are equivalent after normalization
318
```