{-# LANGUAGE Safe #-}

module Data.Time.Calendar.Week (
    -- * Week
    DayOfWeek (..),
    dayOfWeek,
    dayOfWeekDiff,
    firstDayOfWeekOnAfter,
    weekAllDays,
    weekFirstDay,
    weekLastDay,
) where

import Control.DeepSeq
import Data.Data
import Data.Fixed
import Data.Ix
import Data.Time.Calendar.Days

data DayOfWeek
    = Monday
    | Tuesday
    | Wednesday
    | Thursday
    | Friday
    | Saturday
    | Sunday
    deriving (DayOfWeek -> DayOfWeek -> Bool
(DayOfWeek -> DayOfWeek -> Bool)
-> (DayOfWeek -> DayOfWeek -> Bool) -> Eq DayOfWeek
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DayOfWeek -> DayOfWeek -> Bool
== :: DayOfWeek -> DayOfWeek -> Bool
$c/= :: DayOfWeek -> DayOfWeek -> Bool
/= :: DayOfWeek -> DayOfWeek -> Bool
Eq, Int -> DayOfWeek -> ShowS
[DayOfWeek] -> ShowS
DayOfWeek -> String
(Int -> DayOfWeek -> ShowS)
-> (DayOfWeek -> String)
-> ([DayOfWeek] -> ShowS)
-> Show DayOfWeek
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DayOfWeek -> ShowS
showsPrec :: Int -> DayOfWeek -> ShowS
$cshow :: DayOfWeek -> String
show :: DayOfWeek -> String
$cshowList :: [DayOfWeek] -> ShowS
showList :: [DayOfWeek] -> ShowS
Show, ReadPrec [DayOfWeek]
ReadPrec DayOfWeek
Int -> ReadS DayOfWeek
ReadS [DayOfWeek]
(Int -> ReadS DayOfWeek)
-> ReadS [DayOfWeek]
-> ReadPrec DayOfWeek
-> ReadPrec [DayOfWeek]
-> Read DayOfWeek
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS DayOfWeek
readsPrec :: Int -> ReadS DayOfWeek
$creadList :: ReadS [DayOfWeek]
readList :: ReadS [DayOfWeek]
$creadPrec :: ReadPrec DayOfWeek
readPrec :: ReadPrec DayOfWeek
$creadListPrec :: ReadPrec [DayOfWeek]
readListPrec :: ReadPrec [DayOfWeek]
Read, Typeable DayOfWeek
Typeable DayOfWeek =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> DayOfWeek -> c DayOfWeek)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c DayOfWeek)
-> (DayOfWeek -> Constr)
-> (DayOfWeek -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c DayOfWeek))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c DayOfWeek))
-> ((forall b. Data b => b -> b) -> DayOfWeek -> DayOfWeek)
-> (forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r)
-> (forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r)
-> (forall u. (forall d. Data d => d -> u) -> DayOfWeek -> [u])
-> (forall u.
    Int -> (forall d. Data d => d -> u) -> DayOfWeek -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek)
-> Data DayOfWeek
DayOfWeek -> Constr
DayOfWeek -> DataType
(forall b. Data b => b -> b) -> DayOfWeek -> DayOfWeek
forall a.
Typeable a =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> DayOfWeek -> u
forall u. (forall d. Data d => d -> u) -> DayOfWeek -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c DayOfWeek
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> DayOfWeek -> c DayOfWeek
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c DayOfWeek)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c DayOfWeek)
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> DayOfWeek -> c DayOfWeek
gfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> DayOfWeek -> c DayOfWeek
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c DayOfWeek
gunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c DayOfWeek
$ctoConstr :: DayOfWeek -> Constr
toConstr :: DayOfWeek -> Constr
$cdataTypeOf :: DayOfWeek -> DataType
dataTypeOf :: DayOfWeek -> DataType
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c DayOfWeek)
dataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c DayOfWeek)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c DayOfWeek)
dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c DayOfWeek)
$cgmapT :: (forall b. Data b => b -> b) -> DayOfWeek -> DayOfWeek
gmapT :: (forall b. Data b => b -> b) -> DayOfWeek -> DayOfWeek
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r
gmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r
gmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> DayOfWeek -> r
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> DayOfWeek -> [u]
gmapQ :: forall u. (forall d. Data d => d -> u) -> DayOfWeek -> [u]
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> DayOfWeek -> u
gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> DayOfWeek -> u
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
gmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
gmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
gmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> DayOfWeek -> m DayOfWeek
Data, Typeable, Eq DayOfWeek
Eq DayOfWeek =>
(DayOfWeek -> DayOfWeek -> Ordering)
-> (DayOfWeek -> DayOfWeek -> Bool)
-> (DayOfWeek -> DayOfWeek -> Bool)
-> (DayOfWeek -> DayOfWeek -> Bool)
-> (DayOfWeek -> DayOfWeek -> Bool)
-> (DayOfWeek -> DayOfWeek -> DayOfWeek)
-> (DayOfWeek -> DayOfWeek -> DayOfWeek)
-> Ord DayOfWeek
DayOfWeek -> DayOfWeek -> Bool
DayOfWeek -> DayOfWeek -> Ordering
DayOfWeek -> DayOfWeek -> DayOfWeek
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: DayOfWeek -> DayOfWeek -> Ordering
compare :: DayOfWeek -> DayOfWeek -> Ordering
$c< :: DayOfWeek -> DayOfWeek -> Bool
< :: DayOfWeek -> DayOfWeek -> Bool
$c<= :: DayOfWeek -> DayOfWeek -> Bool
<= :: DayOfWeek -> DayOfWeek -> Bool
$c> :: DayOfWeek -> DayOfWeek -> Bool
> :: DayOfWeek -> DayOfWeek -> Bool
$c>= :: DayOfWeek -> DayOfWeek -> Bool
>= :: DayOfWeek -> DayOfWeek -> Bool
$cmax :: DayOfWeek -> DayOfWeek -> DayOfWeek
max :: DayOfWeek -> DayOfWeek -> DayOfWeek
$cmin :: DayOfWeek -> DayOfWeek -> DayOfWeek
min :: DayOfWeek -> DayOfWeek -> DayOfWeek
Ord, Ord DayOfWeek
Ord DayOfWeek =>
((DayOfWeek, DayOfWeek) -> [DayOfWeek])
-> ((DayOfWeek, DayOfWeek) -> DayOfWeek -> Int)
-> ((DayOfWeek, DayOfWeek) -> DayOfWeek -> Int)
-> ((DayOfWeek, DayOfWeek) -> DayOfWeek -> Bool)
-> ((DayOfWeek, DayOfWeek) -> Int)
-> ((DayOfWeek, DayOfWeek) -> Int)
-> Ix DayOfWeek
(DayOfWeek, DayOfWeek) -> Int
(DayOfWeek, DayOfWeek) -> [DayOfWeek]
(DayOfWeek, DayOfWeek) -> DayOfWeek -> Bool
(DayOfWeek, DayOfWeek) -> DayOfWeek -> Int
forall a.
Ord a =>
((a, a) -> [a])
-> ((a, a) -> a -> Int)
-> ((a, a) -> a -> Int)
-> ((a, a) -> a -> Bool)
-> ((a, a) -> Int)
-> ((a, a) -> Int)
-> Ix a
$crange :: (DayOfWeek, DayOfWeek) -> [DayOfWeek]
range :: (DayOfWeek, DayOfWeek) -> [DayOfWeek]
$cindex :: (DayOfWeek, DayOfWeek) -> DayOfWeek -> Int
index :: (DayOfWeek, DayOfWeek) -> DayOfWeek -> Int
$cunsafeIndex :: (DayOfWeek, DayOfWeek) -> DayOfWeek -> Int
unsafeIndex :: (DayOfWeek, DayOfWeek) -> DayOfWeek -> Int
$cinRange :: (DayOfWeek, DayOfWeek) -> DayOfWeek -> Bool
inRange :: (DayOfWeek, DayOfWeek) -> DayOfWeek -> Bool
$crangeSize :: (DayOfWeek, DayOfWeek) -> Int
rangeSize :: (DayOfWeek, DayOfWeek) -> Int
$cunsafeRangeSize :: (DayOfWeek, DayOfWeek) -> Int
unsafeRangeSize :: (DayOfWeek, DayOfWeek) -> Int
Ix)

instance NFData DayOfWeek where
    rnf :: DayOfWeek -> ()
rnf DayOfWeek
Monday = ()
    rnf DayOfWeek
Tuesday = ()
    rnf DayOfWeek
Wednesday = ()
    rnf DayOfWeek
Thursday = ()
    rnf DayOfWeek
Friday = ()
    rnf DayOfWeek
Saturday = ()
    rnf DayOfWeek
Sunday = ()

-- | \"Circular\", so for example @[Tuesday ..]@ gives an endless sequence.
-- Also: 'fromEnum' gives [1 .. 7] for [Monday .. Sunday], and 'toEnum' performs mod 7 to give a cycle of days.
instance Enum DayOfWeek where
    toEnum :: Int -> DayOfWeek
toEnum Int
i = case Int -> Int -> Int
forall a. Integral a => a -> a -> a
mod Int
i Int
7 of
        Int
0 -> DayOfWeek
Sunday
        Int
1 -> DayOfWeek
Monday
        Int
2 -> DayOfWeek
Tuesday
        Int
3 -> DayOfWeek
Wednesday
        Int
4 -> DayOfWeek
Thursday
        Int
5 -> DayOfWeek
Friday
        Int
_ -> DayOfWeek
Saturday
    fromEnum :: DayOfWeek -> Int
fromEnum DayOfWeek
Monday = Int
1
    fromEnum DayOfWeek
Tuesday = Int
2
    fromEnum DayOfWeek
Wednesday = Int
3
    fromEnum DayOfWeek
Thursday = Int
4
    fromEnum DayOfWeek
Friday = Int
5
    fromEnum DayOfWeek
Saturday = Int
6
    fromEnum DayOfWeek
Sunday = Int
7
    enumFromTo :: DayOfWeek -> DayOfWeek -> [DayOfWeek]
enumFromTo DayOfWeek
wd1 DayOfWeek
wd2
        | DayOfWeek
wd1 DayOfWeek -> DayOfWeek -> Bool
forall a. Eq a => a -> a -> Bool
== DayOfWeek
wd2 = [DayOfWeek
wd1]
    enumFromTo DayOfWeek
wd1 DayOfWeek
wd2 = DayOfWeek
wd1 DayOfWeek -> [DayOfWeek] -> [DayOfWeek]
forall a. a -> [a] -> [a]
: DayOfWeek -> DayOfWeek -> [DayOfWeek]
forall a. Enum a => a -> a -> [a]
enumFromTo (DayOfWeek -> DayOfWeek
forall a. Enum a => a -> a
succ DayOfWeek
wd1) DayOfWeek
wd2
    enumFromThenTo :: DayOfWeek -> DayOfWeek -> DayOfWeek -> [DayOfWeek]
enumFromThenTo DayOfWeek
wd1 DayOfWeek
wd2 DayOfWeek
wd3
        | DayOfWeek
wd2 DayOfWeek -> DayOfWeek -> Bool
forall a. Eq a => a -> a -> Bool
== DayOfWeek
wd3 = [DayOfWeek
wd1, DayOfWeek
wd2]
    enumFromThenTo DayOfWeek
wd1 DayOfWeek
wd2 DayOfWeek
wd3 = DayOfWeek
wd1 DayOfWeek -> [DayOfWeek] -> [DayOfWeek]
forall a. a -> [a] -> [a]
: DayOfWeek -> DayOfWeek -> DayOfWeek -> [DayOfWeek]
forall a. Enum a => a -> a -> a -> [a]
enumFromThenTo DayOfWeek
wd2 (Int -> DayOfWeek
forall a. Enum a => Int -> a
toEnum (Int -> DayOfWeek) -> Int -> DayOfWeek
forall a b. (a -> b) -> a -> b
$ (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* DayOfWeek -> Int
forall a. Enum a => a -> Int
fromEnum DayOfWeek
wd2) Int -> Int -> Int
forall a. Num a => a -> a -> a
- (DayOfWeek -> Int
forall a. Enum a => a -> Int
fromEnum DayOfWeek
wd1)) DayOfWeek
wd3

dayOfWeek :: Day -> DayOfWeek
dayOfWeek :: Day -> DayOfWeek
dayOfWeek (ModifiedJulianDay Integer
d) = Int -> DayOfWeek
forall a. Enum a => Int -> a
toEnum (Int -> DayOfWeek) -> Int -> DayOfWeek
forall a b. (a -> b) -> a -> b
$ Integer -> Int
forall a. Num a => Integer -> a
fromInteger (Integer -> Int) -> Integer -> Int
forall a b. (a -> b) -> a -> b
$ Integer
d Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
3

-- | @dayOfWeekDiff a b = a - b@ in range 0 to 6.
-- The number of days from b to the next a.
dayOfWeekDiff :: DayOfWeek -> DayOfWeek -> Int
dayOfWeekDiff :: DayOfWeek -> DayOfWeek -> Int
dayOfWeekDiff DayOfWeek
a DayOfWeek
b = Int -> Int -> Int
forall a. Real a => a -> a -> a
mod' (DayOfWeek -> Int
forall a. Enum a => a -> Int
fromEnum DayOfWeek
a Int -> Int -> Int
forall a. Num a => a -> a -> a
- DayOfWeek -> Int
forall a. Enum a => a -> Int
fromEnum DayOfWeek
b) Int
7

-- | The first day-of-week on or after some day
firstDayOfWeekOnAfter :: DayOfWeek -> Day -> Day
firstDayOfWeekOnAfter :: DayOfWeek -> Day -> Day
firstDayOfWeekOnAfter DayOfWeek
dw Day
d = Integer -> Day -> Day
addDays (Int -> Integer
forall a. Integral a => a -> Integer
toInteger (Int -> Integer) -> Int -> Integer
forall a b. (a -> b) -> a -> b
$ DayOfWeek -> DayOfWeek -> Int
dayOfWeekDiff DayOfWeek
dw (DayOfWeek -> Int) -> DayOfWeek -> Int
forall a b. (a -> b) -> a -> b
$ Day -> DayOfWeek
dayOfWeek Day
d) Day
d

-- | Returns a week containing the given 'Day' where the first day is the
-- 'DayOfWeek' specified.
--
-- Examples:
--
-- >>> weekAllDays Sunday (YearMonthDay 2022 02 21)
-- [YearMonthDay 2022 2 20 .. YearMonthDay 2022 2 26]
--
-- >>> weekAllDays Monday (YearMonthDay 2022 02 21)
-- [YearMonthDay 2022 2 21 .. YearMonthDay 2022 2 27]
--
-- >>> weekAllDays Tuesday (YearMonthDay 2022 02 21)
-- [YearMonthDay 2022 2 15 .. YearMonthDay 2022 2 21]
--
-- @since 1.12.2
weekAllDays :: DayOfWeek -> Day -> [Day]
weekAllDays :: DayOfWeek -> Day -> [Day]
weekAllDays DayOfWeek
firstDay Day
day = [DayOfWeek -> Day -> Day
weekFirstDay DayOfWeek
firstDay Day
day .. DayOfWeek -> Day -> Day
weekLastDay DayOfWeek
firstDay Day
day]

-- | Returns the first day of a week containing the given 'Day'.
--
-- Examples:
--
-- >>> weekFirstDay Sunday (YearMonthDay 2022 02 21)
-- YearMonthDay 2022 2 20
--
-- >>> weekFirstDay Monday (YearMonthDay 2022 02 21)
-- YearMonthDay 2022 2 21
--
-- >>> weekFirstDay Tuesday (YearMonthDay 2022 02 21)
-- YearMonthDay 2022 2 15
--
-- @since 1.12.2
weekFirstDay :: DayOfWeek -> Day -> Day
weekFirstDay :: DayOfWeek -> Day -> Day
weekFirstDay DayOfWeek
firstDay Day
day = Integer -> Day -> Day
addDays (Integer -> Integer
forall a. Num a => a -> a
negate Integer
7) (Day -> Day) -> Day -> Day
forall a b. (a -> b) -> a -> b
$ DayOfWeek -> Day -> Day
firstDayOfWeekOnAfter DayOfWeek
firstDay (Day -> Day) -> Day -> Day
forall a b. (a -> b) -> a -> b
$ Day -> Day
forall a. Enum a => a -> a
succ Day
day

-- | Returns the last day of a week containing the given 'Day'.
--
-- Examples:
--
-- >>> weekLastDay Sunday (YearMonthDay 2022 02 21)
-- YearMonthDay 2022 2 26
--
-- >>> weekLastDay Monday (YearMonthDay 2022 02 21)
-- YearMonthDay 2022 2 27
--
-- >>> weekLastDay Tuesday (YearMonthDay 2022 02 21)
-- YearMonthDay 2022 2 21
--
-- @since 1.12.2
weekLastDay :: DayOfWeek -> Day -> Day
weekLastDay :: DayOfWeek -> Day -> Day
weekLastDay DayOfWeek
firstDay Day
day = Day -> Day
forall a. Enum a => a -> a
pred (Day -> Day) -> Day -> Day
forall a b. (a -> b) -> a -> b
$ DayOfWeek -> Day -> Day
firstDayOfWeekOnAfter DayOfWeek
firstDay (Day -> Day) -> Day -> Day
forall a b. (a -> b) -> a -> b
$ Day -> Day
forall a. Enum a => a -> a
succ Day
day