    -- |
    -- Module      : Data.Hourglass.Calendar
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- Misc calendar functions
    --
    module Data.Hourglass.Calendar
        ( isLeapYear
        , getWeekDay
        , getDayOfTheYear
        , daysInMonth
        , dateToUnixEpoch
        , dateFromUnixEpoch
        , todToSeconds
        , dateTimeToUnixEpoch
        , dateTimeFromUnixEpoch
        , dateTimeFromUnixEpochP
        ) where

    import Data.Int
    import Data.Hourglass.Types
    import Data.Hourglass.Internal

    -- | Return if this year is a leap year (366 days)
    -- or not (365 days in a year)
    isLeapYear :: Int -> Bool
    isLeapYear :: Int -> Bool
    isLeapYear Int
    year
        | Int
    year Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `mod` Int
    4 Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    0   = Bool
    False
        | Int
    year Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `mod` Int
    100 Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    0 = Bool
    True
        | Int
    year Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `mod` Int
    400 Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 = Bool
    True
        | Bool
    otherwise           = Bool
    False

    -- | Return the day of the week a specific date fall in
    getWeekDay :: Date -> WeekDay
    getWeekDay :: Date -> WeekDay
    getWeekDay Date
    date = Int -> WeekDay
    forall a. Enum a => Int -> a
    toEnum (Int -> WeekDay) -> Int -> WeekDay
    forall a b. (a -> b) -> a -> b
    $ Int64 -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int64
    d Int64 -> Int64 -> Int64
    forall a. Integral a => a -> a -> a
    `mod` Int64
    7)
      where d :: Int64
    d = Date -> Int64
    daysOfDate Date
    date

    -- | return the number of days until the beggining of the month specified for a specific year.
    daysUntilMonth :: Int -> Month -> Int
    daysUntilMonth :: Int -> Month -> Int
    daysUntilMonth Int
    y Month
    m
        | Int -> Bool
    isLeapYear Int
    y = [Int]
    leapYears [Int] -> Int -> Int
    forall a. HasCallStack => [a] -> Int -> a
    !! Month -> Int
    forall a. Enum a => a -> Int
    fromEnum Month
    m
        | Bool
    otherwise    = [Int]
    normalYears [Int] -> Int -> Int
    forall a. HasCallStack => [a] -> Int -> a
    !! Month -> Int
    forall a. Enum a => a -> Int
    fromEnum Month
    m
      where normalYears :: [Int]
    normalYears = [ Int
    0, Int
    31, Int
    59, Int
    90, Int
    120, Int
    151, Int
    181, Int
    212, Int
    243, Int
    273, Int
    304, Int
    334, Int
    365 ]
            leapYears :: [Int]
    leapYears   = [ Int
    0, Int
    31, Int
    60, Int
    91, Int
    121, Int
    152, Int
    182, Int
    213, Int
    244, Int
    274, Int
    305, Int
    335, Int
    366 ]

    -- | Return the number of days in a month.
    daysInMonth :: Int -> Month -> Int
    daysInMonth :: Int -> Month -> Int
    daysInMonth Int
    y Month
    m
        | Month
    m Month -> Month -> Bool
    forall a. Eq a => a -> a -> Bool
    == Month
    February Bool -> Bool -> Bool
    && Int -> Bool
    isLeapYear Int
    y = Int
    29
        | Bool
    otherwise                     = [Int]
    days [Int] -> Int -> Int
    forall a. HasCallStack => [a] -> Int -> a
    !! Month -> Int
    forall a. Enum a => a -> Int
    fromEnum Month
    m
      where days :: [Int]
    days = [Int
    31,Int
    28,Int
    31,Int
    30,Int
    31,Int
    30,Int
    31,Int
    31,Int
    30,Int
    31,Int
    30,Int
    31]

    -- | return the day of the year where Jan 1 is 0
    --
    -- between 0 and 364. 365 for leap years
    getDayOfTheYear :: Date -> Int
    getDayOfTheYear :: Date -> Int
    getDayOfTheYear (Date Int
    y Month
    m Int
    d) = Int -> Month -> Int
    daysUntilMonth Int
    y Month
    m Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    d

    -- | return the number of days before Jan 1st of the year
    daysBeforeYear :: Int -> Int64
    daysBeforeYear :: Int -> Int64
    daysBeforeYear Int
    year = Int64
    y Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    365 Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    + (Int64
    y Int64 -> Int64 -> Int64
    forall a. Integral a => a -> a -> a
    `div` Int64
    4) Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    - (Int64
    y Int64 -> Int64 -> Int64
    forall a. Integral a => a -> a -> a
    `div` Int64
    100) Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    + (Int64
    y Int64 -> Int64 -> Int64
    forall a. Integral a => a -> a -> a
    `div` Int64
    400)
      where y :: Int64
    y = Int -> Int64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int
    year Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1)

    -- | Return the number of day since 1 january 1
    daysOfDate :: Date -> Int64
    daysOfDate :: Date -> Int64
    daysOfDate (Date Int
    y Month
    m Int
    d) = Int -> Int64
    daysBeforeYear Int
    y Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    + Int -> Int64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int -> Month -> Int
    daysUntilMonth Int
    y Month
    m Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    d)

    -- | Return the number of days since epoch
    daysOfDateSinceEpoch :: Date -> Int64
    daysOfDateSinceEpoch :: Date -> Int64
    daysOfDateSinceEpoch Date
    date = Date -> Int64
    daysOfDate Date
    date Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    - Int64
    epochDays
      where epochDays :: Int64
    epochDays = Int64
    719163
    -- https://gitlab.haskell.org/ghc/ghc/-/issues/24700
    {-# NOINLINE daysOfDateSinceEpoch #-}

    -- | Return the number of seconds to unix epoch of a date considering hour=0,minute=0,second=0
    dateToUnixEpoch :: Date -> Elapsed
    dateToUnixEpoch :: Date -> Elapsed
    dateToUnixEpoch Date
    date = Seconds -> Elapsed
    Elapsed (Seconds -> Elapsed) -> Seconds -> Elapsed
    forall a b. (a -> b) -> a -> b
    $ Int64 -> Seconds
    Seconds (Date -> Int64
    daysOfDateSinceEpoch Date
    date Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    secondsPerDay)
      where secondsPerDay :: Int64
    secondsPerDay = Int64
    86400 -- julian day is 24h

    -- | Return the Date associated with the unix epoch
    dateFromUnixEpoch :: Elapsed -> Date
    dateFromUnixEpoch :: Elapsed -> Date
    dateFromUnixEpoch Elapsed
    e = DateTime -> Date
    dtDate (DateTime -> Date) -> DateTime -> Date
    forall a b. (a -> b) -> a -> b
    $ Elapsed -> DateTime
    dateTimeFromUnixEpoch Elapsed
    e

    -- | Return the number of seconds from a time structure
    todToSeconds :: TimeOfDay -> Seconds
    todToSeconds :: TimeOfDay -> Seconds
    todToSeconds (TimeOfDay Hours
    h Minutes
    m Seconds
    s NanoSeconds
    _) = Hours -> Seconds
    forall i. TimeInterval i => i -> Seconds
    toSeconds Hours
    h Seconds -> Seconds -> Seconds
    forall a. Num a => a -> a -> a
    + Minutes -> Seconds
    forall i. TimeInterval i => i -> Seconds
    toSeconds Minutes
    m Seconds -> Seconds -> Seconds
    forall a. Num a => a -> a -> a
    + Seconds
    s

    -- | Return the number of seconds to unix epoch of a date time
    dateTimeToUnixEpoch :: DateTime -> Elapsed
    dateTimeToUnixEpoch :: DateTime -> Elapsed
    dateTimeToUnixEpoch (DateTime Date
    d TimeOfDay
    t) = Date -> Elapsed
    dateToUnixEpoch Date
    d Elapsed -> Elapsed -> Elapsed
    forall a. Num a => a -> a -> a
    + Seconds -> Elapsed
    Elapsed (TimeOfDay -> Seconds
    todToSeconds TimeOfDay
    t)