Skip to content

Commit 525691e

Browse files
authored
Merge pull request #73 from mschristiansen/enum
Added enum instance for Date and date implementation of adjust
2 parents ffdd94f + ae79529 commit 525691e

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

src/Data/Date.purs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ module Data.Date
99
, diff
1010
, isLeapYear
1111
, lastDayOfMonth
12+
, adjust
1213
, module Data.Date.Component
1314
) where
1415

1516
import Prelude
1617

1718
import Data.Date.Component (Day, Month(..), Weekday(..), Year)
18-
import Data.Enum (toEnum, fromEnum)
19+
import Data.Enum (class Enum, toEnum, fromEnum, succ, pred)
1920
import Data.Function.Uncurried (Fn3, runFn3, Fn4, runFn4, Fn6, runFn6)
20-
import Data.Maybe (Maybe(..), fromJust)
21-
import Data.Time.Duration (class Duration, Milliseconds, toDuration)
21+
import Data.Int (fromNumber)
22+
import Data.Maybe (Maybe(..), fromJust, fromMaybe, isNothing)
23+
import Data.Time.Duration (class Duration, Days(..), Milliseconds, toDuration)
2224
import Partial.Unsafe (unsafePartial)
2325

2426
-- | A date value in the Gregorian calendar.
@@ -51,6 +53,24 @@ instance boundedDate :: Bounded Date where
5153
instance showDate :: Show Date where
5254
show (Date y m d) = "(Date " <> show y <> " " <> show m <> " " <> show d <> ")"
5355

56+
instance enumDate :: Enum Date where
57+
succ (Date y m d) = Date <$> y' <*> pure m' <*> d'
58+
where
59+
d' = if isNothing sd then toEnum 1 else sd
60+
m' = if isNothing sd then fromMaybe January sm else m
61+
y' = if isNothing sd && isNothing sm then succ y else Just y
62+
sd = let v = succ d in if v > Just l then Nothing else v
63+
sm = succ m
64+
l = lastDayOfMonth y m
65+
pred (Date y m d) = Date <$> y' <*> pure m' <*> d'
66+
where
67+
d' = if isNothing pd then Just l else pd
68+
m' = if isNothing pd then fromMaybe December pm else m
69+
y' = if isNothing pd && isNothing pm then pred y else Just y
70+
pd = pred d
71+
pm = pred m
72+
l = lastDayOfMonth y m'
73+
5474
-- | The year component of a date value.
5575
year :: Date -> Year
5676
year (Date y _ _) = y
@@ -69,6 +89,26 @@ weekday = unsafePartial \(Date y m d) ->
6989
let n = runFn3 calcWeekday y (fromEnum m) d
7090
in if n == 0 then fromJust (toEnum 7) else fromJust (toEnum n)
7191

92+
-- | Adjusts a date with a Duration in days. The number of days must
93+
-- | already be an integer and fall within the valid range of values
94+
-- | for the Int type.
95+
adjust :: Days -> Date -> Maybe Date
96+
adjust (Days n) date = fromNumber n >>= flip adj date
97+
where
98+
adj 0 dt = Just dt
99+
adj i (Date y m d) = adj i' =<< dt'
100+
where
101+
i' | low = j
102+
| hi = j - fromEnum l - 1
103+
| otherwise = 0
104+
dt' | low = pred =<< Date y m <$> toEnum 1
105+
| hi = succ (Date y m l)
106+
| otherwise = Date y m <$> toEnum j
107+
j = i + fromEnum d
108+
low = j < 1
109+
hi = j > fromEnum l
110+
l = lastDayOfMonth y (if low then fromMaybe December (pred m) else m)
111+
72112
-- | Calculates the difference between two dates, returning the result as a
73113
-- | duration.
74114
diff :: forall d. Duration d => Date -> Date -> d

test/Test/Main.purs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ main = do
117117
let d1 = unsafePartial fromJust $ Date.canonicalDate <$> toEnum 2016 <*> pure Date.January <*> toEnum 1
118118
let d2 = unsafePartial fromJust $ Date.canonicalDate <$> toEnum 2016 <*> pure Date.February <*> toEnum 1
119119
let d3 = unsafePartial fromJust $ Date.canonicalDate <$> toEnum 2016 <*> pure Date.March <*> toEnum 1
120+
let d4 = unsafePartial fromJust $ Date.canonicalDate <$> toEnum 2018 <*> pure Date.September <*> toEnum 26
121+
let d5 = unsafePartial fromJust $ Date.canonicalDate <$> toEnum 1988 <*> pure Date.August <*> toEnum 15
120122

121123
log "Check that diff behaves as expected"
122124
assert $ Date.diff d2 d1 == Duration.Days 31.0
@@ -132,6 +134,14 @@ main = do
132134
assert $ Date.month epochDate == bottom
133135
assert $ Date.day epochDate == bottom
134136

137+
log "Check that adjust behaves as expected"
138+
assert $ Date.adjust (Duration.Days 31.0) d1 == Just d2
139+
assert $ Date.adjust (Duration.Days 999.0) d1 == Just d4
140+
assert $ Date.adjust (Duration.Days 10000.0) d5 == Just d1
141+
assert $ Date.adjust (Duration.Days (-31.0)) d2 == Just d1
142+
assert $ Date.adjust (Duration.Days (- 999.0)) d4 == Just d1
143+
assert $ Date.adjust (Duration.Days (-10000.0)) d1 == Just d5
144+
135145
-- datetime ----------------------------------------------------------------
136146

137147
let dt1 = DateTime.DateTime d1 t1

0 commit comments

Comments
 (0)