Skip to content

Patch.Temporal for ZonedDateTimeType casts to LocalDate (ClassCastException) #1098

Description

@haskiindahouse

Patch.Temporal.patch has a ZonedDateTimeType case that's a copy-paste of the LocalDateType case — it casts the value to LocalDate and rebuilds via LocalDate.ofEpochDay. Applying such a patch to a ZonedDateTime throws ClassCastException.

Differ.zonedDateTime produces Patch.ZonedDateTime (a separate case class), not Patch.Temporal, so this branch isn't hit by the standard diff path. It's reachable through direct construction of Patch.Temporal(_, StandardType.ZonedDateTimeType) and through deserialisation of patch data.

Reproducer (scala-cli):

//> using scala 3.8.3
//> using dep dev.zio::zio-schema::1.8.5

import zio.schema.*
import java.time.*

@main def repro(): Unit =
  val patch = Patch.Temporal(List(1L), StandardType.ZonedDateTimeType)
  try
    println(patch.patch(ZonedDateTime.now()))
  catch
    case e: ClassCastException => println(s"ClassCastException: ${e.getMessage}")

  // for contrast: the LocalDateType branch is fine
  val ok = Patch.Temporal(List(1L), StandardType.LocalDateType)
  println(ok.patch(LocalDate.now()))

Output:

ClassCastException: class java.time.ZonedDateTime cannot be cast to class java.time.LocalDate
Right(...)   <- LocalDate branch works

Source:

case (_: StandardType.ZonedDateTimeType.type, distance :: Nil) =>
Right(LocalDate.ofEpochDay(a.asInstanceOf[LocalDate].toEpochDay - distance).asInstanceOf[A])

case (_: StandardType.ZonedDateTimeType.type, distance :: Nil) =>
  Right(LocalDate.ofEpochDay(a.asInstanceOf[LocalDate].toEpochDay - distance).asInstanceOf[A])

The neighbouring LocalDateType case (around line 178) is the obvious source of the copy.

Versions. zio-schema 1.8.5, Scala 3.8.3.

Suggested fix. Either delete the Patch.Temporal ZonedDateTimeType branch (the proper Patch.ZonedDateTime case class handles it correctly), or implement it using Duration arithmetic on ZonedDateTime. Happy to PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions