@@ -0,0 +1,41 @@
1+module Data.Time.Holidays
2+ ( addTimeExcludingHolidays
6+import qualified Data.Set as S
8+import Data.Time.Calendar.OrdinalDate
9+import Data.Time.LocalTime.TimeZone.Series
12+addTimeExcludingHolidays
18+addTimeExcludingHolidays hDays tz dTime =
19+ addLocalTime' dTime . utcToLocalTime' tz
21+ shouldSkip day = weekday `elem` [6, 7] || day `S.member` hDays
22+ where (_, weekday) = mondayStartWeek day
24+ diffTimeToPicoseconds . secondsToDiffTime $ 60 * 60 * 24
25+ addLocalTime' :: DiffTime -> LocalTime -> UTCTime
26+ addLocalTime' dTime' (LocalTime lDay' lTime') =
28+ actualDays = filter (not . shouldSkip) [lDay'..]
29+ -- Jumps to nearest day if `now` is a holiday
30+ lTime = if shouldSkip lDay'
33+ -- At this point the head of `actualDays` and `lTime`
34+ -- is the day from which we should be counting
35+ (dDays, dPicoSeconds) =
36+ (diffTimeToPicoseconds $ dTime' + (timeOfDayToTime lTime))
37+ `divMod` dayPicoseconds
38+ resultDay = head . drop (fromIntegral dDays) $ actualDays
40+ timeToTimeOfDay . picosecondsToDiffTime $ dPicoSeconds
41+ in localTimeToUTC' tz $ LocalTime resultDay resultTime