From 8d2c032f626fe681c950b7a2762bb723a1a703ed Mon Sep 17 00:00:00 2001 From: Pavel Shirshov Date: Fri, 12 Jan 2024 16:33:22 +0000 Subject: [PATCH 1/3] better time APIs --- .../functional/bio/laws/env/ZIOTestEnv.scala | 8 +- .../scala/izumi/functional/bio/Clock1.scala | 112 +++++++++++++++--- .../platform/time/IzLongParsers.scala | 26 +++- .../platform/time/IzOptionalTimeParsers.scala | 11 -- .../fundamentals/platform/time/IzTime.scala | 4 +- 5 files changed, 125 insertions(+), 36 deletions(-) delete mode 100644 fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzOptionalTimeParsers.scala diff --git a/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala b/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala index ced692be4e..5257c087c6 100644 --- a/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala +++ b/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala @@ -51,9 +51,7 @@ trait ZIOTestEnv extends TestInstances with EqThrowable { implicit def clock2(implicit ticker: Ticker): Clock2[IO] = { new Clock2[IO] { override def epoch: IO[Nothing, Long] = ZIO.succeed(ticker.ctx.now().toMillis) - override def now(accuracy: Clock1.ClockAccuracy): IO[Nothing, ZonedDateTime] = ZIO.succeed( - ZonedDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), ZoneOffset.UTC) - ) + override def now(accuracy: Clock1.ClockAccuracy): IO[Nothing, ZonedDateTime] = nowZoned(accuracy) override def nowLocal(accuracy: Clock1.ClockAccuracy): IO[Nothing, LocalDateTime] = ZIO.succeed( LocalDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), ZoneOffset.UTC) ) @@ -61,6 +59,10 @@ trait ZIOTestEnv extends TestInstances with EqThrowable { OffsetDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), ZoneOffset.UTC) ) override def monotonicNano: IO[Nothing, Long] = ZIO.succeed(ticker.ctx.now().toNanos) + + override def nowZoned(accuracy: Clock1.ClockAccuracy): IO[Nothing, ZonedDateTime] = ZIO.succeed( + ZonedDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), ZoneOffset.UTC) + ) } } diff --git a/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala b/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala index 6e1ba6dab7..6b4efbc61a 100644 --- a/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala +++ b/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala @@ -4,7 +4,7 @@ import izumi.functional.bio.Clock1.ClockAccuracy import izumi.functional.bio.DivergenceHelper.{Divergent, Nondivergent} import izumi.fundamentals.platform.functional.Identity -import java.time.temporal.ChronoUnit +import java.time.temporal.{ChronoUnit, TemporalUnit} import java.time.{LocalDateTime, OffsetDateTime, ZoneId, ZonedDateTime} import scala.annotation.unused import scala.language.implicitConversions @@ -14,9 +14,12 @@ trait Clock1[F[_]] extends DivergenceHelper { def epoch: F[Long] /** Should return current time (UTC timezone) */ - def now(accuracy: ClockAccuracy = ClockAccuracy.DEFAULT): F[ZonedDateTime] - def nowLocal(accuracy: ClockAccuracy = ClockAccuracy.DEFAULT): F[LocalDateTime] - def nowOffset(accuracy: ClockAccuracy = ClockAccuracy.DEFAULT): F[OffsetDateTime] + @deprecated("use nowZoned") + def now(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[ZonedDateTime] + + def nowZoned(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[ZonedDateTime] + def nowLocal(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[LocalDateTime] + def nowOffset(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[OffsetDateTime] /** Should return a never decreasing measure of time, in nanoseconds */ def monotonicNano: F[Long] @@ -39,50 +42,120 @@ object Clock1 extends LowPriorityClockInstances { System.nanoTime() } - override def now(accuracy: ClockAccuracy): ZonedDateTime = { + override def nowZoned(accuracy: ClockAccuracy): ZonedDateTime = { ClockAccuracy.applyAccuracy(ZonedDateTime.now(TZ_UTC), accuracy) } + @deprecated("use nowZoned") + override def now(accuracy: ClockAccuracy): ZonedDateTime = { + nowZoned(accuracy) + } + override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = { - now(accuracy).toLocalDateTime + // do not reuse nowZoned because of sjs + ClockAccuracy.applyAccuracy(LocalDateTime.now(TZ_UTC), accuracy) } override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = { - now(accuracy).toOffsetDateTime + // do not reuse nowZoned because of sjs + ClockAccuracy.applyAccuracy(OffsetDateTime.now(TZ_UTC), accuracy) } private[this] final val TZ_UTC: ZoneId = ZoneId.of("UTC") } + @deprecated("Use ConstantZoned/ConstantOffset") final class Constant(time: ZonedDateTime, nano: Long) extends Clock1[Identity] { + private val underlying = new ConstantZoned(time, nano) + + override def epoch: Long = underlying.epoch + @deprecated("use nowZoned") + override def now(accuracy: ClockAccuracy): ZonedDateTime = underlying.now(accuracy) + + override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = underlying.nowLocal(accuracy) + override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = underlying.nowOffset(accuracy) + override def nowZoned(accuracy: ClockAccuracy): Identity[ZonedDateTime] = underlying.nowZoned(accuracy) + override def monotonicNano: Long = nano + } + + final class ConstantZoned(time: ZonedDateTime, nano: Long) extends Clock1[Identity] { override def epoch: Long = time.toEpochSecond - override def now(accuracy: ClockAccuracy): ZonedDateTime = ClockAccuracy.applyAccuracy(time, accuracy) - override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = now(accuracy).toLocalDateTime - override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = now(accuracy).toOffsetDateTime + @deprecated("use nowZoned") + override def now(accuracy: ClockAccuracy): ZonedDateTime = nowZoned(accuracy) + + override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = ClockAccuracy.applyAccuracy(time.toLocalDateTime, accuracy) + override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = ClockAccuracy.applyAccuracy(time.toOffsetDateTime, accuracy) + override def nowZoned(accuracy: ClockAccuracy): Identity[ZonedDateTime] = ClockAccuracy.applyAccuracy(time, accuracy) + override def monotonicNano: Long = nano + } + + final class ConstantOffset(time: OffsetDateTime, nano: Long) extends Clock1[Identity] { + override def epoch: Long = time.toEpochSecond + @deprecated("use nowZoned") + override def now(accuracy: ClockAccuracy): ZonedDateTime = nowZoned(accuracy) + + override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = ClockAccuracy.applyAccuracy(time.toLocalDateTime, accuracy) + override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = ClockAccuracy.applyAccuracy(time, accuracy) + override def nowZoned(accuracy: ClockAccuracy): Identity[ZonedDateTime] = ClockAccuracy.applyAccuracy(time.toZonedDateTime, accuracy) override def monotonicNano: Long = nano } sealed trait ClockAccuracy object ClockAccuracy { + @deprecated("Use ClockAccuracy.RAW (but better set the limit explicitly!)") case object DEFAULT extends ClockAccuracy + + /** The accuracy will not be changed. Generally speaking, it's a bad idea because the actual accuracy might differ between JVM versions (e.g. 11 vs 17) + */ + case object RAW extends ClockAccuracy + + /** Highest precision, although it might be unsafe to use it because some tools (e.g. PostgreSQL) may be implicitly truncating nanosecond-precision timestamps. + */ case object NANO extends ClockAccuracy case object MILLIS extends ClockAccuracy + + /** + * This is the safest choice for PostgreSQL which is known to truncate timestamps to microseconds + */ case object MICROS extends ClockAccuracy case object SECONDS extends ClockAccuracy case object MINUTES extends ClockAccuracy case object HOURS extends ClockAccuracy - def applyAccuracy(now: ZonedDateTime, clockAccuracy: ClockAccuracy): ZonedDateTime = { + private trait TruncatableTime[T] { + def truncatedTo(timestamp: T, unit: TemporalUnit): T + } + private object TruncatableTime { + implicit object TruncatableZoned extends TruncatableTime[ZonedDateTime] { + override def truncatedTo(timestamp: ZonedDateTime, unit: TemporalUnit): ZonedDateTime = timestamp.truncatedTo(unit) + } + implicit object TruncatableOffset extends TruncatableTime[OffsetDateTime] { + override def truncatedTo(timestamp: OffsetDateTime, unit: TemporalUnit): OffsetDateTime = timestamp.truncatedTo(unit) + } + implicit object TruncatableLocal extends TruncatableTime[LocalDateTime] { + override def truncatedTo(timestamp: LocalDateTime, unit: TemporalUnit): LocalDateTime = timestamp.truncatedTo(unit) + } + } + + private def applyTTAccuracy[T: TruncatableTime](now: T, clockAccuracy: ClockAccuracy): T = { + val tt = implicitly[TruncatableTime[T]] clockAccuracy match { case ClockAccuracy.DEFAULT => now - case ClockAccuracy.NANO => now.truncatedTo(ChronoUnit.NANOS) - case ClockAccuracy.MILLIS => now.truncatedTo(ChronoUnit.MILLIS) - case ClockAccuracy.MICROS => now.truncatedTo(ChronoUnit.MICROS) - case ClockAccuracy.SECONDS => now.truncatedTo(ChronoUnit.SECONDS) - case ClockAccuracy.MINUTES => now.truncatedTo(ChronoUnit.MINUTES) - case ClockAccuracy.HOURS => now.truncatedTo(ChronoUnit.HOURS) + case ClockAccuracy.RAW => now + case ClockAccuracy.NANO => tt.truncatedTo(now, ChronoUnit.NANOS) + case ClockAccuracy.MILLIS => tt.truncatedTo(now, ChronoUnit.MILLIS) + case ClockAccuracy.MICROS => tt.truncatedTo(now, ChronoUnit.MICROS) + case ClockAccuracy.SECONDS => tt.truncatedTo(now, ChronoUnit.SECONDS) + case ClockAccuracy.MINUTES => tt.truncatedTo(now, ChronoUnit.MINUTES) + case ClockAccuracy.HOURS => tt.truncatedTo(now, ChronoUnit.HOURS) } } + + def applyAccuracy(now: ZonedDateTime, clockAccuracy: ClockAccuracy): ZonedDateTime = applyTTAccuracy(now, clockAccuracy) + + def applyAccuracy(now: OffsetDateTime, clockAccuracy: ClockAccuracy): OffsetDateTime = applyTTAccuracy(now, clockAccuracy) + + def applyAccuracy(now: LocalDateTime, clockAccuracy: ClockAccuracy): LocalDateTime = applyTTAccuracy(now, clockAccuracy) } @inline implicit final def impureClock: Clock1[Identity] = Standard @@ -118,10 +191,13 @@ sealed trait LowPriorityClockInstances { @inline implicit final def fromImpureClock[F[_]](implicit impureClock: Clock1[Identity], F: SyncSafe1[F]): Clock1[F] = { new Clock1[F] { override val epoch: F[Long] = F.syncSafe(impureClock.epoch) - override def now(accuracy: ClockAccuracy): F[ZonedDateTime] = F.syncSafe(impureClock.now(accuracy)) + @deprecated("use nowZoned") + override def now(accuracy: ClockAccuracy): F[ZonedDateTime] = nowZoned(accuracy) override def nowLocal(accuracy: ClockAccuracy): F[LocalDateTime] = F.syncSafe(impureClock.nowLocal(accuracy)) override def nowOffset(accuracy: ClockAccuracy): F[OffsetDateTime] = F.syncSafe(impureClock.nowOffset(accuracy)) + override def nowZoned(accuracy: ClockAccuracy): F[ZonedDateTime] = F.syncSafe(impureClock.nowZoned(accuracy)) override val monotonicNano: F[Long] = F.syncSafe(impureClock.monotonicNano) + } } diff --git a/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzLongParsers.scala b/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzLongParsers.scala index d3f17aad5f..af3e7df100 100644 --- a/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzLongParsers.scala +++ b/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzLongParsers.scala @@ -1,7 +1,6 @@ package izumi.fundamentals.platform.time -import java.time.{Instant, LocalDateTime, ZoneId, ZonedDateTime} - +import java.time.{Instant, LocalDateTime, OffsetDateTime, ZoneId, ZonedDateTime} import izumi.fundamentals.platform.time.IzTime.TZ_UTC final class IzLongParsers(private val t: Long) extends AnyVal { @@ -15,14 +14,35 @@ final class IzLongParsers(private val t: Long) extends AnyVal { LocalDateTime.ofInstant(instant, ZoneId.systemDefault()) } + def asEpochSecondUtcZoned: ZonedDateTime = { + val instant = Instant.ofEpochSecond(t) + ZonedDateTime.ofInstant(instant, TZ_UTC) + } + + def asEpochMillisUtcZoned: ZonedDateTime = { + val instant = Instant.ofEpochMilli(t) + ZonedDateTime.ofInstant(instant, TZ_UTC) + } + + def asEpochSecondsUtcOffset: OffsetDateTime = { + val instant = Instant.ofEpochSecond(t) + OffsetDateTime.ofInstant(instant, TZ_UTC) + } + + def asEpochMillisUtcOffset: OffsetDateTime = { + val instant = Instant.ofEpochMilli(t) + OffsetDateTime.ofInstant(instant, TZ_UTC) + } + + @deprecated("use asEpochSecondZonedsUtc") def asEpochSecondsUtc: ZonedDateTime = { val instant = Instant.ofEpochSecond(t) ZonedDateTime.ofInstant(instant, TZ_UTC) } + @deprecated("use asEpochMillisZonedUtc") def asEpochMillisUtc: ZonedDateTime = { val instant = Instant.ofEpochMilli(t) ZonedDateTime.ofInstant(instant, TZ_UTC) } - } diff --git a/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzOptionalTimeParsers.scala b/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzOptionalTimeParsers.scala deleted file mode 100644 index 72087c22f3..0000000000 --- a/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzOptionalTimeParsers.scala +++ /dev/null @@ -1,11 +0,0 @@ -package izumi.fundamentals.platform.time - -import java.time.ZonedDateTime -import java.time.temporal.TemporalAccessor - -final class IzOptionalTimeParsers(private val value: Option[String]) extends AnyVal { - import IzTime._ - def mapToTemporal: Option[TemporalAccessor] = value.map(_.toTemporal) - - def mapToTsZ: Option[ZonedDateTime] = value.map(_.toTsZ) -} diff --git a/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzTime.scala b/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzTime.scala index 05ca853840..89cbad21d6 100644 --- a/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzTime.scala +++ b/fundamentals/fundamentals-platform/src/main/scala/izumi/fundamentals/platform/time/IzTime.scala @@ -87,13 +87,15 @@ trait IzTime extends IzTimeSafe { // parsers @inline implicit final def toRichLong(value: Long): IzLongParsers = new IzLongParsers(value) @inline implicit final def stringToParseableTime(value: String): IzTimeParsers = new IzTimeParsers(value) - @inline implicit final def maybeStringToParseableTime(value: Option[String]): IzOptionalTimeParsers = new IzOptionalTimeParsers(value) // current time + @deprecated("use Clock1.Standard.now") def utcNow: ZonedDateTime = ZonedDateTime.now(TZ_UTC) + @deprecated("use Clock1.Standard.nowOffset") def utcNowOffset: OffsetDateTime = OffsetDateTime.now(TZ_UTC) + @deprecated("use Clock1.Standard.utcNow.isoFormat") def isoNow: String = utcNow.isoFormat final lazy val ISO_ZONED_DATE_TIME_3NANO: DateTimeFormatter = { From 81a92c52c8d92b5c415327ea8466cb945583bc4b Mon Sep 17 00:00:00 2001 From: Pavel Shirshov Date: Fri, 12 Jan 2024 16:39:53 +0000 Subject: [PATCH 2/3] better time APIs: zone parameter for all the Clock1 methods --- .../functional/bio/laws/env/ZIOTestEnv.scala | 12 ++--- .../scala/izumi/functional/bio/Clock1.scala | 45 ++++++++++--------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala b/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala index 5257c087c6..27c5b01e8c 100644 --- a/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala +++ b/fundamentals/fundamentals-bio/.jvm/src/test/scala/izumi/functional/bio/laws/env/ZIOTestEnv.scala @@ -52,16 +52,16 @@ trait ZIOTestEnv extends TestInstances with EqThrowable { new Clock2[IO] { override def epoch: IO[Nothing, Long] = ZIO.succeed(ticker.ctx.now().toMillis) override def now(accuracy: Clock1.ClockAccuracy): IO[Nothing, ZonedDateTime] = nowZoned(accuracy) - override def nowLocal(accuracy: Clock1.ClockAccuracy): IO[Nothing, LocalDateTime] = ZIO.succeed( - LocalDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), ZoneOffset.UTC) + override def nowLocal(accuracy: Clock1.ClockAccuracy, zone: ZoneId): IO[Nothing, LocalDateTime] = ZIO.succeed( + LocalDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), zone) ) - override def nowOffset(accuracy: Clock1.ClockAccuracy): IO[Nothing, OffsetDateTime] = ZIO.succeed( - OffsetDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), ZoneOffset.UTC) + override def nowOffset(accuracy: Clock1.ClockAccuracy, zone: ZoneId): IO[Nothing, OffsetDateTime] = ZIO.succeed( + OffsetDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), zone) ) override def monotonicNano: IO[Nothing, Long] = ZIO.succeed(ticker.ctx.now().toNanos) - override def nowZoned(accuracy: Clock1.ClockAccuracy): IO[Nothing, ZonedDateTime] = ZIO.succeed( - ZonedDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), ZoneOffset.UTC) + override def nowZoned(accuracy: Clock1.ClockAccuracy, zone: ZoneId): IO[Nothing, ZonedDateTime] = ZIO.succeed( + ZonedDateTime.ofInstant(Instant.ofEpochMilli(ticker.ctx.now().toMillis), zone) ) } } diff --git a/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala b/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala index 6b4efbc61a..ee861a4449 100644 --- a/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala +++ b/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala @@ -17,9 +17,9 @@ trait Clock1[F[_]] extends DivergenceHelper { @deprecated("use nowZoned") def now(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[ZonedDateTime] - def nowZoned(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[ZonedDateTime] - def nowLocal(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[LocalDateTime] - def nowOffset(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[OffsetDateTime] + def nowZoned(accuracy: ClockAccuracy = ClockAccuracy.RAW, zone: ZoneId = Clock1.TZ_UTC): F[ZonedDateTime] + def nowLocal(accuracy: ClockAccuracy = ClockAccuracy.RAW, zone: ZoneId = Clock1.TZ_UTC): F[LocalDateTime] + def nowOffset(accuracy: ClockAccuracy = ClockAccuracy.RAW, zone: ZoneId = Clock1.TZ_UTC): F[OffsetDateTime] /** Should return a never decreasing measure of time, in nanoseconds */ def monotonicNano: F[Long] @@ -28,6 +28,8 @@ trait Clock1[F[_]] extends DivergenceHelper { } object Clock1 extends LowPriorityClockInstances { + final val TZ_UTC: ZoneId = ZoneId.of("UTC") + def apply[F[_]: Clock1]: Clock1[F] = implicitly def fromImpure[F[_]: SyncSafe1](impureClock: Clock1[Identity]): Clock1[F] = fromImpureClock(impureClock, SyncSafe1[F]) @@ -42,8 +44,8 @@ object Clock1 extends LowPriorityClockInstances { System.nanoTime() } - override def nowZoned(accuracy: ClockAccuracy): ZonedDateTime = { - ClockAccuracy.applyAccuracy(ZonedDateTime.now(TZ_UTC), accuracy) + override def nowZoned(accuracy: ClockAccuracy, zone: ZoneId): ZonedDateTime = { + ClockAccuracy.applyAccuracy(ZonedDateTime.now(zone), accuracy) } @deprecated("use nowZoned") @@ -51,17 +53,16 @@ object Clock1 extends LowPriorityClockInstances { nowZoned(accuracy) } - override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = { + override def nowLocal(accuracy: ClockAccuracy, zone: ZoneId): LocalDateTime = { // do not reuse nowZoned because of sjs - ClockAccuracy.applyAccuracy(LocalDateTime.now(TZ_UTC), accuracy) + ClockAccuracy.applyAccuracy(LocalDateTime.now(zone), accuracy) } - override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = { + override def nowOffset(accuracy: ClockAccuracy, zone: ZoneId): OffsetDateTime = { // do not reuse nowZoned because of sjs - ClockAccuracy.applyAccuracy(OffsetDateTime.now(TZ_UTC), accuracy) + ClockAccuracy.applyAccuracy(OffsetDateTime.now(zone), accuracy) } - private[this] final val TZ_UTC: ZoneId = ZoneId.of("UTC") } @deprecated("Use ConstantZoned/ConstantOffset") @@ -72,9 +73,9 @@ object Clock1 extends LowPriorityClockInstances { @deprecated("use nowZoned") override def now(accuracy: ClockAccuracy): ZonedDateTime = underlying.now(accuracy) - override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = underlying.nowLocal(accuracy) - override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = underlying.nowOffset(accuracy) - override def nowZoned(accuracy: ClockAccuracy): Identity[ZonedDateTime] = underlying.nowZoned(accuracy) + override def nowLocal(accuracy: ClockAccuracy, zone: ZoneId): LocalDateTime = underlying.nowLocal(accuracy, zone) + override def nowOffset(accuracy: ClockAccuracy, zone: ZoneId): OffsetDateTime = underlying.nowOffset(accuracy, zone) + override def nowZoned(accuracy: ClockAccuracy, zone: ZoneId): Identity[ZonedDateTime] = underlying.nowZoned(accuracy, zone) override def monotonicNano: Long = nano } @@ -83,9 +84,9 @@ object Clock1 extends LowPriorityClockInstances { @deprecated("use nowZoned") override def now(accuracy: ClockAccuracy): ZonedDateTime = nowZoned(accuracy) - override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = ClockAccuracy.applyAccuracy(time.toLocalDateTime, accuracy) - override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = ClockAccuracy.applyAccuracy(time.toOffsetDateTime, accuracy) - override def nowZoned(accuracy: ClockAccuracy): Identity[ZonedDateTime] = ClockAccuracy.applyAccuracy(time, accuracy) + override def nowLocal(accuracy: ClockAccuracy, zone: ZoneId): LocalDateTime = ClockAccuracy.applyAccuracy(time.toLocalDateTime, accuracy) + override def nowOffset(accuracy: ClockAccuracy, zone: ZoneId): OffsetDateTime = ClockAccuracy.applyAccuracy(time.toOffsetDateTime, accuracy) + override def nowZoned(accuracy: ClockAccuracy, zone: ZoneId): Identity[ZonedDateTime] = ClockAccuracy.applyAccuracy(time, accuracy) override def monotonicNano: Long = nano } @@ -94,9 +95,9 @@ object Clock1 extends LowPriorityClockInstances { @deprecated("use nowZoned") override def now(accuracy: ClockAccuracy): ZonedDateTime = nowZoned(accuracy) - override def nowLocal(accuracy: ClockAccuracy): LocalDateTime = ClockAccuracy.applyAccuracy(time.toLocalDateTime, accuracy) - override def nowOffset(accuracy: ClockAccuracy): OffsetDateTime = ClockAccuracy.applyAccuracy(time, accuracy) - override def nowZoned(accuracy: ClockAccuracy): Identity[ZonedDateTime] = ClockAccuracy.applyAccuracy(time.toZonedDateTime, accuracy) + override def nowLocal(accuracy: ClockAccuracy, zone: ZoneId): LocalDateTime = ClockAccuracy.applyAccuracy(time.toLocalDateTime, accuracy) + override def nowOffset(accuracy: ClockAccuracy, zone: ZoneId): OffsetDateTime = ClockAccuracy.applyAccuracy(time, accuracy) + override def nowZoned(accuracy: ClockAccuracy, zone: ZoneId): Identity[ZonedDateTime] = ClockAccuracy.applyAccuracy(time.toZonedDateTime, accuracy) override def monotonicNano: Long = nano } @@ -193,9 +194,9 @@ sealed trait LowPriorityClockInstances { override val epoch: F[Long] = F.syncSafe(impureClock.epoch) @deprecated("use nowZoned") override def now(accuracy: ClockAccuracy): F[ZonedDateTime] = nowZoned(accuracy) - override def nowLocal(accuracy: ClockAccuracy): F[LocalDateTime] = F.syncSafe(impureClock.nowLocal(accuracy)) - override def nowOffset(accuracy: ClockAccuracy): F[OffsetDateTime] = F.syncSafe(impureClock.nowOffset(accuracy)) - override def nowZoned(accuracy: ClockAccuracy): F[ZonedDateTime] = F.syncSafe(impureClock.nowZoned(accuracy)) + override def nowLocal(accuracy: ClockAccuracy, zone: ZoneId): F[LocalDateTime] = F.syncSafe(impureClock.nowLocal(accuracy, zone)) + override def nowOffset(accuracy: ClockAccuracy, zone: ZoneId): F[OffsetDateTime] = F.syncSafe(impureClock.nowOffset(accuracy, zone)) + override def nowZoned(accuracy: ClockAccuracy, zone: ZoneId): F[ZonedDateTime] = F.syncSafe(impureClock.nowZoned(accuracy, zone)) override val monotonicNano: F[Long] = F.syncSafe(impureClock.monotonicNano) } From d0f9329867a19ac3d2a87f909b3542cdeac8a0fd Mon Sep 17 00:00:00 2001 From: Pavel Shirshov Date: Wed, 17 Jan 2024 15:54:05 +0000 Subject: [PATCH 3/3] cleanups --- .../scala/izumi/functional/bio/Clock1.scala | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala b/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala index ee861a4449..70a6b4b657 100644 --- a/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala +++ b/fundamentals/fundamentals-bio/src/main/scala/izumi/functional/bio/Clock1.scala @@ -16,10 +16,12 @@ trait Clock1[F[_]] extends DivergenceHelper { /** Should return current time (UTC timezone) */ @deprecated("use nowZoned") def now(accuracy: ClockAccuracy = ClockAccuracy.RAW): F[ZonedDateTime] + private[izumi] def nowLocal(accuracy: ClockAccuracy): F[LocalDateTime] = nowLocal(accuracy, Clock1.TZ_UTC) + private[izumi] def nowOffset(accuracy: ClockAccuracy): F[OffsetDateTime] = nowOffset(accuracy, Clock1.TZ_UTC) - def nowZoned(accuracy: ClockAccuracy = ClockAccuracy.RAW, zone: ZoneId = Clock1.TZ_UTC): F[ZonedDateTime] - def nowLocal(accuracy: ClockAccuracy = ClockAccuracy.RAW, zone: ZoneId = Clock1.TZ_UTC): F[LocalDateTime] - def nowOffset(accuracy: ClockAccuracy = ClockAccuracy.RAW, zone: ZoneId = Clock1.TZ_UTC): F[OffsetDateTime] + def nowZoned(accuracy: ClockAccuracy = ClockAccuracy.MICROS, zone: ZoneId = Clock1.TZ_UTC): F[ZonedDateTime] + def nowLocal(accuracy: ClockAccuracy = ClockAccuracy.MICROS, zone: ZoneId = Clock1.TZ_UTC): F[LocalDateTime] + def nowOffset(accuracy: ClockAccuracy = ClockAccuracy.MICROS, zone: ZoneId = Clock1.TZ_UTC): F[OffsetDateTime] /** Should return a never decreasing measure of time, in nanoseconds */ def monotonicNano: F[Long] @@ -104,7 +106,7 @@ object Clock1 extends LowPriorityClockInstances { sealed trait ClockAccuracy object ClockAccuracy { @deprecated("Use ClockAccuracy.RAW (but better set the limit explicitly!)") - case object DEFAULT extends ClockAccuracy + private[izumi] case object DEFAULT extends ClockAccuracy /** The accuracy will not be changed. Generally speaking, it's a bad idea because the actual accuracy might differ between JVM versions (e.g. 11 vs 17) */ @@ -127,15 +129,9 @@ object Clock1 extends LowPriorityClockInstances { def truncatedTo(timestamp: T, unit: TemporalUnit): T } private object TruncatableTime { - implicit object TruncatableZoned extends TruncatableTime[ZonedDateTime] { - override def truncatedTo(timestamp: ZonedDateTime, unit: TemporalUnit): ZonedDateTime = timestamp.truncatedTo(unit) - } - implicit object TruncatableOffset extends TruncatableTime[OffsetDateTime] { - override def truncatedTo(timestamp: OffsetDateTime, unit: TemporalUnit): OffsetDateTime = timestamp.truncatedTo(unit) - } - implicit object TruncatableLocal extends TruncatableTime[LocalDateTime] { - override def truncatedTo(timestamp: LocalDateTime, unit: TemporalUnit): LocalDateTime = timestamp.truncatedTo(unit) - } + implicit lazy val TruncatableZoned: TruncatableTime[ZonedDateTime] = (timestamp: ZonedDateTime, unit: TemporalUnit) => timestamp.truncatedTo(unit) + implicit lazy val TruncatableOffset: TruncatableTime[OffsetDateTime] = (timestamp: OffsetDateTime, unit: TemporalUnit) => timestamp.truncatedTo(unit) + implicit lazy val TruncatableLocal: TruncatableTime[LocalDateTime] = (timestamp: LocalDateTime, unit: TemporalUnit) => timestamp.truncatedTo(unit) } private def applyTTAccuracy[T: TruncatableTime](now: T, clockAccuracy: ClockAccuracy): T = {