09154c71eaac2555cc2490e161e3fd0a3751456d
[packages/time.git] / lib / Data / Time / Calendar / WeekDate.hs
1 -- | ISO 8601 Week Date format
2 module Data.Time.Calendar.WeekDate where
3
4 import Data.Time.Calendar.OrdinalDate
5 import Data.Time.Calendar.Days
6 import Data.Time.Calendar.Private
7
8 -- | 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).
9 -- Note that \"Week\" years are not quite the same as Gregorian years, as the first day of the year is always a Monday.
10 -- The first week of a year is the first week to contain at least four days in the corresponding Gregorian year.
11 toWeekDate :: Day -> (Integer,Int,Int)
12 toWeekDate date@(ModifiedJulianDay mjd) = (y1,fromInteger (w1 + 1),fromInteger d_mod_7 + 1) where
13 (d_div_7, d_mod_7) = d `divMod` 7
14 (y0,yd) = toOrdinalDate date
15 d = mjd + 2
16 foo :: Integer -> Integer
17 foo y = bar (toModifiedJulianDay (fromOrdinalDate y 6))
18 bar k = d_div_7 - k `div` 7
19 (y1,w1) = case bar (d - toInteger yd + 4) of
20 -1 -> (y0 - 1, foo (y0 - 1))
21 52 -> if foo (y0 + 1) == 0
22 then (y0 + 1, 0)
23 else (y0, 52)
24 w0 -> (y0, w0)
25
26 -- | 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).
27 -- Invalid week and day values will be clipped to the correct range.
28 fromWeekDate :: Integer -> Int -> Int -> Day
29 fromWeekDate y w d = ModifiedJulianDay (k - (mod k 7) + (toInteger (((clip 1 (if longYear then 53 else 52) w) * 7) + (clip 1 7 d))) - 10) where
30 k = toModifiedJulianDay (fromOrdinalDate y 6)
31 longYear = case toWeekDate (fromOrdinalDate y 365) of
32 (_,53,_) -> True
33 _ -> False
34
35 -- | 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).
36 -- Invalid week and day values will return Nothing.
37 fromWeekDateValid :: Integer -> Int -> Int -> Maybe Day
38 fromWeekDateValid y w d = do
39 d' <- clipValid 1 7 d
40 let
41 longYear = case toWeekDate (fromOrdinalDate y 365) of
42 (_,53,_) -> True
43 _ -> False
44 w' <- clipValid 1 (if longYear then 53 else 52) w
45 let
46 k = toModifiedJulianDay (fromOrdinalDate y 6)
47 return (ModifiedJulianDay (k - (mod k 7) + (toInteger ((w' * 7) + d')) - 10))
48
49 -- | Show in ISO 8601 Week Date format as yyyy-Www-d (e.g. \"2006-W46-3\").
50 showWeekDate :: Day -> String
51 showWeekDate date = (show4 y) ++ "-W" ++ (show2 w) ++ "-" ++ (show d) where
52 (y,w,d) = toWeekDate date