2

I am looking for a clean solution to work with datetime in Play framework. Specifically I need to persist datetime in a database, convert that datetime to a Scala object, and convert that datetime to and from Json.

For example I am making a bidding service like eBay, so I want to persist the time an item is created, and when the bidding time is over. I have a simple model like this:

case class Item(name: String, description: String, start: DateTime, end: DateTime)

Since Slick does not support Joda DateTime, I use a MappedColumnType to convert between java.sql.Timestamp, and Joda DateTime. This is trivial, although I would appreciate if there is a better solution.

The reason why I use Joda DateTime instead of java.sql.Timestamp is because Play does now know how to write Timestamp to Json, and all the solutions I can find online require a lot of patching code.

With Joda DateTime in place, I hit another issue. Play writes DateTime as a Long number, which is fine, but a full format date is preferred. The problem is that the client can only submit the DateTime as "yyyy-MM-dd" without time and timezone.

Here is my attempt so far:

 implicit val itemFormatter = (
    (__ \ "ean").format[Long] and
      (__ \ "description").format[String] and
      (__ \ "start").format[DateTime] and
      (__ \ "end").format[DateTime]
    )(Item.apply _, unlift(Item.unapply))

  val userDateFormatter = "dd/MM/yyyy HH:mm:ss"
  implicit val jodaDateTimeReads = Reads.jodaDateReads(userDateFormatter)
  implicit val jodaDateTimeWrites = Writes.jodaDateWrites(userDateFormatter)
  implicit val jodaDateTimeFormats = Format(jodaDateTimeReads, jodaDateTimeWrites)

The error message I receive when trying to post time with the format as above is:

error.expected.jodadate.format,WrappedArray(yyyy-MM-dd)

This seems to be a trivial thing in other langs/frameworks such as Ruby/Rails, but I feel like date and time support is seriously lacking in Scala.

Khanetor
  • 11,595
  • 8
  • 40
  • 76

1 Answers1

4

I found a simple solution. Basically I have to overwrite the default Reads and Writes for DateTime as below:

# If you use Timestamp
implicit val tsreads: Reads[Timestamp] = Reads.of[Long] map (new Timestamp(_))
implicit val tswrites: Writes[Timestamp] = Writes { (ts: Timestamp) => JsString(ts.toString)}

# If you use DateTime
implicit val tsreads: Reads[DateTime] = Reads.of[String] map (new DateTime(_))
implicit val tswrites: Writes[DateTime] = Writes { (dt: DateTime) => JsString(dt.toString)}
Khanetor
  • 11,595
  • 8
  • 40
  • 76