or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arithmetic.mdformatting.mdindex.mdinstant.mdlocal-types.mdplatform.mdranges.mdserialization.mdtimezones.md

arithmetic.mddocs/

0

# Date/Time Arithmetic

1

2

Date and time arithmetic operations using units, periods, and duration-based calculations. The library provides flexible arithmetic capabilities for both precise time-based operations and calendar-aware date operations.

3

4

## Capabilities

5

6

### DateTimeUnit

7

8

Sealed class hierarchy representing units for measuring time intervals, supporting both precise time-based units and calendar-based units.

9

10

```kotlin { .api }

11

/**

12

* Units for measuring time intervals

13

* Base sealed class for all time measurement units

14

*/

15

sealed class DateTimeUnit {

16

/**

17

* Multiply this unit by a scalar value

18

* @param scalar Multiplier for the unit

19

* @returns New DateTimeUnit representing the scaled unit

20

*/

21

abstract fun times(scalar: Int): DateTimeUnit

22

}

23

```

24

25

**Time-Based Units:**

26

27

```kotlin { .api }

28

/**

29

* Time-based unit with precise nanosecond duration

30

* Represents units that have a fixed duration in nanoseconds

31

*/

32

class TimeBased(val nanoseconds: Long) : DateTimeUnit() {

33

override fun times(scalar: Int): TimeBased

34

}

35

36

// Predefined time-based constants

37

object DateTimeUnit {

38

val NANOSECOND: TimeBased // 1 nanosecond

39

val MICROSECOND: TimeBased // 1,000 nanoseconds

40

val MILLISECOND: TimeBased // 1,000,000 nanoseconds

41

val SECOND: TimeBased // 1,000,000,000 nanoseconds

42

val MINUTE: TimeBased // 60 seconds

43

val HOUR: TimeBased // 60 minutes

44

}

45

```

46

47

**Date-Based Units:**

48

49

```kotlin { .api }

50

/**

51

* Date-based unit (abstract base for calendar units)

52

* Represents units that vary in duration based on calendar context

53

*/

54

sealed class DateBased : DateTimeUnit()

55

56

/**

57

* Day-based unit representing a number of calendar days

58

* Duration varies based on DST transitions and leap seconds

59

*/

60

class DayBased(val days: Int) : DateBased() {

61

override fun times(scalar: Int): DayBased

62

}

63

64

/**

65

* Month-based unit representing a number of months

66

* Duration varies significantly based on month length and year

67

*/

68

class MonthBased(val months: Int) : DateBased() {

69

override fun times(scalar: Int): MonthBased

70

}

71

72

// Predefined date-based constants

73

object DateTimeUnit {

74

val DAY: DayBased // 1 calendar day

75

val WEEK: DayBased // 7 calendar days

76

val MONTH: MonthBased // 1 calendar month

77

val QUARTER: MonthBased // 3 calendar months

78

val YEAR: MonthBased // 12 calendar months

79

val CENTURY: MonthBased // 1200 calendar months

80

}

81

```

82

83

**Usage Examples:**

84

85

```kotlin

86

import kotlinx.datetime.*

87

import kotlin.time.Clock

88

89

val now = Clock.System.now()

90

91

// Time-based arithmetic (precise)

92

val oneHourLater = now.plus(1, DateTimeUnit.HOUR, TimeZone.UTC)

93

val thirtyMinutes = now.plus(30, DateTimeUnit.MINUTE, TimeZone.UTC)

94

val fiveSeconds = now.plus(5, DateTimeUnit.SECOND, TimeZone.UTC)

95

96

// Custom time units

97

val twoHours = DateTimeUnit.HOUR.times(2)

98

val halfHour = DateTimeUnit.MINUTE.times(30)

99

100

// Date-based arithmetic (calendar-aware)

101

val tomorrow = now.plus(1, DateTimeUnit.DAY, TimeZone.of("America/New_York"))

102

val nextWeek = now.plus(1, DateTimeUnit.WEEK, TimeZone.UTC)

103

val nextMonth = now.plus(1, DateTimeUnit.MONTH, TimeZone.currentSystemDefault())

104

```

105

106

### DateTimePeriod

107

108

Represents a combination of date and time components for complex arithmetic operations.

109

110

```kotlin { .api }

111

/**

112

* Combination of date and time components

113

* Represents a period with separate year/month/day/hour/minute/second/nanosecond parts

114

*/

115

sealed class DateTimePeriod {

116

/** Whole years */

117

abstract val years: Int

118

119

/** Months not forming whole years (-11..11) */

120

abstract val months: Int

121

122

/** Calendar days (can exceed month boundaries) */

123

abstract val days: Int

124

125

/** Whole hours (can exceed day boundaries) */

126

abstract val hours: Int

127

128

/** Minutes not forming whole hours (-59..59) */

129

abstract val minutes: Int

130

131

/** Seconds not forming whole minutes (-59..59) */

132

abstract val seconds: Int

133

134

/** Nanoseconds not forming whole seconds */

135

abstract val nanoseconds: Int

136

}

137

```

138

139

**Factory Functions:**

140

141

```kotlin { .api }

142

/**

143

* Create a DateTimePeriod with specified components

144

* @param years Number of years (default 0)

145

* @param months Number of months (default 0)

146

* @param days Number of days (default 0)

147

* @param hours Number of hours (default 0)

148

* @param minutes Number of minutes (default 0)

149

* @param seconds Number of seconds (default 0)

150

* @param nanoseconds Number of nanoseconds (default 0)

151

* @returns DateTimePeriod with the specified components

152

*/

153

fun DateTimePeriod(

154

years: Int = 0,

155

months: Int = 0,

156

days: Int = 0,

157

hours: Int = 0,

158

minutes: Int = 0,

159

seconds: Int = 0,

160

nanoseconds: Long = 0

161

): DateTimePeriod

162

163

/**

164

* Parse DateTimePeriod from ISO 8601 duration string

165

* @param text ISO 8601 duration string (e.g., "P1Y2M3DT4H5M6.7S")

166

* @returns Parsed DateTimePeriod

167

*/

168

fun DateTimePeriod.Companion.parse(text: String): DateTimePeriod

169

170

/**

171

* Convert kotlin.time.Duration to DateTimePeriod

172

* @returns DateTimePeriod representing the duration

173

*/

174

fun Duration.toDateTimePeriod(): DateTimePeriod

175

```

176

177

**Formatting:**

178

179

```kotlin { .api }

180

/**

181

* Convert to ISO 8601 duration format

182

* @returns ISO 8601 duration string (e.g., "P1Y2M3DT4H5M6.789S")

183

*/

184

override fun DateTimePeriod.toString(): String

185

```

186

187

**Usage Examples:**

188

189

```kotlin

190

import kotlinx.datetime.*

191

import kotlin.time.*

192

193

// Create periods

194

val complexPeriod = DateTimePeriod(

195

years = 1,

196

months = 2,

197

days = 15,

198

hours = 8,

199

minutes = 30,

200

seconds = 45

201

)

202

203

val shortPeriod = DateTimePeriod(hours = 2, minutes = 30)

204

205

// From Duration

206

val duration = 2.hours + 30.minutes

207

val period = duration.toDateTimePeriod()

208

209

// Parse from ISO 8601

210

val parsed = DateTimePeriod.parse("P1Y2M15DT8H30M45S")

211

212

// Format as ISO 8601

213

println(complexPeriod.toString()) // "P1Y2M15DT8H30M45S"

214

215

// Use in arithmetic

216

val now = Clock.System.now()

217

val timeZone = TimeZone.of("Europe/Paris")

218

val future = now.plus(complexPeriod, timeZone)

219

```

220

221

### DatePeriod

222

223

Specialized period class for date-only arithmetic operations.

224

225

```kotlin { .api }

226

/**

227

* Date-only period (time components are zero)

228

* Extends DateTimePeriod with only date components

229

*/

230

class DatePeriod : DateTimePeriod {

231

/**

232

* Create a date period with specified components

233

* @param years Number of years (default 0)

234

* @param months Number of months (default 0)

235

* @param days Number of days (default 0)

236

*/

237

constructor(years: Int = 0, months: Int = 0, days: Int = 0)

238

239

companion object {

240

/**

241

* Parse DatePeriod from ISO 8601 date duration string

242

* @param text ISO 8601 date duration (e.g., "P1Y2M15D")

243

* @returns Parsed DatePeriod

244

*/

245

fun parse(text: String): DatePeriod

246

}

247

}

248

```

249

250

**Usage Examples:**

251

252

```kotlin

253

import kotlinx.datetime.*

254

255

// Create date periods

256

val oneYear = DatePeriod(years = 1)

257

val twoMonths = DatePeriod(months = 2)

258

val fifteenDays = DatePeriod(days = 15)

259

val combined = DatePeriod(years = 1, months = 6, days = 10)

260

261

// Parse from ISO 8601

262

val parsed = DatePeriod.parse("P1Y6M10D")

263

264

// Use with LocalDate

265

val today = LocalDate(2023, 12, 25)

266

val nextYear = today.plus(oneYear)

267

val futureDate = today.plus(combined)

268

269

// Use with Instant (requires time zone for calendar arithmetic)

270

val now = Clock.System.now()

271

val timeZone = TimeZone.of("America/New_York")

272

val futureInstant = now.plus(combined, timeZone)

273

```

274

275

## Instant Arithmetic Operations

276

277

Extension functions for performing arithmetic on Instant values.

278

279

### Basic Arithmetic

280

281

```kotlin { .api }

282

/**

283

* Add a DateTimePeriod to an Instant in the specified time zone

284

* @param period Period to add

285

* @param timeZone Time zone for calendar calculations

286

* @returns New Instant representing the result

287

*/

288

fun Instant.plus(period: DateTimePeriod, timeZone: TimeZone): Instant

289

290

/**

291

* Subtract a DateTimePeriod from an Instant in the specified time zone

292

* @param period Period to subtract

293

* @param timeZone Time zone for calendar calculations

294

* @returns New Instant representing the result

295

*/

296

fun Instant.minus(period: DateTimePeriod, timeZone: TimeZone): Instant

297

298

/**

299

* Add a value in the specified unit to an Instant

300

* @param value Amount to add

301

* @param unit Unit for the arithmetic

302

* @param timeZone Time zone for calendar-based units

303

* @returns New Instant representing the result

304

*/

305

fun Instant.plus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant

306

fun Instant.plus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant

307

308

/**

309

* Subtract a value in the specified unit from an Instant

310

* @param value Amount to subtract

311

* @param unit Unit for the arithmetic

312

* @param timeZone Time zone for calendar-based units

313

* @returns New Instant representing the result

314

*/

315

fun Instant.minus(value: Int, unit: DateTimeUnit, timeZone: TimeZone): Instant

316

fun Instant.minus(value: Long, unit: DateTimeUnit, timeZone: TimeZone): Instant

317

```

318

319

### Difference Calculations

320

321

```kotlin { .api }

322

/**

323

* Calculate the difference between two instants in the specified unit

324

* @param other The other instant

325

* @param unit Unit for the result

326

* @param timeZone Time zone for calendar-based calculations

327

* @returns Difference in the specified unit

328

*/

329

fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long

330

331

/**

332

* Calculate the number of whole days between two instants

333

* @param other The other instant

334

* @param timeZone Time zone for day calculations

335

* @returns Number of days

336

*/

337

fun Instant.daysUntil(other: Instant, timeZone: TimeZone): Int

338

339

/**

340

* Calculate the number of whole months between two instants

341

* @param other The other instant

342

* @param timeZone Time zone for month calculations

343

* @returns Number of months

344

*/

345

fun Instant.monthsUntil(other: Instant, timeZone: TimeZone): Int

346

347

/**

348

* Calculate the number of whole years between two instants

349

* @param other The other instant

350

* @param timeZone Time zone for year calculations

351

* @returns Number of years

352

*/

353

fun Instant.yearsUntil(other: Instant, timeZone: TimeZone): Int

354

355

/**

356

* Calculate the period between two instants

357

* @param other The other instant

358

* @param timeZone Time zone for calculations

359

* @returns DateTimePeriod representing the difference

360

*/

361

fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateTimePeriod

362

```

363

364

**Usage Examples:**

365

366

```kotlin

367

import kotlinx.datetime.*

368

import kotlin.time.Clock

369

370

val now = Clock.System.now()

371

val timeZone = TimeZone.of("Europe/London")

372

373

// Basic arithmetic with periods

374

val period = DateTimePeriod(years = 1, months = 6, days = 15, hours = 12)

375

val future = now.plus(period, timeZone)

376

val past = now.minus(period, timeZone)

377

378

// Arithmetic with units

379

val tomorrow = now.plus(1, DateTimeUnit.DAY, timeZone)

380

val nextHour = now.plus(1, DateTimeUnit.HOUR, timeZone)

381

val nextMonth = now.plus(1, DateTimeUnit.MONTH, timeZone)

382

383

// Calculate differences

384

val startOfYear = LocalDate(2023, 1, 1).atStartOfDayIn(timeZone)

385

val endOfYear = LocalDate(2023, 12, 31).atTime(23, 59, 59).toInstant(timeZone)

386

387

val daysInYear = startOfYear.daysUntil(endOfYear, timeZone)

388

val monthsInYear = startOfYear.monthsUntil(endOfYear, timeZone)

389

val fullPeriod = startOfYear.periodUntil(endOfYear, timeZone)

390

391

println("Days: $daysInYear") // 364

392

println("Months: $monthsInYear") // 11

393

println("Period: $fullPeriod") // P11M30D23H59M59S

394

```

395

396

## LocalDate Arithmetic Operations

397

398

Arithmetic operations specifically for LocalDate values.

399

400

```kotlin { .api }

401

/**

402

* Add a DatePeriod to a LocalDate

403

* @param period Date period to add

404

* @returns New LocalDate representing the result

405

*/

406

fun LocalDate.plus(period: DatePeriod): LocalDate

407

408

/**

409

* Subtract a DatePeriod from a LocalDate

410

* @param period Date period to subtract

411

* @returns New LocalDate representing the result

412

*/

413

fun LocalDate.minus(period: DatePeriod): LocalDate

414

415

/**

416

* Add a value in the specified date-based unit

417

* @param value Amount to add

418

* @param unit Date-based unit (DayBased or MonthBased)

419

* @returns New LocalDate representing the result

420

*/

421

fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate

422

fun LocalDate.plus(value: Long, unit: DateTimeUnit.DateBased): LocalDate

423

424

/**

425

* Subtract a value in the specified date-based unit

426

* @param value Amount to subtract

427

* @param unit Date-based unit (DayBased or MonthBased)

428

* @returns New LocalDate representing the result

429

*/

430

fun LocalDate.minus(value: Int, unit: DateTimeUnit.DateBased): LocalDate

431

fun LocalDate.minus(value: Long, unit: DateTimeUnit.DateBased): LocalDate

432

433

/**

434

* Calculate difference between dates in the specified unit

435

* @param other The other date

436

* @param unit Date-based unit for the result

437

* @returns Difference in the specified unit

438

*/

439

fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased): Long

440

441

/**

442

* Calculate whole days between dates

443

* @param other The other date

444

* @returns Number of days

445

*/

446

fun LocalDate.daysUntil(other: LocalDate): Int

447

448

/**

449

* Calculate whole months between dates

450

* @param other The other date

451

* @returns Number of months

452

*/

453

fun LocalDate.monthsUntil(other: LocalDate): Int

454

455

/**

456

* Calculate whole years between dates

457

* @param other The other date

458

* @returns Number of years

459

*/

460

fun LocalDate.yearsUntil(other: LocalDate): Int

461

462

/**

463

* Calculate the date period between two dates

464

* @param other The other date

465

* @returns DatePeriod representing the difference

466

*/

467

fun LocalDate.periodUntil(other: LocalDate): DatePeriod

468

```

469

470

**Usage Examples:**

471

472

```kotlin

473

import kotlinx.datetime.*

474

475

val startDate = LocalDate(2023, 1, 15)

476

477

// Add/subtract periods

478

val period = DatePeriod(years = 1, months = 6, days = 10)

479

val futureDate = startDate.plus(period) // 2024-07-25

480

val pastDate = startDate.minus(period) // 2021-07-05

481

482

// Add/subtract units

483

val tomorrow = startDate.plus(1, DateTimeUnit.DAY) // 2023-01-16

484

val nextWeek = startDate.plus(1, DateTimeUnit.WEEK) // 2023-01-22

485

val nextMonth = startDate.plus(1, DateTimeUnit.MONTH) // 2023-02-15

486

val nextYear = startDate.plus(1, DateTimeUnit.YEAR) // 2024-01-15

487

488

// Calculate differences

489

val endDate = LocalDate(2024, 7, 25)

490

val daysDiff = startDate.daysUntil(endDate) // 556

491

val monthsDiff = startDate.monthsUntil(endDate) // 18

492

val yearsDiff = startDate.yearsUntil(endDate) // 1

493

val fullPeriod = startDate.periodUntil(endDate) // P1Y6M10D

494

```

495

496

## YearMonth Arithmetic Operations

497

498

Specialized arithmetic for YearMonth values.

499

500

```kotlin { .api }

501

/**

502

* Add months to a YearMonth

503

* @param value Number of months to add

504

* @param unit Must be MonthBased unit

505

* @returns New YearMonth representing the result

506

*/

507

fun YearMonth.plus(value: Int, unit: DateTimeUnit.MonthBased): YearMonth

508

fun YearMonth.plus(value: Long, unit: DateTimeUnit.MonthBased): YearMonth

509

510

/**

511

* Subtract months from a YearMonth

512

* @param value Number of months to subtract

513

* @param unit Must be MonthBased unit

514

* @returns New YearMonth representing the result

515

*/

516

fun YearMonth.minus(value: Int, unit: DateTimeUnit.MonthBased): YearMonth

517

fun YearMonth.minus(value: Long, unit: DateTimeUnit.MonthBased): YearMonth

518

519

/**

520

* Calculate difference in months

521

* @param other The other YearMonth

522

* @param unit MonthBased unit for the result

523

* @returns Difference in the specified unit

524

*/

525

fun YearMonth.until(other: YearMonth, unit: DateTimeUnit.MonthBased): Long

526

527

/**

528

* Calculate whole months between YearMonth values

529

* @param other The other YearMonth

530

* @returns Number of months

531

*/

532

fun YearMonth.monthsUntil(other: YearMonth): Int

533

534

/**

535

* Calculate whole years between YearMonth values

536

* @param other The other YearMonth

537

* @returns Number of years

538

*/

539

fun YearMonth.yearsUntil(other: YearMonth): Int

540

541

// Convenience functions

542

fun YearMonth.plusYear(): YearMonth

543

fun YearMonth.minusYear(): YearMonth

544

fun YearMonth.plusMonth(): YearMonth

545

fun YearMonth.minusMonth(): YearMonth

546

```

547

548

**Usage Examples:**

549

550

```kotlin

551

import kotlinx.datetime.*

552

553

val startMonth = YearMonth(2023, 6) // June 2023

554

555

// Add/subtract using units

556

val nextYear = startMonth.plus(1, DateTimeUnit.YEAR) // 2024-06

557

val nextQuarter = startMonth.plus(1, DateTimeUnit.QUARTER) // 2023-09

558

val nextMonth = startMonth.plus(1, DateTimeUnit.MONTH) // 2023-07

559

560

// Convenience functions

561

val plusYear = startMonth.plusYear() // 2024-06

562

val plusMonth = startMonth.plusMonth() // 2023-07

563

val minusYear = startMonth.minusYear() // 2022-06

564

565

// Calculate differences

566

val endMonth = YearMonth(2025, 12) // December 2025

567

val monthsDiff = startMonth.monthsUntil(endMonth) // 30

568

val yearsDiff = startMonth.yearsUntil(endMonth) // 2

569

```

570

571

## Error Handling

572

573

Arithmetic operations can throw DateTimeArithmeticException in certain scenarios.

574

575

```kotlin { .api }

576

/**

577

* Thrown when datetime arithmetic operations fail

578

* Typically occurs with invalid date calculations

579

*/

580

class DateTimeArithmeticException : RuntimeException {

581

constructor(message: String)

582

constructor(message: String, cause: Throwable?)

583

}

584

```

585

586

**Common Error Scenarios:**

587

588

```kotlin

589

import kotlinx.datetime.*

590

591

try {

592

// This can throw if the result would be invalid

593

val leap = LocalDate(2020, 2, 29) // Feb 29 in leap year

594

val nextYear = leap.plus(1, DateTimeUnit.YEAR) // Would be Feb 29, 2021 (invalid)

595

} catch (e: DateTimeArithmeticException) {

596

println("Arithmetic failed: ${e.message}")

597

}

598

599

// Safe alternatives - check before arithmetic

600

val date = LocalDate(2020, 2, 29)

601

val targetYear = 2021

602

603

// Check if the target date would be valid

604

val wouldBeValid = try {

605

LocalDate(targetYear, date.month, date.day)

606

true

607

} catch (e: Exception) {

608

false

609

}

610

611

if (wouldBeValid) {

612

val result = date.plus(1, DateTimeUnit.YEAR)

613

} else {

614

// Handle invalid date case (e.g., use last day of month)

615

val lastDayOfMonth = YearMonth(targetYear, date.month).lastDay

616

println("Using last day of month instead: $lastDayOfMonth")

617

}

618

```

619

620

## DST-Aware Arithmetic

621

622

When performing arithmetic with Instant values, the choice of time zone affects calendar-based calculations during DST transitions.

623

624

```kotlin

625

import kotlinx.datetime.*

626

627

val timeZone = TimeZone.of("America/New_York")

628

629

// Spring forward example (2023-03-12 02:00 -> 03:00)

630

val beforeSpring = LocalDateTime(2023, 3, 12, 1, 0).toInstant(timeZone)

631

632

// Adding 24 hours vs adding 1 day

633

val plus24Hours = beforeSpring.plus(24, DateTimeUnit.HOUR, timeZone)

634

val plus1Day = beforeSpring.plus(1, DateTimeUnit.DAY, timeZone)

635

636

val local24Hours = plus24Hours.toLocalDateTime(timeZone) // 2:00 AM (due to DST)

637

val local1Day = plus1Day.toLocalDateTime(timeZone) // 1:00 AM (same time next day)

638

639

println("24 hours later: $local24Hours") // 2023-03-13T02:00

640

println("1 day later: $local1Day") // 2023-03-13T01:00

641

642

// Fall back example (2023-11-05 02:00 -> 01:00)

643

val beforeFall = LocalDateTime(2023, 11, 5, 1, 0).toInstant(timeZone)

644

645

val fallPlus24Hours = beforeFall.plus(24, DateTimeUnit.HOUR, timeZone)

646

val fallPlus1Day = beforeFall.plus(1, DateTimeUnit.DAY, timeZone)

647

648

val fallLocal24Hours = fallPlus24Hours.toLocalDateTime(timeZone) // 12:00 AM (due to DST)

649

val fallLocal1Day = fallPlus1Day.toLocalDateTime(timeZone) // 1:00 AM (same time next day)

650

```