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
|
package xyz.driver.core
import java.util.Calendar
import scala.util.Try
import scalaz.std.anyVal._
import scalaz.Scalaz.stringInstance
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 {
sealed trait DayOfWeek
object 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 All: Set[DayOfWeek] = Set(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
def fromString(day: String): Option[DayOfWeek] = All.find(_.toString === 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
}
}
}
}
|