1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
package xyz.driver.core
import java.util.Calendar
import enumeratum._
import scalaz.std.anyVal._
import scalaz.syntax.equal._
import scala.collection.immutable.IndexedSeq
import scala.util.Try
/**
* 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 {
sealed trait DayOfWeek extends EnumEntry
object DayOfWeek extends Enum[DayOfWeek] {
case object Monday extends DayOfWeek
case object Tuesday extends DayOfWeek
case object Wednesday extends DayOfWeek
case object Thursday extends DayOfWeek
case object Friday extends DayOfWeek
case object Saturday extends DayOfWeek
case object Sunday extends DayOfWeek
val values: IndexedSeq[DayOfWeek] = findValues
val All: Set[DayOfWeek] = values.toSet
def fromString(day: String): Option[DayOfWeek] = withNameInsensitiveOption(day)
}
type Day = Int @@ Day.type
object Day {
def apply(value: Int): Day = {
require(1 to 31 contains value, "Day must be in range 1 <= value <= 31")
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.map(apply)
}
}
type Month = Int @@ Month.type
object Month {
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)
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))
}
}
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.map(apply)
}
}
final case class Date(year: Int, month: Month, day: Int) {
override def toString = f"$year%04d-${month + 1}%02d-$day%02d"
}
object Date {
implicit def dateOrdering: Ordering[Date] = Ordering.fromLessThan { (date1, date2) =>
if (date1.year != date2.year) {
date1.year < date2.year
} else if (date1.month != date2.month) {
date1.month < date2.month
} else {
date1.day < date2.day
}
}
def fromString(dateString: String): Option[Date] = {
dateString.split('-') match {
case Array(Year(year), Month(month), Day(day)) => Some(Date(year, month, day))
case _ => None
}
}
}
}
|