    {-# LANGUAGE Safe #-}

    -- | ISO 8601 Ordinal Date format
    module Data.Time.Calendar.OrdinalDate (Day, Year, DayOfYear, WeekOfYear, module Data.Time.Calendar.OrdinalDate) where

    import Data.Time.Calendar.Days
    import Data.Time.Calendar.Private
    import Data.Time.Calendar.Types

    -- | Convert to ISO 8601 Ordinal Date format.
    toOrdinalDate :: Day -> (Year, DayOfYear)
    toOrdinalDate :: Day -> (Year, DayOfYear)
    toOrdinalDate (ModifiedJulianDay Year
    mjd) = (Year
    year, DayOfYear
    yd)
      where
        a :: Year
    a = Year
    mjd Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    678575
        quadcent :: Year
    quadcent = Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    a Year
    146097
        b :: Year
    b = Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    mod Year
    a Year
    146097
        cent :: Year
    cent = Year -> Year -> Year
    forall a. Ord a => a -> a -> a
    min (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    b Year
    36524) Year
    3
        c :: Year
    c = Year
    b Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (Year
    cent Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * Year
    36524)
        quad :: Year
    quad = Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    c Year
    1461
        d :: Year
    d = Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    mod Year
    c Year
    1461
        y :: Year
    y = Year -> Year -> Year
    forall a. Ord a => a -> a -> a
    min (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    d Year
    365) Year
    3
        yd :: DayOfYear
    yd = Year -> DayOfYear
    forall a. Num a => Year -> a
    fromInteger (Year
    d Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (Year
    y Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * Year
    365) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    1)
        year :: Year
    year = Year
    quadcent Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * Year
    400 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    cent Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * Year
    100 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    quad Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * Year
    4 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    y Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    1

    -- | Convert from ISO 8601 Ordinal Date format.
    -- Invalid day numbers will be clipped to the correct range (1 to 365 or 366).
    fromOrdinalDate :: Year -> DayOfYear -> Day
    fromOrdinalDate :: Year -> DayOfYear -> Day
    fromOrdinalDate Year
    year DayOfYear
    day = Year -> Day
    ModifiedJulianDay Year
    mjd
      where
        y :: Year
    y = Year
    year Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Year
    1
        mjd :: Year
    mjd =
            ( DayOfYear -> Year
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral
                ( DayOfYear -> DayOfYear -> DayOfYear -> DayOfYear
    forall t. Ord t => t -> t -> t -> t
    clip
                    DayOfYear
    1
                    ( if Year -> Bool
    isLeapYear Year
    year
                        then DayOfYear
    366
                        else DayOfYear
    365
                    )
                    DayOfYear
    day
                )
            )
                Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + (Year
    365 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * Year
    y)
                Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    y Year
    4)
                Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    y Year
    100)
                Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    y Year
    400)
                Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Year
    678576

    -- | Bidirectional abstract constructor for ISO 8601 Ordinal Date format.
    -- Invalid day numbers will be clipped to the correct range (1 to 365 or 366).
    pattern YearDay :: Year -> DayOfYear -> Day
    pattern $mYearDay :: forall {r}. Day -> (Year -> DayOfYear -> r) -> ((# #) -> r) -> r
    $bYearDay :: Year -> DayOfYear -> Day
    YearDay y d <-
        (toOrdinalDate -> (y, d))
        where
            YearDay Year
    y DayOfYear
    d = Year -> DayOfYear -> Day
    fromOrdinalDate Year
    y DayOfYear
    d

    {-# COMPLETE YearDay #-}

    -- | Convert from ISO 8601 Ordinal Date format.
    -- Invalid day numbers return 'Nothing'
    fromOrdinalDateValid :: Year -> DayOfYear -> Maybe Day
    fromOrdinalDateValid :: Year -> DayOfYear -> Maybe Day
    fromOrdinalDateValid Year
    year DayOfYear
    day = do
        DayOfYear
    day' <-
            DayOfYear -> DayOfYear -> DayOfYear -> Maybe DayOfYear
    forall t. Ord t => t -> t -> t -> Maybe t
    clipValid
                DayOfYear
    1
                ( if Year -> Bool
    isLeapYear Year
    year
                    then DayOfYear
    366
                    else DayOfYear
    365
                )
                DayOfYear
    day
        let
            y :: Year
    y = Year
    year Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Year
    1
            mjd :: Year
    mjd = (DayOfYear -> Year
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral DayOfYear
    day') Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + (Year
    365 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * Year
    y) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    y Year
    4) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    y Year
    100) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    y Year
    400) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Year
    678576
        Day -> Maybe Day
    forall a. a -> Maybe a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Year -> Day
    ModifiedJulianDay Year
    mjd)

    -- | Show in ISO 8601 Ordinal Date format (yyyy-ddd)
    showOrdinalDate :: Day -> String
    showOrdinalDate :: Day -> String
    showOrdinalDate Day
    date = (Year -> String
    forall t. ShowPadded t => t -> String
    show4 Year
    y) String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "-" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ (DayOfYear -> String
    forall t. ShowPadded t => t -> String
    show3 DayOfYear
    d)
      where
        (Year
    y, DayOfYear
    d) = Day -> (Year, DayOfYear)
    toOrdinalDate Day
    date

    -- | Is this year a leap year according to the proleptic Gregorian calendar?
    isLeapYear :: Year -> Bool
    isLeapYear :: Year -> Bool
    isLeapYear Year
    year = (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    mod Year
    year Year
    4 Year -> Year -> Bool
    forall a. Eq a => a -> a -> Bool
    == Year
    0) Bool -> Bool -> Bool
    && ((Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    mod Year
    year Year
    400 Year -> Year -> Bool
    forall a. Eq a => a -> a -> Bool
    == Year
    0) Bool -> Bool -> Bool
    || Bool -> Bool
    not (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    mod Year
    year Year
    100 Year -> Year -> Bool
    forall a. Eq a => a -> a -> Bool
    == Year
    0))

    -- | Get the number of the Monday-starting week in the year and the day of the week.
    -- The first Monday is the first day of week 1, any earlier days in the year are week 0 (as @%W@ in 'Data.Time.Format.formatTime').
    -- Monday is 1, Sunday is 7 (as @%u@ in 'Data.Time.Format.formatTime').
    mondayStartWeek :: Day -> (WeekOfYear, Int)
    mondayStartWeek :: Day -> (DayOfYear, DayOfYear)
    mondayStartWeek Day
    date = (Year -> DayOfYear
    forall a. Num a => Year -> a
    fromInteger ((Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    d Year
    7) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    k Year
    7)), Year -> DayOfYear
    forall a. Num a => Year -> a
    fromInteger (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    mod Year
    d Year
    7) DayOfYear -> DayOfYear -> DayOfYear
    forall a. Num a => a -> a -> a
    + DayOfYear
    1)
      where
        yd :: DayOfYear
    yd = (Year, DayOfYear) -> DayOfYear
    forall a b. (a, b) -> b
    snd (Day -> (Year, DayOfYear)
    toOrdinalDate Day
    date)
        d :: Year
    d = (Day -> Year
    toModifiedJulianDay Day
    date) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    2
        k :: Year
    k = Year
    d Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    yd)

    -- | Get the number of the Sunday-starting week in the year and the day of the week.
    -- The first Sunday is the first day of week 1, any earlier days in the year are week 0 (as @%U@ in 'Data.Time.Format.formatTime').
    -- Sunday is 0, Saturday is 6 (as @%w@ in 'Data.Time.Format.formatTime').
    sundayStartWeek :: Day -> (WeekOfYear, Int)
    sundayStartWeek :: Day -> (DayOfYear, DayOfYear)
    sundayStartWeek Day
    date = (Year -> DayOfYear
    forall a. Num a => Year -> a
    fromInteger ((Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    d Year
    7) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    div Year
    k Year
    7)), Year -> DayOfYear
    forall a. Num a => Year -> a
    fromInteger (Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    mod Year
    d Year
    7))
      where
        yd :: DayOfYear
    yd = (Year, DayOfYear) -> DayOfYear
    forall a b. (a, b) -> b
    snd (Day -> (Year, DayOfYear)
    toOrdinalDate Day
    date)
        d :: Year
    d = (Day -> Year
    toModifiedJulianDay Day
    date) Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    3
        k :: Year
    k = Year
    d Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - (DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    yd)

    -- | The inverse of 'mondayStartWeek'. Get a 'Day' given the year,
    -- the number of the Monday-starting week, and the day of the week.
    -- The first Monday is the first day of week 1, any earlier days in the year
    -- are week 0 (as @%W@ in 'Data.Time.Format.formatTime').
    fromMondayStartWeek ::
        -- | Year.
        Year ->
        -- | Monday-starting week number (as @%W@ in 'Data.Time.Format.formatTime').
        WeekOfYear ->
        -- | Day of week.
        -- Monday is 1, Sunday is 7 (as @%u@ in 'Data.Time.Format.formatTime').
        Int ->
        Day
    fromMondayStartWeek :: Year -> DayOfYear -> DayOfYear -> Day
    fromMondayStartWeek Year
    year DayOfYear
    w DayOfYear
    d = let
        -- first day of the year
        firstDay :: Day
    firstDay = Year -> DayOfYear -> Day
    fromOrdinalDate Year
    year DayOfYear
    1
        -- 0-based year day of first monday of the year
        zbFirstMonday :: Year
    zbFirstMonday = (Year
    5 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Day -> Year
    toModifiedJulianDay Day
    firstDay) Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    `mod` Year
    7
        -- 0-based week of year
        zbWeek :: DayOfYear
    zbWeek = DayOfYear
    w DayOfYear -> DayOfYear -> DayOfYear
    forall a. Num a => a -> a -> a
    - DayOfYear
    1
        -- 0-based day of week
        zbDay :: DayOfYear
    zbDay = DayOfYear
    d DayOfYear -> DayOfYear -> DayOfYear
    forall a. Num a => a -> a -> a
    - DayOfYear
    1
        -- 0-based day in year
        zbYearDay :: Year
    zbYearDay = Year
    zbFirstMonday Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    7 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbWeek Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbDay
        in Year -> Day -> Day
    addDays Year
    zbYearDay Day
    firstDay

    fromMondayStartWeekValid ::
        -- | Year.
        Year ->
        -- | Monday-starting week number (as @%W@ in 'Data.Time.Format.formatTime').
        WeekOfYear ->
        -- | Day of week.
        -- Monday is 1, Sunday is 7 (as @%u@ in 'Data.Time.Format.formatTime').
        Int ->
        Maybe Day
    fromMondayStartWeekValid :: Year -> DayOfYear -> DayOfYear -> Maybe Day
    fromMondayStartWeekValid Year
    year DayOfYear
    w DayOfYear
    d = do
        DayOfYear
    d' <- DayOfYear -> DayOfYear -> DayOfYear -> Maybe DayOfYear
    forall t. Ord t => t -> t -> t -> Maybe t
    clipValid DayOfYear
    1 DayOfYear
    7 DayOfYear
    d
        let
            -- first day of the year
            firstDay :: Day
    firstDay = Year -> DayOfYear -> Day
    fromOrdinalDate Year
    year DayOfYear
    1
            -- 0-based week of year
            zbFirstMonday :: Year
    zbFirstMonday = (Year
    5 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Day -> Year
    toModifiedJulianDay Day
    firstDay) Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    `mod` Year
    7
            -- 0-based week number
            zbWeek :: DayOfYear
    zbWeek = DayOfYear
    w DayOfYear -> DayOfYear -> DayOfYear
    forall a. Num a => a -> a -> a
    - DayOfYear
    1
            -- 0-based day of week
            zbDay :: DayOfYear
    zbDay = DayOfYear
    d' DayOfYear -> DayOfYear -> DayOfYear
    forall a. Num a => a -> a -> a
    - DayOfYear
    1
            -- 0-based day in year
            zbYearDay :: Year
    zbYearDay = Year
    zbFirstMonday Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    7 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbWeek Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbDay
        Year
    zbYearDay' <-
            Year -> Year -> Year -> Maybe Year
    forall t. Ord t => t -> t -> t -> Maybe t
    clipValid
                Year
    0
                ( if Year -> Bool
    isLeapYear Year
    year
                    then Year
    365
                    else Year
    364
                )
                Year
    zbYearDay
        Day -> Maybe Day
    forall a. a -> Maybe a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Day -> Maybe Day) -> Day -> Maybe Day
    forall a b. (a -> b) -> a -> b
    $ Year -> Day -> Day
    addDays Year
    zbYearDay' Day
    firstDay

    -- | The inverse of 'sundayStartWeek'. Get a 'Day' given the year and
    -- the number of the day of a Sunday-starting week.
    -- The first Sunday is the first day of week 1, any earlier days in the
    -- year are week 0 (as @%U@ in 'Data.Time.Format.formatTime').
    fromSundayStartWeek ::
        -- | Year.
        Year ->
        -- | Sunday-starting week number (as @%U@ in 'Data.Time.Format.formatTime').
        WeekOfYear ->
        -- | Day of week
        -- Sunday is 0, Saturday is 6 (as @%w@ in 'Data.Time.Format.formatTime').
        Int ->
        Day
    fromSundayStartWeek :: Year -> DayOfYear -> DayOfYear -> Day
    fromSundayStartWeek Year
    year DayOfYear
    w DayOfYear
    d = let
        -- first day of the year
        firstDay :: Day
    firstDay = Year -> DayOfYear -> Day
    fromOrdinalDate Year
    year DayOfYear
    1
        -- 0-based year day of first monday of the year
        zbFirstSunday :: Year
    zbFirstSunday = (Year
    4 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Day -> Year
    toModifiedJulianDay Day
    firstDay) Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    `mod` Year
    7
        -- 0-based week of year
        zbWeek :: DayOfYear
    zbWeek = DayOfYear
    w DayOfYear -> DayOfYear -> DayOfYear
    forall a. Num a => a -> a -> a
    - DayOfYear
    1
        -- 0-based day of week
        zbDay :: DayOfYear
    zbDay = DayOfYear
    d
        -- 0-based day in year
        zbYearDay :: Year
    zbYearDay = Year
    zbFirstSunday Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    7 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbWeek Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbDay
        in Year -> Day -> Day
    addDays Year
    zbYearDay Day
    firstDay

    fromSundayStartWeekValid ::
        -- | Year.
        Year ->
        -- | Sunday-starting week number (as @%U@ in 'Data.Time.Format.formatTime').
        WeekOfYear ->
        -- | Day of week.
        -- Sunday is 0, Saturday is 6 (as @%w@ in 'Data.Time.Format.formatTime').
        Int ->
        Maybe Day
    fromSundayStartWeekValid :: Year -> DayOfYear -> DayOfYear -> Maybe Day
    fromSundayStartWeekValid Year
    year DayOfYear
    w DayOfYear
    d = do
        DayOfYear
    d' <- DayOfYear -> DayOfYear -> DayOfYear -> Maybe DayOfYear
    forall t. Ord t => t -> t -> t -> Maybe t
    clipValid DayOfYear
    0 DayOfYear
    6 DayOfYear
    d
        let
            -- first day of the year
            firstDay :: Day
    firstDay = Year -> DayOfYear -> Day
    fromOrdinalDate Year
    year DayOfYear
    1
            -- 0-based week of year
            zbFirstSunday :: Year
    zbFirstSunday = (Year
    4 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    - Day -> Year
    toModifiedJulianDay Day
    firstDay) Year -> Year -> Year
    forall a. Integral a => a -> a -> a
    `mod` Year
    7
            -- 0-based week number
            zbWeek :: DayOfYear
    zbWeek = DayOfYear
    w DayOfYear -> DayOfYear -> DayOfYear
    forall a. Num a => a -> a -> a
    - DayOfYear
    1
            -- 0-based day of week
            zbDay :: DayOfYear
    zbDay = DayOfYear
    d'
            -- 0-based day in year
            zbYearDay :: Year
    zbYearDay = Year
    zbFirstSunday Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + Year
    7 Year -> Year -> Year
    forall a. Num a => a -> a -> a
    * DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbWeek Year -> Year -> Year
    forall a. Num a => a -> a -> a
    + DayOfYear -> Year
    forall a. Integral a => a -> Year
    toInteger DayOfYear
    zbDay
        Year
    zbYearDay' <-
            Year -> Year -> Year -> Maybe Year
    forall t. Ord t => t -> t -> t -> Maybe t
    clipValid
                Year
    0
                ( if Year -> Bool
    isLeapYear Year
    year
                    then Year
    365
                    else Year
    364
                )
                Year
    zbYearDay
        Day -> Maybe Day
    forall a. a -> Maybe a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Day -> Maybe Day) -> Day -> Maybe Day
    forall a b. (a -> b) -> a -> b
    $ Year -> Day -> Day
    addDays Year
    zbYearDay' Day
    firstDay