All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.digitalasset.daml.lf.data.Time.scala Maven / Gradle / Ivy

There is a newer version: 1.18.2
Show newest version
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.lf.data

import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoField
import java.time.temporal.ChronoUnit.MICROS
import java.time.{Duration, Instant, LocalDate, ZoneId}
import java.util.concurrent.TimeUnit

import scalaz.std.anyVal._
import scalaz.syntax.order._
import scalaz.{Order, Ordering}

import scala.util.Try

object Time {

  case class Date private (days: Int) extends Ordered[Date] {

    override def toString: String =
      Date.formatter.format(LocalDate.ofEpochDay(days.toLong))

    override def compare(that: Date): Int =
      days.compareTo(that.days)
  }

  object Date {

    type T = Date

    private val formatter: DateTimeFormatter =
      DateTimeFormatter.ISO_DATE.withZone(ZoneId.of("Z"))

    private def assertDaysFromString(str: String) = {
      asInt(formatter.parse(str).getLong(ChronoField.EPOCH_DAY)).fold(sys.error, identity)
    }

    private[lf] def asInt(days: Long): Either[String, Int] = {
      val daysI = days.toInt
      if (daysI.toLong == days) Right(daysI)
      else Left(s"out of bound Date $days")
    }

    val MinValue: Date =
      Date(assertDaysFromString("0001-01-01"))

    val MaxValue: Date =
      Date(assertDaysFromString("9999-12-31"))

    val Epoch: Date =
      Date(0)

    def fromDaysSinceEpoch(days: Int): Either[String, Date] =
      if (MinValue.days <= days && days <= MaxValue.days)
        Right(Date(days))
      else
        Left(s"out of bound Date $days")

    def fromString(str: String): Either[String, Date] =
      Try(assertDaysFromString(str)).toEither.left
        .map(_ => s"cannot interpret $str as Date")
        .flatMap(fromDaysSinceEpoch)

    @throws[IllegalArgumentException]
    final def assertFromString(s: String): T =
      assertRight(fromString(s))

    def assertFromDaysSinceEpoch(days: Int): Date =
      assertRight(fromDaysSinceEpoch(days))

    implicit val `Time.Date Order`: Order[Date] = new Order[Date] {
      override def equalIsNatural = true

      override def equal(x: Date, y: Date): Boolean = x == y

      override def order(x: Date, y: Date): Ordering = x.days ?|? y.days
    }

  }

  case class Timestamp private (micros: Long) extends Ordered[Timestamp] {

    override def toString: String =
      Timestamp.formatter.format(toInstant)

    def compare(that: Timestamp): Int =
      micros.compareTo(that.micros)

    def toInstant: Instant = {
      val seconds = TimeUnit.MICROSECONDS.toSeconds(micros)
      val microsOfSecond = micros - TimeUnit.SECONDS.toMicros(seconds)
      Instant.ofEpochSecond(seconds, TimeUnit.MICROSECONDS.toNanos(microsOfSecond))
    }

    @throws[IllegalArgumentException]
    def add(duration: Duration): Timestamp = {
      val secondsInMicros = TimeUnit.SECONDS.toMicros(duration.getSeconds)
      val nanosecondsInMicros = TimeUnit.NANOSECONDS.toMicros(duration.getNano.toLong)
      addMicros(secondsInMicros + nanosecondsInMicros)
    }

    @throws[IllegalArgumentException]
    def addMicros(x: Long): Timestamp =
      Timestamp.assertFromLong(micros + x)
  }

  object Timestamp {

    type T = Timestamp

    private val formatter: DateTimeFormatter =
      DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.of("Z"))

    private def assertMicrosFromInstant(i: Instant): Long =
      TimeUnit.SECONDS.toMicros(i.getEpochSecond) + TimeUnit.NANOSECONDS.toMicros(i.getNano.toLong)

    private def assertMicrosFromString(str: String): Long =
      assertMicrosFromInstant(Instant.parse(str))

    val MinValue: Timestamp =
      Timestamp(assertMicrosFromString("0001-01-01T00:00:00.000000Z"))

    val MaxValue: Timestamp =
      Timestamp(assertMicrosFromString("9999-12-31T23:59:59.999999Z"))

    val Resolution: Duration = Duration.of(1L, MICROS)

    val Epoch: Timestamp =
      Timestamp(0)

    def now(): Timestamp =
      assertFromLong(assertMicrosFromInstant(Instant.now()))

    def fromLong(micros: Long): Either[String, Timestamp] =
      if (MinValue.micros <= micros && micros <= MaxValue.micros)
        Right(Timestamp(micros))
      else
        Left(s"out of bound Timestamp $micros")

    @throws[IllegalArgumentException]
    def assertFromLong(micros: Long): Timestamp =
      assertRight(fromLong(micros))

    def fromString(str: String): Either[String, Timestamp] =
      Try(assertMicrosFromString(str)).toEither.left
        .map(_ => s"cannot interpret $str as Timestamp")
        .flatMap(fromLong)

    @throws[IllegalArgumentException]
    final def assertFromString(s: String): T =
      assertRight(fromString(s))

    def fromInstant(i: Instant): Either[String, Timestamp] =
      Try(assertMicrosFromInstant(i)).toEither.left
        .map(_ => s"cannot interpret $i as Timestamp")
        .flatMap(fromLong)

    def assertFromInstant(i: Instant): Timestamp =
      assertFromLong(assertMicrosFromInstant(i))

    implicit val `Time.Timestamp Order`: Order[Timestamp] = new Order[Timestamp] {
      override def equalIsNatural = true

      override def equal(x: Timestamp, y: Timestamp): Boolean = x == y

      override def order(x: Timestamp, y: Timestamp): Ordering = x.micros ?|? y.micros
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy