Search code examples
postgresqlscaladoobie

How to read/write Timestamp in Doobie (Postgres)


How to read/write Timestamp in Doobie?

I have a record class that contains a timestamp field. When I am trying to write it to the database or read it using doobie I get an error Cannot find or construct a Read instance for type.

case class ExampleRecord(data: String, created_at: Timestamp)

val create = sql"create table if not exists example_ts (data TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP)".update.run
val insert = Update[ExampleRecord]("insert into example_ts (data, created_at) values (?, ?)")
  .updateMany(List(
    ExampleRecord("one", Timestamp.valueOf(LocalDateTime.now())),
    ExampleRecord("two", Timestamp.valueOf(LocalDateTime.now()))
  ))
val select = sql"select data, created_at from example_ts".query[ExampleRecord].stream

val app = for {
  _ <- create.transact(xa).compile.drain
  _ <- insert.transact(xa).compile.drain
  _ <- select.transact(xa).compile.drain
} yield ()

app.unsafeRunSync()

Solution

  • You need to import doobie.implicits.javasql._ and doobie.implicits.javatime._ release notes. Here is a full app example reading/writing timestamp with doobie.

    // sbt
    // "org.tpolecat" %% "doobie-core"      % "0.8.8",
    // "org.tpolecat" %% "doobie-postgres"  % "0.8.8"
    
    
    import java.sql.Timestamp
    import java.time.LocalDateTime
    
    import doobie._
    import doobie.implicits._
    import doobie.implicits.javasql._
    import doobie.postgres._
    import doobie.postgres.implicits._
    import doobie.postgres.pgisimplicits._
    import cats._
    import cats.implicits._
    import cats.effect._
    import cats.effect.implicits._
    
    case class ExampleRecord(data: String, created_at: Timestamp)
    
    object Example extends IOApp {
      override def run(args: List[String]): IO[ExitCode] = {
        val xa = Transactor.fromDriverManager[IO](
          "org.postgresql.Driver",     // driver classname
          "jdbc:postgresql:example_db",     // connect URL (driver-specific)
          "postgres",                  // user
          ""                          // password
        )
    
        val drop = sql"drop table if exists example_ts".update.run
        val create =
          sql"create table if not exists example_ts (data TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP)".update.run
        val insert = Update[ExampleRecord]("insert into example_ts (data, created_at) values (?, ?)")
          .updateMany(List(
            ExampleRecord("one", Timestamp.valueOf(LocalDateTime.now())),
            ExampleRecord("two", Timestamp.valueOf(LocalDateTime.now()))
          ))
    
        val setup = for {
          _ <- drop.transact(xa)
          _ <- create.transact(xa)
          _ <- insert.transact(xa)
        } yield ()
    
        val select =
          sql"select data, created_at from example_ts".query[ExampleRecord].stream.transact(xa)
    
        val output = select.evalTap { record =>
          IO(println(record))
        }.compile.drain
    
        for {
          _ <- setup
          _ <- output
        } yield ExitCode.Success
      }
    }