aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz
diff options
context:
space:
mode:
authorArthur Rand <arand@ucsc.edu>2018-03-28 05:56:21 -0700
committerGitHub <noreply@github.com>2018-03-28 05:56:21 -0700
commitfc6ecfe212c84271a3454617054aaf25890e886a (patch)
tree4d6b85e059e10aa5b461af9e21c32c84bd73250f /src/main/scala/xyz
parent30dba9ebf2abfd06452f46bac2b4c922043f56e6 (diff)
downloaddriver-core-fc6ecfe212c84271a3454617054aaf25890e886a.tar.gz
driver-core-fc6ecfe212c84271a3454617054aaf25890e886a.tar.bz2
driver-core-fc6ecfe212c84271a3454617054aaf25890e886a.zip
[API-1468] add TimeOfDay (#141)v1.8.11
* add TimeOfDay * add formatter * . * Revert "." This reverts commit 89576de98092dd75d3af7d82d244d5eaa24d31d9. * scalafmt * add before and after to ToD, and tests * rearrage, make fromStrings * add generator * address comments * use explicit string for TimeZoneId * renaming * revert Converters changes * change name of private method * change apply method * use month
Diffstat (limited to 'src/main/scala/xyz')
-rw-r--r--src/main/scala/xyz/driver/core/generators.scala4
-rw-r--r--src/main/scala/xyz/driver/core/json.scala31
-rw-r--r--src/main/scala/xyz/driver/core/time.scala87
3 files changed, 119 insertions, 3 deletions
diff --git a/src/main/scala/xyz/driver/core/generators.scala b/src/main/scala/xyz/driver/core/generators.scala
index e3ff326..143044c 100644
--- a/src/main/scala/xyz/driver/core/generators.scala
+++ b/src/main/scala/xyz/driver/core/generators.scala
@@ -3,7 +3,7 @@ package xyz.driver.core
import java.math.MathContext
import java.util.UUID
-import xyz.driver.core.time.{Time, TimeRange}
+import xyz.driver.core.time.{Time, TimeOfDay, TimeRange}
import xyz.driver.core.date.{Date, DayOfWeek}
import scala.reflect.ClassTag
@@ -69,6 +69,8 @@ object generators {
def nextTime(): Time = Time(math.abs(nextLong() % System.currentTimeMillis))
+ def nextTimeOfDay: TimeOfDay = TimeOfDay(java.time.LocalTime.MIN.plusSeconds(nextLong), java.util.TimeZone.getDefault)
+
def nextTimeRange(): TimeRange = {
val oneTime = nextTime()
val anotherTime = nextTime()
diff --git a/src/main/scala/xyz/driver/core/json.scala b/src/main/scala/xyz/driver/core/json.scala
index 02a35fd..4d7fa04 100644
--- a/src/main/scala/xyz/driver/core/json.scala
+++ b/src/main/scala/xyz/driver/core/json.scala
@@ -1,7 +1,7 @@
package xyz.driver.core
import java.net.InetAddress
-import java.util.UUID
+import java.util.{TimeZone, UUID}
import scala.reflect.runtime.universe._
import scala.util.Try
@@ -14,7 +14,7 @@ import spray.json._
import xyz.driver.core.auth.AuthCredentials
import xyz.driver.core.date.{Date, DayOfWeek, Month}
import xyz.driver.core.domain.{Email, PhoneNumber}
-import xyz.driver.core.time.Time
+import xyz.driver.core.time.{Time, TimeOfDay}
import eu.timepit.refined.refineV
import eu.timepit.refined.api.{Refined, Validate}
import eu.timepit.refined.collection.NonEmpty
@@ -80,6 +80,33 @@ object json {
}
}
+ implicit object localTimeFormat extends JsonFormat[java.time.LocalTime] {
+ private val formatter = TimeOfDay.getFormatter
+ def read(json: JsValue): java.time.LocalTime = json match {
+ case JsString(chars) =>
+ java.time.LocalTime.parse(chars)
+ case _ => deserializationError(s"Expected time string got ${json.toString}")
+ }
+
+ def write(obj: java.time.LocalTime): JsValue = {
+ JsString(obj.format(formatter))
+ }
+ }
+
+ implicit object timeZoneFormat extends JsonFormat[java.util.TimeZone] {
+ override def write(obj: TimeZone): JsValue = {
+ JsString(obj.getID())
+ }
+
+ override def read(json: JsValue): TimeZone = json match {
+ case JsString(chars) =>
+ java.util.TimeZone.getTimeZone(chars)
+ case _ => deserializationError(s"Expected time zone string got ${json.toString}")
+ }
+ }
+
+ implicit val timeOfDayFormat: RootJsonFormat[TimeOfDay] = jsonFormat2(TimeOfDay.apply)
+
implicit val dayOfWeekFormat: JsonFormat[DayOfWeek] =
new EnumJsonFormat[DayOfWeek](DayOfWeek.All.map(w => w.toString -> w)(collection.breakOut): _*)
diff --git a/src/main/scala/xyz/driver/core/time.scala b/src/main/scala/xyz/driver/core/time.scala
index 3bcc7bc..bab304d 100644
--- a/src/main/scala/xyz/driver/core/time.scala
+++ b/src/main/scala/xyz/driver/core/time.scala
@@ -4,7 +4,10 @@ import java.text.SimpleDateFormat
import java.util._
import java.util.concurrent.TimeUnit
+import xyz.driver.core.date.Month
+
import scala.concurrent.duration._
+import scala.util.Try
object time {
@@ -39,6 +42,90 @@ object time {
}
}
+ /**
+ * Encapsulates a time and timezone without a specific date.
+ */
+ final case class TimeOfDay(localTime: java.time.LocalTime, timeZone: TimeZone) {
+
+ /**
+ * Is this time before another time on a specific day. Day light savings safe. These are zero-indexed
+ * for month/day.
+ */
+ def isBefore(other: TimeOfDay, day: Int, month: Month, year: Int): Boolean = {
+ toCalendar(day, month, year).before(other.toCalendar(day, month, year))
+ }
+
+ /**
+ * Is this time after another time on a specific day. Day light savings safe.
+ */
+ def isAfter(other: TimeOfDay, day: Int, month: Month, year: Int): Boolean = {
+ toCalendar(day, month, year).after(other.toCalendar(day, month, year))
+ }
+
+ def sameTimeAs(other: TimeOfDay, day: Int, month: Month, year: Int): Boolean = {
+ toCalendar(day, month, year).equals(other.toCalendar(day, month, year))
+ }
+
+ /**
+ * Enforces the same formatting as expected by [[java.sql.Time]]
+ * @return string formatted for `java.sql.Time`
+ */
+ def timeString: String = {
+ localTime.format(TimeOfDay.getFormatter)
+ }
+
+ /**
+ * @return a string parsable by [[java.util.TimeZone]]
+ */
+ def timeZoneString: String = {
+ timeZone.getID
+ }
+
+ /**
+ * @return this [[TimeOfDay]] as [[java.sql.Time]] object, [[java.sql.Time.valueOf]] will
+ * throw when the string is not valid, but this is protected by [[timeString]] method.
+ */
+ def toTime: java.sql.Time = {
+ java.sql.Time.valueOf(timeString)
+ }
+
+ private def toCalendar(day: Int, month: Int, year: Int): Calendar = {
+ val cal = Calendar.getInstance(timeZone)
+ cal.set(year, month, day, localTime.getHour, localTime.getMinute, localTime.getSecond)
+ cal
+ }
+ }
+
+ object TimeOfDay {
+ def now(): TimeOfDay = {
+ TimeOfDay(java.time.LocalTime.now(), TimeZone.getDefault)
+ }
+
+ /**
+ * Throws when [s] is not parsable by [[java.time.LocalTime.parse]], uses default [[java.util.TimeZone]]
+ */
+ def parseTimeString(tz: TimeZone = TimeZone.getDefault)(s: String): TimeOfDay = {
+ TimeOfDay(java.time.LocalTime.parse(s), tz)
+ }
+
+ def fromString(tz: TimeZone)(s: String): Option[TimeOfDay] = {
+ val op = Try(java.time.LocalTime.parse(s)).toOption
+ op.map(lt => TimeOfDay(lt, tz))
+ }
+
+ def fromStrings(zoneId: String)(s: String): Option[TimeOfDay] = {
+ val op = Try(TimeZone.getTimeZone(zoneId)).toOption
+ op.map(tz => TimeOfDay.parseTimeString(tz)(s))
+ }
+
+ /**
+ * Formatter that enforces `HH:mm:ss` which is expected by [[java.sql.Time]]
+ */
+ def getFormatter: java.time.format.DateTimeFormatter = {
+ java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss")
+ }
+ }
+
object Time {
implicit def timeOrdering: Ordering[Time] = Ordering.by(_.millis)