Skip to content

Commit 9828d4c

Browse files
committed
Match 'plus' with the corresponding 'minus' functions
1 parent c3792e5 commit 9828d4c

File tree

5 files changed

+133
-1
lines changed

5 files changed

+133
-1
lines changed

core/commonMain/src/DateTimePeriod.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ sealed class DateTimePeriod {
6767
return result
6868
}
6969

70+
internal operator fun unaryMinus(): DateTimePeriod =
71+
DateTimePeriod(-years, -months, -days, -hours, -minutes, -seconds, -nanoseconds)
72+
7073
// TODO: parsing from iso string
7174
}
7275

core/commonMain/src/Instant.kt

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ public fun String.toInstant(): Instant = Instant.parse(this)
173173
*/
174174
public expect fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant
175175

176+
/**
177+
* Returns an instant that is the result of subtracting components of [DateTimePeriod] from this instant. The components
178+
* are subtracted in the order from the largest units to the smallest, i.e. from years to nanoseconds.
179+
*
180+
* @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in
181+
* [LocalDateTime].
182+
*/
183+
public fun Instant.minus(period: DateTimePeriod, timeZone: TimeZone): Instant = plus(-period, timeZone)
184+
176185
/**
177186
* Returns a [DateTimePeriod] representing the difference between `this` and [other] instants.
178187
*
@@ -282,6 +291,17 @@ public fun Instant.minus(other: Instant, timeZone: TimeZone): DateTimePeriod =
282291
*/
283292
public expect fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant
284293

294+
/**
295+
* Returns an instant that is the result of subtracting one [unit] from this instant
296+
* in the specified [timeZone].
297+
*
298+
* The returned instant is earlier than this instant.
299+
*
300+
* @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime].
301+
*/
302+
public fun Instant.minus(unit: DateTimeUnit, timeZone: TimeZone): Instant =
303+
plus(-1, unit, timeZone)
304+
285305
/**
286306
* Returns an instant that is the result of adding one [unit] to this instant.
287307
*
@@ -292,6 +312,16 @@ public expect fun Instant.plus(unit: DateTimeUnit, timeZone: TimeZone): Instant
292312
public fun Instant.plus(unit: DateTimeUnit.TimeBased): Instant =
293313
plus(1L, unit)
294314

315+
/**
316+
* Returns an instant that is the result of subtracting one [unit] from this instant.
317+
*
318+
* The returned instant is earlier than this instant.
319+
*
320+
* The return value is clamped to the platform-specific boundaries for [Instant] if the result exceeds them.
321+
*/
322+
public fun Instant.minus(unit: DateTimeUnit.TimeBased): Instant =
323+
plus(-1L, unit)
324+
295325
/**
296326
* Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant
297327
* in the specified [timeZone].
@@ -303,6 +333,18 @@ public fun Instant.plus(unit: DateTimeUnit.TimeBased): Instant =
303333
*/
304334
public expect fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant
305335

336+
/**
337+
* Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant
338+
* in the specified [timeZone].
339+
*
340+
* If the [value] is positive, the returned instant is earlier than this instant.
341+
* If the [value] is negative, the returned instant is later than this instant.
342+
*
343+
* @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime].
344+
*/
345+
public fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant =
346+
plus(-value, unit, timeZone)
347+
306348
/**
307349
* Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant.
308350
*
@@ -314,6 +356,17 @@ public expect fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZon
314356
public fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant =
315357
plus(value.toLong(), unit)
316358

359+
/**
360+
* Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant.
361+
*
362+
* If the [value] is positive, the returned instant is earlier than this instant.
363+
* If the [value] is negative, the returned instant is later than this instant.
364+
*
365+
* The return value is clamped to the platform-specific boundaries for [Instant] if the result exceeds them.
366+
*/
367+
public fun Instant.minus(value: Int, unit: DateTimeUnit.TimeBased): Instant =
368+
plus(-value.toLong(), unit)
369+
317370
/**
318371
* Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant
319372
* in the specified [timeZone].
@@ -325,6 +378,17 @@ public fun Instant.plus(value: Int, unit: DateTimeUnit.TimeBased): Instant =
325378
*/
326379
public expect fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant
327380

381+
/**
382+
* Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant
383+
* in the specified [timeZone].
384+
*
385+
* If the [value] is positive, the returned instant is earlier than this instant.
386+
* If the [value] is negative, the returned instant is later than this instant.
387+
*
388+
* @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime].
389+
*/
390+
public fun Instant.minus(value: Long, unit: DateTimeUnit, timeZone: TimeZone) = plus(-value, unit, timeZone)
391+
328392
/**
329393
* Returns an instant that is the result of adding the [value] number of the specified [unit] to this instant.
330394
*
@@ -335,6 +399,16 @@ public expect fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZo
335399
*/
336400
public expect fun Instant.plus(value: Long, unit: DateTimeUnit.TimeBased): Instant
337401

402+
/**
403+
* Returns an instant that is the result of subtracting the [value] number of the specified [unit] from this instant.
404+
*
405+
* If the [value] is positive, the returned instant is earlier than this instant.
406+
* If the [value] is negative, the returned instant is later than this instant.
407+
*
408+
* The return value is clamped to the platform-specific boundaries for [Instant] if the result exceeds them.
409+
*/
410+
public fun Instant.minus(value: Long, unit: DateTimeUnit.TimeBased): Instant = plus(-value, unit)
411+
338412
/**
339413
* Returns the whole number of the specified date or time [units][unit] between [other] and `this` instants
340414
* in the specified [timeZone].

core/commonMain/src/LocalDate.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ public fun LocalDate.atTime(hour: Int, minute: Int, second: Int = 0, nanosecond:
110110
*/
111111
expect operator fun LocalDate.plus(period: DatePeriod): LocalDate
112112

113+
/**
114+
* Returns a date that is the result of subtracting components of [DatePeriod] from this date. The components are
115+
* subtracted in the order from the largest units to the smallest, i.e. from years to days.
116+
*
117+
* @see LocalDate.periodUntil
118+
* @throws DateTimeArithmeticException if this value or the results of intermediate computations are too large to fit in
119+
* [LocalDate].
120+
*/
121+
public operator fun LocalDate.minus(period: DatePeriod): LocalDate = plus(-period as DatePeriod)
122+
113123
/**
114124
* Returns a [DatePeriod] representing the difference between `this` and [other] dates.
115125
*
@@ -186,6 +196,15 @@ public expect fun LocalDate.yearsUntil(other: LocalDate): Int
186196
*/
187197
public expect fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate
188198

199+
/**
200+
* Returns a [LocalDate] that is the result of subtracting one [unit] from this date.
201+
*
202+
* The returned date is earlier than this date.
203+
*
204+
* @throws DateTimeArithmeticException if the result exceeds the boundaries of [LocalDate].
205+
*/
206+
public fun LocalDate.minus(unit: DateTimeUnit.DateBased): LocalDate = plus(-1, unit)
207+
189208
/**
190209
* Returns a [LocalDate] that is the result of adding the [value] number of the specified [unit] to this date.
191210
*
@@ -196,6 +215,16 @@ public expect fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate
196215
*/
197216
public expect fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate
198217

218+
/**
219+
* Returns a [LocalDate] that is the result of subtracting the [value] number of the specified [unit] from this date.
220+
*
221+
* If the [value] is positive, the returned date is earlier than this date.
222+
* If the [value] is negative, the returned date is later than this date.
223+
*
224+
* @throws DateTimeArithmeticException if the result exceeds the boundaries of [LocalDate].
225+
*/
226+
public fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate = plus(-value, unit)
227+
199228
/**
200229
* Returns a [LocalDate] that is the result of adding the [value] number of the specified [unit] to this date.
201230
*
@@ -205,3 +234,13 @@ public expect fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): Loca
205234
* @throws DateTimeArithmeticException if the result exceeds the boundaries of [LocalDate].
206235
*/
207236
public expect fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate
237+
238+
/**
239+
* Returns a [LocalDate] that is the result of subtracting the [value] number of the specified [unit] from this date.
240+
*
241+
* If the [value] is positive, the returned date is earlier than this date.
242+
* If the [value] is negative, the returned date is later than this date.
243+
*
244+
* @throws DateTimeArithmeticException if the result exceeds the boundaries of [LocalDate].
245+
*/
246+
public fun LocalDate.minus(value: Long, unit: DateTimeUnit.DateBased): LocalDate = plus(-value, unit)

core/commonTest/src/InstantTest.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class InstantTest {
100100
val diff = instant2.minus(instant1, timeUnit, zone)
101101
assertEquals(instant2 - instant1, timeUnit.duration * diff.toDouble())
102102
assertEquals(instant2, instant1.plus(diff, timeUnit, zone))
103+
assertEquals(instant1, instant2.minus(diff, timeUnit, zone))
104+
assertEquals(instant2, instant1.plus(diff, timeUnit))
105+
assertEquals(instant1, instant2.minus(diff, timeUnit))
103106
}
104107
}
105108

@@ -109,12 +112,14 @@ class InstantTest {
109112
val instant2 = instant1.plus(DateTimePeriod(hours = 24), zone)
110113
checkComponents(instant2.toLocalDateTime(zone), 2019, 10, 28, 1, 59)
111114
expectBetween(instant1, instant2, 24, DateTimeUnit.HOUR)
115+
assertEquals(instant1, instant2.minus(DateTimePeriod(hours = 24), zone))
112116

113117
val instant3 = instant1.plus(DateTimeUnit.DAY, zone)
114118
checkComponents(instant3.toLocalDateTime(zone), 2019, 10, 28, 2, 59)
115119
expectBetween(instant1, instant3, 25, DateTimeUnit.HOUR)
116120
expectBetween(instant1, instant3, 1, DateTimeUnit.DAY)
117121
assertEquals(1, instant1.daysUntil(instant3, zone))
122+
assertEquals(instant1.minus(DateTimeUnit.HOUR), instant2.minus(DateTimeUnit.DAY, zone))
118123

119124
val instant4 = instant1.plus(14, DateTimeUnit.MONTH, zone)
120125
checkComponents(instant4.toLocalDateTime(zone), 2020, 12, 27, 2, 59)
@@ -124,19 +129,27 @@ class InstantTest {
124129
expectBetween(instant1, instant4, 61, DateTimeUnit.WEEK)
125130
expectBetween(instant1, instant4, 366 + 31 + 30, DateTimeUnit.DAY)
126131
expectBetween(instant1, instant4, (366 + 31 + 30) * 24 + 1, DateTimeUnit.HOUR)
127-
132+
/* TODO: make this pass on Darwin-based systems.
133+
* Since the Darwin implementation of `offset_at_datetime` can't utilize the preferred offset,
134+
* when there's overlap, the implementation always assumes that the used offset is the one before the
135+
* transition. However, it's not always the case, as seen here. In practice, this is a problem in
136+
* scenarios where the destination of an arithmetic operation on datetimes is an overlap, and the source
137+
* datetime is outside the daylight saving time. */
138+
// assertEquals(instant1.plus(DateTimeUnit.HOUR), instant4.minus(14, DateTimeUnit.MONTH, zone))
128139

129140
val period = DateTimePeriod(days = 1, hours = 1)
130141
val instant5 = instant1.plus(period, zone)
131142
checkComponents(instant5.toLocalDateTime(zone), 2019, 10, 28, 3, 59)
132143
assertEquals(period, instant1.periodUntil(instant5, zone))
133144
assertEquals(period, instant5.minus(instant1, zone))
134145
assertEquals(26.hours, instant5.minus(instant1))
146+
assertEquals(instant1.plus(DateTimeUnit.HOUR), instant5.minus(period, zone))
135147

136148
val instant6 = instant1.plus(23, DateTimeUnit.HOUR, zone)
137149
checkComponents(instant6.toLocalDateTime(zone), 2019, 10, 28, 0, 59)
138150
expectBetween(instant1, instant6, 23, DateTimeUnit.HOUR)
139151
expectBetween(instant1, instant6, 0, DateTimeUnit.DAY)
152+
assertEquals(instant1, instant6.minus(23, DateTimeUnit.HOUR, zone))
140153
}
141154

142155
@Test

core/commonTest/src/LocalDateTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class LocalDateTest {
9191
checkComponents(startDate.plus(1, DateTimeUnit.DAY), 2016, 3, 1)
9292
checkComponents(startDate.plus(DateTimeUnit.YEAR), 2017, 2, 28)
9393
checkComponents(startDate + DatePeriod(years = 4), 2020, 2, 29)
94+
assertEquals(startDate, startDate.plus(DateTimeUnit.DAY).minus(DateTimeUnit.DAY))
95+
assertEquals(startDate, startDate.plus(3, DateTimeUnit.DAY).minus(3, DateTimeUnit.DAY))
96+
assertEquals(startDate, startDate + DatePeriod(years = 4) - DatePeriod(years = 4))
9497

9598
checkComponents(LocalDate.parse("2016-01-31") + DatePeriod(months = 1), 2016, 2, 29)
9699

0 commit comments

Comments
 (0)