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
|
package xyz.driver.core
import java.util.Calendar
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
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
}
}
}
}
|