fuzzy-time-0.2.0.2: src/Data/FuzzyTime/Resolve.hs
module Data.FuzzyTime.Resolve
( resolveZonedTime,
resolveLocalTime,
resolveLocalTimeOne,
resolveLocalTimeOther,
resolveLocalTimeBoth,
morning,
evening,
resolveTimeOfDay,
resolveTimeOfDayWithDiff,
normaliseTimeOfDay,
resolveDay,
)
where
import Data.Fixed (Pico, mod')
import Data.FuzzyTime.Types (AmbiguousLocalTime (BothTimeAndDay, OnlyDaySpecified), FuzzyDay (DayInMonth, DiffDays, DiffMonths, DiffWeeks, ExactDay, NextDayOfTheWeek, Now, OnlyDay, Today, Tomorrow, Yesterday), FuzzyLocalTime (FuzzyLocalTime), FuzzyTimeOfDay (AtExact, AtHour, AtMinute, Evening, HoursDiff, Midnight, MinutesDiff, Morning, Noon, SameTime, SecondsDiff), FuzzyZonedTime (ZonedNow), Month, Some (Both, One, Other), dayOfTheWeekNum, daysInMonth, monthNum, numMonth)
import Data.Maybe (fromJust)
import Data.Time (Day, DayOfWeek, LocalTime (LocalTime), TimeOfDay (TimeOfDay), ZonedTime, addDays, fromGregorian, midday, midnight, toGregorian)
import Data.Time.Calendar.WeekDate (toWeekDate)
resolveZonedTime :: ZonedTime -> FuzzyZonedTime -> ZonedTime
resolveZonedTime zt ZonedNow = zt
resolveLocalTime :: LocalTime -> FuzzyLocalTime -> AmbiguousLocalTime
resolveLocalTime lt (FuzzyLocalTime sft) =
case sft of
One fd -> OnlyDaySpecified $ resolveLocalTimeOne lt fd
Other ftod -> BothTimeAndDay $ resolveLocalTimeOther lt ftod
Both fd ftod -> BothTimeAndDay $ resolveLocalTimeBoth lt fd ftod
resolveLocalTimeOne :: LocalTime -> FuzzyDay -> Day
resolveLocalTimeOne (LocalTime ld _) fd = resolveDay ld fd
resolveLocalTimeOther :: LocalTime -> FuzzyTimeOfDay -> LocalTime
resolveLocalTimeOther (LocalTime ld ltod) ftod =
let (d, tod) = resolveTimeOfDayWithDiff ltod ftod
in LocalTime (addDays d ld) tod
resolveLocalTimeBoth :: LocalTime -> FuzzyDay -> FuzzyTimeOfDay -> LocalTime
resolveLocalTimeBoth (LocalTime ld ltod) fd ftod =
let withDiff = resolveTimeOfDayWithDiff ltod ftod
withoutDiff = (0, resolveTimeOfDay ltod ftod)
(d, tod) =
case fd of
Now -> withDiff
Today -> withDiff
_ -> withoutDiff
in LocalTime (addDays d $ resolveDay ld fd) tod
resolveTimeOfDay :: TimeOfDay -> FuzzyTimeOfDay -> TimeOfDay
resolveTimeOfDay tod ftod = snd $ resolveTimeOfDayWithDiff tod ftod
resolveTimeOfDayWithDiff :: TimeOfDay -> FuzzyTimeOfDay -> (Integer, TimeOfDay)
resolveTimeOfDayWithDiff tod@(TimeOfDay h m s) ftod =
case ftod of
SameTime -> (0, tod)
Noon -> next midday
Midnight -> next midnight
Morning -> next morning
Evening -> next evening
AtHour h_ -> next $ TimeOfDay h_ 0 0
AtMinute h_ m_ -> next $ TimeOfDay h_ m_ 0
AtExact tod_ -> next tod_
HoursDiff hd -> normaliseTimeOfDay (h + fromIntegral hd) m s
MinutesDiff md -> normaliseTimeOfDay h (m + fromIntegral md) s
SecondsDiff sd -> normaliseTimeOfDay h m (s + sd)
where
next tod_ = (skipIf (>= tod_), tod_)
skipIf p =
if p tod
then 1
else 0
normaliseTimeOfDay :: Int -> Int -> Pico -> (Integer, TimeOfDay)
normaliseTimeOfDay h m s =
let s' = s `mod'` 60
totalM = m + round (s - s') `div` 60
m' = totalM `mod` 60
totalH = h + (totalM - m') `div` 60
h' = totalH `mod` 24
totalD = (totalH - h') `div` 24
in (fromIntegral totalD, TimeOfDay h' m' s')
morning :: TimeOfDay
morning = TimeOfDay 6 0 0
evening :: TimeOfDay
evening = TimeOfDay 18 0 0
resolveDay :: Day -> FuzzyDay -> Day
resolveDay d fd =
case fd of
Yesterday -> addDays (-1) d
Now -> d
Today -> d
Tomorrow -> addDays 1 d
OnlyDay di -> nextDayOnDay d di
DayInMonth mi di -> nextDayOndayInMonth d mi di
DiffDays ds -> addDays (fromIntegral ds) d
DiffWeeks ws -> addDays (7 * fromIntegral ws) d
DiffMonths ms -> addDays (30 * fromIntegral ms) d
NextDayOfTheWeek dow -> nextDayOfTheWeek d dow
ExactDay d_ -> d_
nextDayOnDay :: Day -> Int -> Day
nextDayOnDay d di =
let (y_, m_, _) = toGregorian d
go :: Integer -> [(Month, Int)] -> Day
go y [] =
let y' = y + 1
in go y' (daysInMonth y')
go y ((month, mds) : rest) =
if mds >= di
then
let d' = fromGregorian y (monthNum month) di
in if d' >= d
then d'
else go y rest
else go y rest
in go y_ (drop (m_ - 1) $ daysInMonth y_)
nextDayOndayInMonth :: Day -> Int -> Int -> Day
nextDayOndayInMonth d mi di =
let (y_, _, _) = toGregorian d
go y =
let mds = fromJust $ lookup (numMonth mi) (daysInMonth y)
in if mds >= di
then
let d' = fromGregorian y mi di
in if d' >= d
then d'
else go (y + 1)
else go (y + 1)
in go y_
nextDayOfTheWeek :: Day -> DayOfWeek -> Day
nextDayOfTheWeek d dow =
let (_, _, i_) = toWeekDate d
down = dayOfTheWeekNum dow
diff = fromIntegral $ down - i_
diff' =
if diff <= 0
then diff + 7
else diff
in addDays diff' d