{-# LANGUAGE Safe #-} -- | Week-based calendars module Data.Time.Calendar.WeekDate ( Year, WeekOfYear, DayOfWeek (..), dayOfWeek, FirstWeekType (..), toWeekCalendar, fromWeekCalendar, fromWeekCalendarValid, -- * ISO 8601 Week Date format toWeekDate, fromWeekDate, pattern YearWeekDay, fromWeekDateValid, showWeekDate, ) where import Data.Time.Calendar.Days import Data.Time.Calendar.OrdinalDate import Data.Time.Calendar.Private import Data.Time.Calendar.Week data FirstWeekType = -- | first week is the first whole week of the year FirstWholeWeek | -- | first week is the first week with four days in the year FirstMostWeek deriving (FirstWeekType -> FirstWeekType -> Bool (FirstWeekType -> FirstWeekType -> Bool) -> (FirstWeekType -> FirstWeekType -> Bool) -> Eq FirstWeekType forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a $c== :: FirstWeekType -> FirstWeekType -> Bool == :: FirstWeekType -> FirstWeekType -> Bool $c/= :: FirstWeekType -> FirstWeekType -> Bool /= :: FirstWeekType -> FirstWeekType -> Bool Eq) firstDayOfWeekCalendar :: FirstWeekType -> DayOfWeek -> Year -> Day firstDayOfWeekCalendar :: FirstWeekType -> DayOfWeek -> Integer -> Day firstDayOfWeekCalendar FirstWeekType wt DayOfWeek dow Integer year = let jan1st :: Day jan1st = Integer -> WeekOfYear -> Day fromOrdinalDate Integer year WeekOfYear 1 in case FirstWeekType wt of FirstWeekType FirstWholeWeek -> DayOfWeek -> Day -> Day firstDayOfWeekOnAfter DayOfWeek dow Day jan1st FirstWeekType FirstMostWeek -> DayOfWeek -> Day -> Day firstDayOfWeekOnAfter DayOfWeek dow (Day -> Day) -> Day -> Day forall a b. (a -> b) -> a -> b $ Integer -> Day -> Day addDays (-Integer 3) Day jan1st -- | Convert to the given kind of "week calendar". -- Note that the year number matches the weeks, and so is not always the same as the Gregorian year number. toWeekCalendar :: -- | how to reckon the first week of the year FirstWeekType -> -- | the first day of each week DayOfWeek -> Day -> (Year, WeekOfYear, DayOfWeek) toWeekCalendar :: FirstWeekType -> DayOfWeek -> Day -> (Integer, WeekOfYear, DayOfWeek) toWeekCalendar FirstWeekType wt DayOfWeek ws Day d = let dw :: DayOfWeek dw = Day -> DayOfWeek dayOfWeek Day d (Integer y0, WeekOfYear _) = Day -> (Integer, WeekOfYear) toOrdinalDate Day d j1p :: Day j1p = FirstWeekType -> DayOfWeek -> Integer -> Day firstDayOfWeekCalendar FirstWeekType wt DayOfWeek ws (Integer -> Day) -> Integer -> Day forall a b. (a -> b) -> a -> b $ Integer -> Integer forall a. Enum a => a -> a pred Integer y0 j1 :: Day j1 = FirstWeekType -> DayOfWeek -> Integer -> Day firstDayOfWeekCalendar FirstWeekType wt DayOfWeek ws Integer y0 j1s :: Day j1s = FirstWeekType -> DayOfWeek -> Integer -> Day firstDayOfWeekCalendar FirstWeekType wt DayOfWeek ws (Integer -> Day) -> Integer -> Day forall a b. (a -> b) -> a -> b $ Integer -> Integer forall a. Enum a => a -> a succ Integer y0 in if Day d Day -> Day -> Bool forall a. Ord a => a -> a -> Bool < Day j1 then (Integer -> Integer forall a. Enum a => a -> a pred Integer y0, WeekOfYear -> WeekOfYear forall a. Enum a => a -> a succ (WeekOfYear -> WeekOfYear) -> WeekOfYear -> WeekOfYear forall a b. (a -> b) -> a -> b $ WeekOfYear -> WeekOfYear -> WeekOfYear forall a. Integral a => a -> a -> a div (Integer -> WeekOfYear forall a. Num a => Integer -> a fromInteger (Integer -> WeekOfYear) -> Integer -> WeekOfYear forall a b. (a -> b) -> a -> b $ Day -> Day -> Integer diffDays Day d Day j1p) WeekOfYear 7, DayOfWeek dw) else if Day d Day -> Day -> Bool forall a. Ord a => a -> a -> Bool < Day j1s then (Integer y0, WeekOfYear -> WeekOfYear forall a. Enum a => a -> a succ (WeekOfYear -> WeekOfYear) -> WeekOfYear -> WeekOfYear forall a b. (a -> b) -> a -> b $ WeekOfYear -> WeekOfYear -> WeekOfYear forall a. Integral a => a -> a -> a div (Integer -> WeekOfYear forall a. Num a => Integer -> a fromInteger (Integer -> WeekOfYear) -> Integer -> WeekOfYear forall a b. (a -> b) -> a -> b $ Day -> Day -> Integer diffDays Day d Day j1) WeekOfYear 7, DayOfWeek dw) else (Integer -> Integer forall a. Enum a => a -> a succ Integer y0, WeekOfYear -> WeekOfYear forall a. Enum a => a -> a succ (WeekOfYear -> WeekOfYear) -> WeekOfYear -> WeekOfYear forall a b. (a -> b) -> a -> b $ WeekOfYear -> WeekOfYear -> WeekOfYear forall a. Integral a => a -> a -> a div (Integer -> WeekOfYear forall a. Num a => Integer -> a fromInteger (Integer -> WeekOfYear) -> Integer -> WeekOfYear forall a b. (a -> b) -> a -> b $ Day -> Day -> Integer diffDays Day d Day j1s) WeekOfYear 7, DayOfWeek dw) -- | Convert from the given kind of "week calendar". -- Invalid week and day values will be clipped to the correct range. fromWeekCalendar :: -- | how to reckon the first week of the year FirstWeekType -> -- | the first day of each week DayOfWeek -> Year -> WeekOfYear -> DayOfWeek -> Day fromWeekCalendar :: FirstWeekType -> DayOfWeek -> Integer -> WeekOfYear -> DayOfWeek -> Day fromWeekCalendar FirstWeekType wt DayOfWeek ws Integer y WeekOfYear wy DayOfWeek dw = let d1 :: Day d1 :: Day d1 = FirstWeekType -> DayOfWeek -> Integer -> Day firstDayOfWeekCalendar FirstWeekType wt DayOfWeek ws Integer y wy' :: WeekOfYear wy' = WeekOfYear -> WeekOfYear -> WeekOfYear -> WeekOfYear forall t. Ord t => t -> t -> t -> t clip WeekOfYear 1 WeekOfYear 53 WeekOfYear wy getday :: WeekOfYear -> Day getday :: WeekOfYear -> Day getday WeekOfYear wy'' = Integer -> Day -> Day addDays (WeekOfYear -> Integer forall a. Integral a => a -> Integer toInteger (WeekOfYear -> Integer) -> WeekOfYear -> Integer forall a b. (a -> b) -> a -> b $ (WeekOfYear -> WeekOfYear forall a. Enum a => a -> a pred WeekOfYear wy'' WeekOfYear -> WeekOfYear -> WeekOfYear forall a. Num a => a -> a -> a * WeekOfYear 7) WeekOfYear -> WeekOfYear -> WeekOfYear forall a. Num a => a -> a -> a + (DayOfWeek -> DayOfWeek -> WeekOfYear dayOfWeekDiff DayOfWeek dw DayOfWeek ws)) Day d1 d1s :: Day d1s = FirstWeekType -> DayOfWeek -> Integer -> Day firstDayOfWeekCalendar FirstWeekType wt DayOfWeek ws (Integer -> Day) -> Integer -> Day forall a b. (a -> b) -> a -> b $ Integer -> Integer forall a. Enum a => a -> a succ Integer y day :: Day day = WeekOfYear -> Day getday WeekOfYear wy' in if WeekOfYear wy' WeekOfYear -> WeekOfYear -> Bool forall a. Eq a => a -> a -> Bool == WeekOfYear 53 then if Day day Day -> Day -> Bool forall a. Ord a => a -> a -> Bool >= Day d1s then WeekOfYear -> Day getday WeekOfYear 52 else Day day else Day day -- | Convert from the given kind of "week calendar". -- Invalid week and day values will return Nothing. fromWeekCalendarValid :: -- | how to reckon the first week of the year FirstWeekType -> -- | the first day of each week DayOfWeek -> Year -> WeekOfYear -> DayOfWeek -> Maybe Day fromWeekCalendarValid :: FirstWeekType -> DayOfWeek -> Integer -> WeekOfYear -> DayOfWeek -> Maybe Day fromWeekCalendarValid FirstWeekType wt DayOfWeek ws Integer y WeekOfYear wy DayOfWeek dw = let d :: Day d = FirstWeekType -> DayOfWeek -> Integer -> WeekOfYear -> DayOfWeek -> Day fromWeekCalendar FirstWeekType wt DayOfWeek ws Integer y WeekOfYear wy DayOfWeek dw in if FirstWeekType -> DayOfWeek -> Day -> (Integer, WeekOfYear, DayOfWeek) toWeekCalendar FirstWeekType wt DayOfWeek ws Day d (Integer, WeekOfYear, DayOfWeek) -> (Integer, WeekOfYear, DayOfWeek) -> Bool forall a. Eq a => a -> a -> Bool == (Integer y, WeekOfYear wy, DayOfWeek dw) then Day -> Maybe Day forall a. a -> Maybe a Just Day d else Maybe Day forall a. Maybe a Nothing -- | Convert to ISO 8601 Week Date format. First element of result is year, second week number (1-53), third day of week (1 for Monday to 7 for Sunday). -- Note that \"Week\" years are not quite the same as Gregorian years, as the first day of the year is always a Monday. -- The first week of a year is the first week to contain at least four days in the corresponding Gregorian year. toWeekDate :: Day -> (Year, WeekOfYear, Int) toWeekDate :: Day -> (Integer, WeekOfYear, WeekOfYear) toWeekDate Day d = let (Integer y, WeekOfYear wy, DayOfWeek dw) = FirstWeekType -> DayOfWeek -> Day -> (Integer, WeekOfYear, DayOfWeek) toWeekCalendar FirstWeekType FirstMostWeek DayOfWeek Monday Day d in (Integer y, WeekOfYear wy, DayOfWeek -> WeekOfYear forall a. Enum a => a -> WeekOfYear fromEnum DayOfWeek dw) -- | Convert from ISO 8601 Week Date format. First argument is year, second week number (1-52 or 53), third day of week (1 for Monday to 7 for Sunday). -- Invalid week and day values will be clipped to the correct range. fromWeekDate :: Year -> WeekOfYear -> Int -> Day fromWeekDate :: Integer -> WeekOfYear -> WeekOfYear -> Day fromWeekDate Integer y WeekOfYear wy WeekOfYear dw = FirstWeekType -> DayOfWeek -> Integer -> WeekOfYear -> DayOfWeek -> Day fromWeekCalendar FirstWeekType FirstMostWeek DayOfWeek Monday Integer y WeekOfYear wy (WeekOfYear -> DayOfWeek forall a. Enum a => WeekOfYear -> a toEnum (WeekOfYear -> DayOfWeek) -> WeekOfYear -> DayOfWeek forall a b. (a -> b) -> a -> b $ WeekOfYear -> WeekOfYear -> WeekOfYear -> WeekOfYear forall t. Ord t => t -> t -> t -> t clip WeekOfYear 1 WeekOfYear 7 WeekOfYear dw) -- | Bidirectional abstract constructor for ISO 8601 Week Date format. -- Invalid week values will be clipped to the correct range. pattern YearWeekDay :: Year -> WeekOfYear -> DayOfWeek -> Day pattern $mYearWeekDay :: forall {r}. Day -> (Integer -> WeekOfYear -> DayOfWeek -> r) -> ((# #) -> r) -> r $bYearWeekDay :: Integer -> WeekOfYear -> DayOfWeek -> Day YearWeekDay y wy dw <- (toWeekDate -> (y, wy, toEnum -> dw)) where YearWeekDay Integer y WeekOfYear wy DayOfWeek dw = Integer -> WeekOfYear -> WeekOfYear -> Day fromWeekDate Integer y WeekOfYear wy (DayOfWeek -> WeekOfYear forall a. Enum a => a -> WeekOfYear fromEnum DayOfWeek dw) {-# COMPLETE YearWeekDay #-} -- | Convert from ISO 8601 Week Date format. First argument is year, second week number (1-52 or 53), third day of week (1 for Monday to 7 for Sunday). -- Invalid week and day values will return Nothing. fromWeekDateValid :: Year -> WeekOfYear -> Int -> Maybe Day fromWeekDateValid :: Integer -> WeekOfYear -> WeekOfYear -> Maybe Day fromWeekDateValid Integer y WeekOfYear wy WeekOfYear dwr = do dw <- WeekOfYear -> WeekOfYear -> WeekOfYear -> Maybe WeekOfYear forall t. Ord t => t -> t -> t -> Maybe t clipValid WeekOfYear 1 WeekOfYear 7 WeekOfYear dwr fromWeekCalendarValid FirstMostWeek Monday y wy (toEnum dw) -- | Show in ISO 8601 Week Date format as yyyy-Www-d (e.g. \"2006-W46-3\"). showWeekDate :: Day -> String showWeekDate :: Day -> String showWeekDate Day date = (Integer -> String forall t. ShowPadded t => t -> String show4 Integer y) String -> String -> String forall a. [a] -> [a] -> [a] ++ String "-W" String -> String -> String forall a. [a] -> [a] -> [a] ++ (WeekOfYear -> String forall t. ShowPadded t => t -> String show2 WeekOfYear w) String -> String -> String forall a. [a] -> [a] -> [a] ++ String "-" String -> String -> String forall a. [a] -> [a] -> [a] ++ (WeekOfYear -> String forall a. Show a => a -> String show WeekOfYear d) where (Integer y, WeekOfYear w, WeekOfYear d) = Day -> (Integer, WeekOfYear, WeekOfYear) toWeekDate Day date