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

lucuma.core.syntax.Time.scala Maven / Gradle / Ivy

There is a newer version: 0.108.0
Show newest version
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.core.syntax

import cats.syntax.all.*
import lucuma.core.math.BoundedInterval
import org.typelevel.cats.time.*
import spire.math.extras.interval.IntervalSeq

import java.time.Duration
import java.time.Instant
import java.time.LocalTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAmount

// A bit of syntax to make working with Java Instant and Duration a bit less
// cumbersome / easier to read.

trait InstantOps:
  extension (self: Instant)
    def +(t: TemporalAmount): Instant =
      self.plus(t)

    def -(t: TemporalAmount): Instant =
      self.minus(t)

object instant extends InstantOps

trait ZonedDateTimeOps:
  extension (self: ZonedDateTime)
    def +(t: TemporalAmount): ZonedDateTime =
      self.plus(t)

    def -(t: TemporalAmount): ZonedDateTime =
      self.minus(t)

object zonedDateTime extends ZonedDateTimeOps

trait DurationOps:
  extension (self: Duration)
    def +(that: Duration): Duration =
      self.plus(that)

    def -(that: Duration): Duration =
      self.minus(that)

    def *(m: Long): Duration =
      self.multipliedBy(m)

    def /(d: Long): Duration =
      self.dividedBy(d)

    def toMicros: Long = self.toNanos / 1000

object duration extends DurationOps

trait  LongDurationOps:
  extension( self: Long)
    def nanoseconds: Duration =
      Duration.ofNanos(self)

    def microseconds: Duration =
      Duration.ofNanos(self * 1000)

    def milliseconds: Duration =
      Duration.ofMillis(self)

    def seconds: Duration =
      Duration.ofSeconds(self)

    def minutes: Duration =
      Duration.ofMinutes(self)

object longDuration extends LongDurationOps

trait InstantBoundedIntervalOps:
  extension (self: BoundedInterval[Instant])
    /**
     * Convert to the minimal full-day interval that includes this interval.
     *
     * Hours in interval may not be a multiple of 24 if there's a DST transition in the resulting
     * interval.
     *
     * @param zone
     *   the timezone where the start of the day should be computed
     * @param startOfDay
     *   time at which the day starts
     */
    def toFullDays(zone: ZoneId, startOfDay: LocalTime): BoundedInterval[Instant] = {
      val fullDayStart = {
        val startAtZone = self.lower.atZone(zone)
        val newStart    = startAtZone.`with`(startOfDay)
        if (newStart <= startAtZone) newStart.toInstant
        else newStart.minusDays(1).`with`(startOfDay).toInstant
      }

      val fullDayEnd = {
        val endAtZone = self.upper.atZone(zone)
        val newEnd    = endAtZone.`with`(startOfDay)
        if (newEnd >= endAtZone) newEnd.toInstant
        else newEnd.plusDays(1).`with`(startOfDay).toInstant
      }

      BoundedInterval.unsafeOpenUpper(fullDayStart, fullDayEnd)
    }

    def duration: Duration = Duration.between(self.lower, self.upper)

object instantBoundedInterval extends InstantBoundedIntervalOps

trait InstantIntervalSeqOps extends InstantBoundedIntervalOps:

  extension (self: IntervalSeq[Instant])
    def duration: Duration =
      self.intervals
        .foldLeft(Duration.ZERO)((d, i) =>
          d.plus(
            BoundedInterval.fromInterval(i).map(_.duration).getOrElse(ChronoUnit.FOREVER.getDuration)
          )
        )

object instantInterval extends InstantIntervalSeqOps

trait TimeOps
    extends InstantOps
    with DurationOps
    with LongDurationOps
    with ZonedDateTimeOps
    with InstantIntervalSeqOps

object time extends TimeOps




© 2015 - 2024 Weber Informatics LLC | Privacy Policy