src/Pure/General/date.scala
author wenzelm
Wed, 12 Mar 2025 11:39:00 +0100
changeset 82265 4b875a4c83b0
parent 81964 8efec8da78f9
permissions -rw-r--r--
update for release;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
     1
/*  Title:      Pure/General/date.scala
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
81964
8efec8da78f9 more authors;
wenzelm
parents: 81883
diff changeset
     3
    Author:     Fabian Huch, TU München
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
     4
79820
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
     5
Date and time, with timezone.
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
     6
*/
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
     7
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
     8
package isabelle
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
     9
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    10
64096
5edeb60a7ec5 more flexible date formats;
wenzelm
parents: 64094
diff changeset
    11
import java.util.Locale
74943
afd8cb7b2be1 tuned imports;
wenzelm
parents: 74925
diff changeset
    12
import java.time.{Instant, ZonedDateTime, LocalTime, ZoneId}
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    13
import java.time.format.{DateTimeFormatter, DateTimeParseException}
81883
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
    14
import java.time.temporal.{ChronoUnit, TemporalAccessor}
64059
365d367d2b45 more flexibile formatting;
wenzelm
parents: 64057
diff changeset
    15
365d367d2b45 more flexibile formatting;
wenzelm
parents: 64057
diff changeset
    16
import scala.annotation.tailrec
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    17
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    18
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    19
object Date {
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    20
  /* format */
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    21
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    22
  object Format {
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    23
    def make(fmts: List[DateTimeFormatter], tune: String => String = s => s): Format = {
73120
c3589f2dff31 more informative errors: simplify diagnosis of spurious failures reported by users;
wenzelm
parents: 71601
diff changeset
    24
      require(fmts.nonEmpty, "no date formats")
64059
365d367d2b45 more flexibile formatting;
wenzelm
parents: 64057
diff changeset
    25
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    26
      new Format {
64059
365d367d2b45 more flexibile formatting;
wenzelm
parents: 64057
diff changeset
    27
        def apply(date: Date): String = fmts.head.format(date.rep)
365d367d2b45 more flexibile formatting;
wenzelm
parents: 64057
diff changeset
    28
        def parse(str: String): Date =
64098
099518e8af2c misc tuning and clarification;
wenzelm
parents: 64096
diff changeset
    29
          new Date(ZonedDateTime.from(Formatter.try_variants(fmts, tune(str))))
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    30
      }
64059
365d367d2b45 more flexibile formatting;
wenzelm
parents: 64057
diff changeset
    31
    }
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    32
64098
099518e8af2c misc tuning and clarification;
wenzelm
parents: 64096
diff changeset
    33
    def apply(pats: String*): Format =
71601
97ccf48c2f0c misc tuning based on hints by IntelliJ IDEA;
wenzelm
parents: 69980
diff changeset
    34
      make(pats.toList.map(Date.Formatter.pattern))
64060
f3fa0bb3f666 tuned signature;
wenzelm
parents: 64059
diff changeset
    35
64098
099518e8af2c misc tuning and clarification;
wenzelm
parents: 64096
diff changeset
    36
    val default: Format = Format("dd-MMM-uuuu HH:mm:ss xx")
099518e8af2c misc tuning and clarification;
wenzelm
parents: 64096
diff changeset
    37
    val date: Format = Format("dd-MMM-uuuu")
099518e8af2c misc tuning and clarification;
wenzelm
parents: 64096
diff changeset
    38
    val time: Format = Format("HH:mm:ss")
73649
029de1598940 tuned signature;
wenzelm
parents: 73120
diff changeset
    39
    val alt_date: Format = Format("uuuuMMdd")
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    40
  }
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    41
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    42
  abstract class Format private {
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    43
    def apply(date: Date): String
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    44
    def parse(str: String): Date
64098
099518e8af2c misc tuning and clarification;
wenzelm
parents: 64096
diff changeset
    45
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    46
    def unapply(str: String): Option[Date] =
64098
099518e8af2c misc tuning and clarification;
wenzelm
parents: 64096
diff changeset
    47
      try { Some(parse(str)) } catch { case _: DateTimeParseException => None }
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
    48
  }
64057
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
    49
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    50
  object Formatter {
64099
wenzelm
parents: 64098
diff changeset
    51
    def pattern(pat: String): DateTimeFormatter = DateTimeFormatter.ofPattern(pat)
wenzelm
parents: 64098
diff changeset
    52
wenzelm
parents: 64098
diff changeset
    53
    def variants(pats: List[String], locs: List[Locale] = Nil): List[DateTimeFormatter] =
75394
42267c650205 tuned formatting;
wenzelm
parents: 75393
diff changeset
    54
      pats.flatMap { pat =>
64099
wenzelm
parents: 64098
diff changeset
    55
        val fmt = pattern(pat)
71601
97ccf48c2f0c misc tuning based on hints by IntelliJ IDEA;
wenzelm
parents: 69980
diff changeset
    56
        if (locs.isEmpty) List(fmt) else locs.map(fmt.withLocale)
75394
42267c650205 tuned formatting;
wenzelm
parents: 75393
diff changeset
    57
      }
64099
wenzelm
parents: 64098
diff changeset
    58
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    59
    @tailrec def try_variants(
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    60
      fmts: List[DateTimeFormatter],
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    61
      str: String,
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    62
      last_exn: Option[DateTimeParseException] = None
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    63
    ): TemporalAccessor = {
64099
wenzelm
parents: 64098
diff changeset
    64
      fmts match {
wenzelm
parents: 64098
diff changeset
    65
        case Nil =>
wenzelm
parents: 64098
diff changeset
    66
          throw last_exn.getOrElse(new DateTimeParseException("Failed to parse date", str, 0))
wenzelm
parents: 64098
diff changeset
    67
        case fmt :: rest =>
wenzelm
parents: 64098
diff changeset
    68
          try { ZonedDateTime.from(fmt.parse(str)) }
wenzelm
parents: 64098
diff changeset
    69
          catch { case exn: DateTimeParseException => try_variants(rest, str, Some(exn)) }
wenzelm
parents: 64098
diff changeset
    70
      }
wenzelm
parents: 64098
diff changeset
    71
    }
wenzelm
parents: 64098
diff changeset
    72
  }
wenzelm
parents: 64098
diff changeset
    73
64057
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
    74
74925
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    75
  /* ordering */
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    76
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    77
  object Ordering extends scala.math.Ordering[Date] {
74925
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    78
    def compare(date1: Date, date2: Date): Int =
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    79
      date1.instant.compareTo(date2.instant)
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    80
  }
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    81
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
    82
  object Rev_Ordering extends scala.math.Ordering[Date] {
74925
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    83
    def compare(date1: Date, date2: Date): Int =
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    84
      date2.instant.compareTo(date1.instant)
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    85
  }
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    86
4bc306cb2832 more data integrity: name vs. address;
wenzelm
parents: 73649
diff changeset
    87
64057
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
    88
  /* date operations */
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
    89
69980
f2e3adfd916f tuned signature;
wenzelm
parents: 65733
diff changeset
    90
  def timezone_utc: ZoneId = ZoneId.of("UTC")
f2e3adfd916f tuned signature;
wenzelm
parents: 65733
diff changeset
    91
  def timezone_berlin: ZoneId = ZoneId.of("Europe/Berlin")
f2e3adfd916f tuned signature;
wenzelm
parents: 65733
diff changeset
    92
64057
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
    93
  def timezone(): ZoneId = ZoneId.systemDefault
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
    94
79820
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
    95
  def now(timezone: ZoneId = Date.timezone()): Date =
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
    96
    new Date(ZonedDateTime.now(timezone))
64057
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
    97
79820
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
    98
  def instant(t: Instant, timezone: ZoneId = Date.timezone()): Date =
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
    99
    new Date(ZonedDateTime.ofInstant(t, timezone))
65014
97a622d01609 support for type Date;
wenzelm
parents: 64117
diff changeset
   100
79820
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
   101
  def apply(t: Time, timezone: ZoneId = Date.timezone()): Date =
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
   102
    instant(t.instant, timezone)
81883
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   103
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   104
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   105
  /* varying-length calendar cycles */
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   106
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   107
  enum Day { case mon, tue, wed, thu, fri, sat, sun }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   108
  enum Month { case jan, feb, mar, apr, may, jun, jul, aug, sep, okt, nov, dec }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   109
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   110
  sealed trait Cycle {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   111
    def zero(date: Date): Date
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   112
    def next(date: Date): Date
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   113
  }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   114
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   115
  case class Daily(at: Time = Time.zero) extends Cycle {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   116
    require(at >= Time.zero && at < Time.hms(24, 0, 0))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   117
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   118
    def zero(date: Date): Date = date.midnight
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   119
    def next(date: Date): Date = {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   120
      val start = zero(date) + at
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   121
      if (date.time < start.time) start else start.shift(1)
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   122
    }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   123
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   124
    override def toString: String = "Daily(" + Format.time(Date(at, timezone_utc)) + ")"
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   125
  }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   126
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   127
  case class Weekly(on: Day = Day.mon, step: Daily = Daily()) extends Cycle {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   128
    def zero(date: Date): Date = date.shift(1 - date.rep.getDayOfWeek.getValue).midnight
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   129
    def next(date: Date): Date = {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   130
      val next = step.next(zero(date).shift(on.ordinal) - Time.ms(1))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   131
      if (date.time < next.time) next else Date(next.rep.plus(1, ChronoUnit.WEEKS))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   132
    }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   133
  }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   134
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   135
  case class Monthly(nth: Int = 1, step: Daily = Daily()) extends Cycle {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   136
    require(nth > 0 && nth <= 31)
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   137
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   138
    def zero(date: Date): Date = date.shift(1 - date.rep.getDayOfMonth).midnight
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   139
    def next(date: Date): Date = {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   140
      @tailrec def find_next(zero: Date): Date = {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   141
        val next = step.next(zero.shift(nth - 1) - Time.ms(1))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   142
        if (next.rep.getDayOfMonth == nth && date.time < next.time) next
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   143
        else find_next(Date(zero.rep.plus(1, ChronoUnit.MONTHS)))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   144
      }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   145
      find_next(zero(date))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   146
    }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   147
  }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   148
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   149
  case class Yearly(in: Month = Month.jan, step: Monthly = Monthly()) extends Cycle {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   150
    def zero(date: Date): Date = date.shift(1 - date.rep.getDayOfYear).midnight
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   151
    def next(date: Date): Date = {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   152
      @tailrec def find_next(zero: Date): Date = {
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   153
        val next = step.next(Date(zero.rep.plus(in.ordinal, ChronoUnit.MONTHS)) - Time.ms(1))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   154
        if (next.rep.getMonthValue - 1 == in.ordinal && date.time < next.time) next
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   155
        else find_next(Date(zero.rep.plus(1, ChronoUnit.YEARS)))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   156
      }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   157
      find_next(zero(date))
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   158
    }
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   159
  }
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
   160
}
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
   161
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74943
diff changeset
   162
sealed case class Date(rep: ZonedDateTime) {
81883
7ce4a45fe4c2 varying-length calendar cycles, e.g. for ci jobs every week on a certain day/time;
Fabian Huch <huch@in.tum.de>
parents: 79820
diff changeset
   163
  def shift(days: Int): Date = Date(rep.plus(days, ChronoUnit.DAYS))
64117
c2b41b073d8a build_history log files with formal meta info;
wenzelm
parents: 64111
diff changeset
   164
  def midnight: Date =
c2b41b073d8a build_history log files with formal meta info;
wenzelm
parents: 64111
diff changeset
   165
    new Date(ZonedDateTime.of(rep.toLocalDate, LocalTime.MIDNIGHT, rep.getZone))
c2b41b073d8a build_history log files with formal meta info;
wenzelm
parents: 64111
diff changeset
   166
79820
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
   167
  def to(other: ZoneId): Date = new Date(rep.withZoneSameInstant(other))
64111
b2290b9d0175 prefer local timezone;
wenzelm
parents: 64100
diff changeset
   168
65733
wenzelm
parents: 65021
diff changeset
   169
  def unix_epoch: Long = rep.toEpochSecond
77163
7ceed24c88dc alternate AFP tests on lrzcloud2, to fit better into one day;
wenzelm
parents: 75394
diff changeset
   170
  def unix_epoch_day: Long = rep.toLocalDate.toEpochDay
65014
97a622d01609 support for type Date;
wenzelm
parents: 64117
diff changeset
   171
  def instant: Instant = Instant.from(rep)
97a622d01609 support for type Date;
wenzelm
parents: 64117
diff changeset
   172
  def time: Time = Time.instant(instant)
64057
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
   173
  def timezone: ZoneId = rep.getZone
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
   174
79820
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
   175
  def + (t: Time): Date = Date(time + t, timezone = timezone)
e7940d49fe74 clarified signature;
wenzelm
parents: 79817
diff changeset
   176
  def - (t: Time): Date = Date(time - t, timezone = timezone)
79817
7308e402451f more operations for Date and Time;
wenzelm
parents: 77163
diff changeset
   177
  def - (d: Date): Time = time - d.time
7308e402451f more operations for Date and Time;
wenzelm
parents: 77163
diff changeset
   178
64057
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
   179
  def format(fmt: Date.Format = Date.Format.default): String = fmt(this)
fd73e0019605 more operations;
wenzelm
parents: 64056
diff changeset
   180
  override def toString: String = format()
64056
0edc966bee55 more date and time operations from Java 8;
wenzelm
parents:
diff changeset
   181
}