From 26613a6690823300515156d75bfb51e430b7ae5c Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Wed, 15 Mar 2017 16:10:55 -0700 Subject: reintroduce month format --- src/main/scala/xyz/driver/core/json.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/scala/xyz/driver/core/json.scala b/src/main/scala/xyz/driver/core/json.scala index 01efd57..4c5d078 100644 --- a/src/main/scala/xyz/driver/core/json.scala +++ b/src/main/scala/xyz/driver/core/json.scala @@ -7,7 +7,7 @@ import akka.http.scaladsl.unmarshalling.Unmarshaller import spray.json.{DeserializationException, JsNumber, _} import xyz.driver.core.auth.AuthCredentials import xyz.driver.core.time.Time -import xyz.driver.core.date.Date +import xyz.driver.core.date.{Date, Month} import xyz.driver.core.domain.{Email, PhoneNumber} import scala.reflect.runtime.universe._ @@ -81,6 +81,14 @@ object json { } } + implicit val monthFormat = new RootJsonFormat[Month] { + def write(month: Month) = JsNumber(month) + def read(value: JsValue): Month = value match { + case JsNumber(month) if 0 <= month && month <= 11 => date.tagMonth(month.toInt) + case _ => throw DeserializationException("Expected a number from 0 to 11") + } + } + def RevisionInPath[T]: PathMatcher1[Revision[T]] = PathMatcher("""[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}""".r) flatMap { string => Some(Revision[T](string)) -- cgit v1.2.3 From d0465333090701c5d27229a17f0402623098e113 Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Wed, 15 Mar 2017 19:41:34 -0700 Subject: make tagMonth public --- src/main/scala/xyz/driver/core/date.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/xyz/driver/core/date.scala b/src/main/scala/xyz/driver/core/date.scala index 5a6f736..37b9a09 100644 --- a/src/main/scala/xyz/driver/core/date.scala +++ b/src/main/scala/xyz/driver/core/date.scala @@ -5,7 +5,7 @@ import java.util.Calendar object date { type Month = Int @@ Month.type - private[core] def tagMonth(value: Int): Month = value.asInstanceOf[Month] + def tagMonth(value: Int): Month = value.asInstanceOf[Month] object Month { val JANUARY = tagMonth(Calendar.JANUARY) -- cgit v1.2.3 From 6ece67cd776ca5da1aef6595929772a7f801f1ab Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Wed, 15 Mar 2017 19:51:56 -0700 Subject: tag int as month via apply method --- .../scala/xyz/driver/core/database/package.scala | 4 +-- src/main/scala/xyz/driver/core/date.scala | 31 ++++++++++++---------- src/main/scala/xyz/driver/core/json.scala | 2 +- src/main/scala/xyz/driver/core/time.scala | 2 +- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/scala/xyz/driver/core/database/package.scala b/src/main/scala/xyz/driver/core/database/package.scala index 32b240e..791a688 100644 --- a/src/main/scala/xyz/driver/core/database/package.scala +++ b/src/main/scala/xyz/driver/core/database/package.scala @@ -3,7 +3,7 @@ package xyz.driver.core import java.sql.{Date => SqlDate} import java.util.Calendar -import date.Date +import date.{Date, Month} import slick.dbio.{DBIOAction, NoStream} package object database { @@ -18,7 +18,7 @@ package object database { // should only be interpreted in the running JVMs timezone. val cal = Calendar.getInstance() cal.setTime(sqlDate) - Date(cal.get(Calendar.YEAR), date.tagMonth(cal.get(Calendar.MONTH)), cal.get(Calendar.DAY_OF_MONTH)) + Date(cal.get(Calendar.YEAR), Month(cal.get(Calendar.MONTH)), cal.get(Calendar.DAY_OF_MONTH)) } private[database] def dateToSqlDate(date: Date): SqlDate = { diff --git a/src/main/scala/xyz/driver/core/date.scala b/src/main/scala/xyz/driver/core/date.scala index 37b9a09..b9bcacb 100644 --- a/src/main/scala/xyz/driver/core/date.scala +++ b/src/main/scala/xyz/driver/core/date.scala @@ -5,21 +5,24 @@ import java.util.Calendar object date { type Month = Int @@ Month.type - def tagMonth(value: Int): Month = value.asInstanceOf[Month] object Month { - val JANUARY = tagMonth(Calendar.JANUARY) - val FEBRUARY = tagMonth(Calendar.FEBRUARY) - val MARCH = tagMonth(Calendar.MARCH) - val APRIL = tagMonth(Calendar.APRIL) - val MAY = tagMonth(Calendar.MAY) - val JUNE = tagMonth(Calendar.JUNE) - val JULY = tagMonth(Calendar.JULY) - val AUGUST = tagMonth(Calendar.AUGUST) - val SEPTEMBER = tagMonth(Calendar.SEPTEMBER) - val OCTOBER = tagMonth(Calendar.OCTOBER) - val NOVEMBER = tagMonth(Calendar.NOVEMBER) - val DECEMBER = tagMonth(Calendar.DECEMBER) + def apply(value: Int): Month = { + require(0 to 11 contains value, "Month is zero-indexed: 0 <= value <= 11") + value.asInstanceOf[Month] + } + val JANUARY = Month(Calendar.JANUARY) + val FEBRUARY = Month(Calendar.FEBRUARY) + val MARCH = Month(Calendar.MARCH) + val APRIL = Month(Calendar.APRIL) + val MAY = Month(Calendar.MAY) + val JUNE = Month(Calendar.JUNE) + val JULY = Month(Calendar.JULY) + val AUGUST = Month(Calendar.AUGUST) + val SEPTEMBER = Month(Calendar.SEPTEMBER) + val OCTOBER = Month(Calendar.OCTOBER) + val NOVEMBER = Month(Calendar.NOVEMBER) + val DECEMBER = Month(Calendar.DECEMBER) } final case class Date(year: Int, month: Month, day: Int) { @@ -40,7 +43,7 @@ object date { def fromString(dateString: String): Option[Date] = { util.Try(dateString.split("-").map(_.toInt)).toOption collect { case Array(year, month, day) if (1 to 12 contains month) && (1 to 31 contains day) => - Date(year, tagMonth(month - 1), day) + Date(year, Month(month - 1), day) } } } diff --git a/src/main/scala/xyz/driver/core/json.scala b/src/main/scala/xyz/driver/core/json.scala index 4c5d078..457a087 100644 --- a/src/main/scala/xyz/driver/core/json.scala +++ b/src/main/scala/xyz/driver/core/json.scala @@ -84,7 +84,7 @@ object json { implicit val monthFormat = new RootJsonFormat[Month] { def write(month: Month) = JsNumber(month) def read(value: JsValue): Month = value match { - case JsNumber(month) if 0 <= month && month <= 11 => date.tagMonth(month.toInt) + case JsNumber(month) if 0 <= month && month <= 11 => Month(month.toInt) case _ => throw DeserializationException("Expected a number from 0 to 11") } } diff --git a/src/main/scala/xyz/driver/core/time.scala b/src/main/scala/xyz/driver/core/time.scala index cbb86ed..ed5eb11 100644 --- a/src/main/scala/xyz/driver/core/time.scala +++ b/src/main/scala/xyz/driver/core/time.scala @@ -30,7 +30,7 @@ object time { def toDate(timezone: TimeZone): date.Date = { val cal = Calendar.getInstance(timezone) cal.setTimeInMillis(millis) - date.Date(cal.get(Calendar.YEAR), date.tagMonth(cal.get(Calendar.MONTH)), cal.get(Calendar.DAY_OF_MONTH)) + date.Date(cal.get(Calendar.YEAR), date.Month(cal.get(Calendar.MONTH)), cal.get(Calendar.DAY_OF_MONTH)) } } -- cgit v1.2.3 From be262d8b985470e24adc924ca1f4d2b83fca744c Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Wed, 15 Mar 2017 21:57:53 -0700 Subject: fix tests --- src/test/scala/xyz/driver/core/DateTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/xyz/driver/core/DateTest.scala b/src/test/scala/xyz/driver/core/DateTest.scala index dc9bca3..c1185cd 100644 --- a/src/test/scala/xyz/driver/core/DateTest.scala +++ b/src/test/scala/xyz/driver/core/DateTest.scala @@ -10,7 +10,7 @@ class DateTest extends FlatSpec with Matchers with Checkers { year <- Gen.choose(0, 3000) month <- Gen.choose(0, 11) day <- Gen.choose(1, 31) - } yield Date(year, date.tagMonth(month), day) + } yield Date(year, date.Month(month), day) implicit val arbitraryDate = Arbitrary[Date](dateGenerator) "Date" should "correctly convert to and from String" in { -- cgit v1.2.3 From df06c48f2d6e9f627f57656a60790d8297510b07 Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Thu, 16 Mar 2017 16:11:43 -0400 Subject: add Day, Month, and Year extractors --- src/main/scala/xyz/driver/core/date.scala | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/scala/xyz/driver/core/date.scala b/src/main/scala/xyz/driver/core/date.scala index b9bcacb..d3fd8a7 100644 --- a/src/main/scala/xyz/driver/core/date.scala +++ b/src/main/scala/xyz/driver/core/date.scala @@ -2,8 +2,20 @@ package xyz.driver.core import java.util.Calendar +import scala.util.Try + +import scalaz.std.anyVal._ +import scalaz.syntax.equal._ + object date { + object Day { + def unapply(dayString: String): Option[Int] = { + require(dayString.length === 2, s"ISO 8601 day string, DD, must have length 2: $dayString") + Try(dayString.toInt).toOption + } + } + type Month = Int @@ Month.type object Month { @@ -23,6 +35,18 @@ object date { val OCTOBER = Month(Calendar.OCTOBER) val NOVEMBER = Month(Calendar.NOVEMBER) val DECEMBER = Month(Calendar.DECEMBER) + + def unapply(monthString: String): Option[Month] = { + require(monthString.length === 2, s"ISO 8601 month string, MM, must have length 2: $monthString") + Try(monthString.toInt).toOption.map(isoM => apply(isoM - 1)) + } + } + + object Year { + def unapply(yearString: String): Option[Int] = { + require(yearString.length === 4, s"ISO 8601 year string, YYYY, must have length 4: $yearString") + Try(yearString.toInt).toOption + } } final case class Date(year: Int, month: Month, day: Int) { @@ -41,9 +65,9 @@ object date { } def fromString(dateString: String): Option[Date] = { - util.Try(dateString.split("-").map(_.toInt)).toOption collect { - case Array(year, month, day) if (1 to 12 contains month) && (1 to 31 contains day) => - Date(year, Month(month - 1), day) + dateString.split('-') match { + case Array(Year(year), Month(month), Day(day)) => Some(Date(year, month, day)) + case _ => None } } } -- cgit v1.2.3 From 59815982ac0c0998573aae3a82f038b34f3dfa75 Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Thu, 16 Mar 2017 16:22:06 -0400 Subject: add Year and Day type tags --- src/main/scala/xyz/driver/core/date.scala | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/scala/xyz/driver/core/date.scala b/src/main/scala/xyz/driver/core/date.scala index d3fd8a7..e58be9a 100644 --- a/src/main/scala/xyz/driver/core/date.scala +++ b/src/main/scala/xyz/driver/core/date.scala @@ -9,10 +9,17 @@ import scalaz.syntax.equal._ object date { + type Day = Int @@ Day.type + object Day { + def apply(value: Int): Day = { + require(0 to 31 contains value, "Day must be in range 0 <= value <= 11") + value.asInstanceOf[Day] + } + def unapply(dayString: String): Option[Int] = { require(dayString.length === 2, s"ISO 8601 day string, DD, must have length 2: $dayString") - Try(dayString.toInt).toOption + Try(dayString.toInt).toOption.map(apply) } } @@ -42,10 +49,14 @@ object date { } } + type Year = Int @@ Year.type + object Year { + def apply(value: Int): Year = value.asInstanceOf[Year] + def unapply(yearString: String): Option[Int] = { require(yearString.length === 4, s"ISO 8601 year string, YYYY, must have length 4: $yearString") - Try(yearString.toInt).toOption + Try(yearString.toInt).toOption.map(apply) } } -- cgit v1.2.3 From 2c81232e45d5eafbf38900014eec6fc4fb7257c2 Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Fri, 17 Mar 2017 14:02:32 -0400 Subject: fix error message --- src/main/scala/xyz/driver/core/date.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/xyz/driver/core/date.scala b/src/main/scala/xyz/driver/core/date.scala index e58be9a..2e0f74e 100644 --- a/src/main/scala/xyz/driver/core/date.scala +++ b/src/main/scala/xyz/driver/core/date.scala @@ -13,7 +13,7 @@ object date { object Day { def apply(value: Int): Day = { - require(0 to 31 contains value, "Day must be in range 0 <= value <= 11") + require(1 to 31 contains value, "Day must be in range 1 <= value <= 31") value.asInstanceOf[Day] } -- cgit v1.2.3 From 8ac5bfc5d5f791f10755672f671a7ee07862cf02 Mon Sep 17 00:00:00 2001 From: Stewart Stewart Date: Fri, 17 Mar 2017 14:08:08 -0400 Subject: Add documentation with todo for decoupling ISO 8601 --- src/main/scala/xyz/driver/core/date.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/scala/xyz/driver/core/date.scala b/src/main/scala/xyz/driver/core/date.scala index 2e0f74e..d6f64e4 100644 --- a/src/main/scala/xyz/driver/core/date.scala +++ b/src/main/scala/xyz/driver/core/date.scala @@ -7,6 +7,11 @@ import scala.util.Try import scalaz.std.anyVal._ import scalaz.syntax.equal._ +/** + * Driver Date type and related validators/extractors. + * Day, Month, and Year extractors are from ISO 8601 strings => driver...Date integers. + * TODO: Decouple extractors from ISO 8601, as we might want to parse other formats. + */ object date { type Day = Int @@ Day.type -- cgit v1.2.3