{-# 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.Types import Data.Time.Calendar.Days import Data.Time.Calendar.Week import Data.Time.Calendar.OrdinalDate import Data.Time.Calendar.Private data FirstWeekType = FirstWholeWeek -- ^ first week is the first whole week of the year | FirstMostWeek -- ^ first week is the first week with four days in the year deriving FirstWeekType -> FirstWeekType -> Bool forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a /= :: FirstWeekType -> FirstWeekType -> Bool $c/= :: FirstWeekType -> FirstWeekType -> Bool == :: FirstWeekType -> FirstWeekType -> Bool $c== :: 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 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 :: FirstWeekType -- ^ how to reckon the first week of the year -> DayOfWeek -- ^ the first day of each week -> 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 forall a b. (a -> b) -> a -> b $ 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 forall a b. (a -> b) -> a -> b $ forall a. Enum a => a -> a succ Integer y0 in if Day d forall a. Ord a => a -> a -> Bool < Day j1 then (forall a. Enum a => a -> a pred Integer y0,forall a. Enum a => a -> a succ forall a b. (a -> b) -> a -> b $ forall a. Integral a => a -> a -> a div (forall a. Num a => Integer -> a fromInteger forall a b. (a -> b) -> a -> b $ Day -> Day -> Integer diffDays Day d Day j1p) WeekOfYear 7,DayOfWeek dw) else if Day d forall a. Ord a => a -> a -> Bool < Day j1s then (Integer y0,forall a. Enum a => a -> a succ forall a b. (a -> b) -> a -> b $ forall a. Integral a => a -> a -> a div (forall a. Num a => Integer -> a fromInteger forall a b. (a -> b) -> a -> b $ Day -> Day -> Integer diffDays Day d Day j1) WeekOfYear 7,DayOfWeek dw) else (forall a. Enum a => a -> a succ Integer y0,forall a. Enum a => a -> a succ forall a b. (a -> b) -> a -> b $ forall a. Integral a => a -> a -> a div (forall a. Num a => Integer -> a fromInteger 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 :: FirstWeekType -- ^ how to reckon the first week of the year -> DayOfWeek -- ^ the first day of each week -> 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' = 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 (forall a. Integral a => a -> Integer toInteger forall a b. (a -> b) -> a -> b $ (forall a. Enum a => a -> a pred WeekOfYear wy'' forall a. Num a => a -> a -> a * WeekOfYear 7) 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 forall a b. (a -> b) -> a -> b $ forall a. Enum a => a -> a succ Integer y day :: Day day = WeekOfYear -> Day getday WeekOfYear wy' in if WeekOfYear wy' forall a. Eq a => a -> a -> Bool == WeekOfYear 53 then if Day day 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 :: FirstWeekType -- ^ how to reckon the first week of the year -> DayOfWeek -- ^ the first day of each week -> 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 forall a. Eq a => a -> a -> Bool == (Integer y,WeekOfYear wy,DayOfWeek dw) then forall a. a -> Maybe a Just Day d else 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,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 (forall a. Enum a => WeekOfYear -> a toEnum forall a b. (a -> b) -> a -> b $ 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 (forall a. Enum a => a -> WeekOfYear fromEnum DayOfWeek dw) #if __GLASGOW_HASKELL__ >= 802 {-# COMPLETE YearWeekDay #-} #endif -- | 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 WeekOfYear dw <- forall t. Ord t => t -> t -> t -> Maybe t clipValid WeekOfYear 1 WeekOfYear 7 WeekOfYear dwr FirstWeekType -> DayOfWeek -> Integer -> WeekOfYear -> DayOfWeek -> Maybe Day fromWeekCalendarValid FirstWeekType FirstMostWeek DayOfWeek Monday Integer y WeekOfYear wy (forall a. Enum a => WeekOfYear -> a toEnum WeekOfYear 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 = (forall t. ShowPadded t => t -> String show4 Integer y) forall a. [a] -> [a] -> [a] ++ String "-W" forall a. [a] -> [a] -> [a] ++ (forall t. ShowPadded t => t -> String show2 WeekOfYear w) forall a. [a] -> [a] -> [a] ++ String "-" forall a. [a] -> [a] -> [a] ++ (forall a. Show a => a -> String show WeekOfYear d) where (Integer y, WeekOfYear w, WeekOfYear d) = Day -> (Integer, WeekOfYear, WeekOfYear) toWeekDate Day date