{-# LANGUAGE Safe #-}

module Data.Time.Calendar.JulianYearDay (
    -- * Year and day format
    module Data.Time.Calendar.JulianYearDay,
) where

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

-- | Convert to proleptic Julian year and day format.
toJulianYearAndDay :: Day -> (Year, DayOfYear)
toJulianYearAndDay :: Day -> (Year, DayOfYear)
toJulianYearAndDay (ModifiedJulianDay Year
mjd) = (Year
year, DayOfYear
yd)
  where
    a :: Year
a = Year
mjd Year -> Year -> Year
forall a. Num a => a -> a -> a
+ Year
678577
    quad :: Year
quad = Year -> Year -> Year
forall a. Integral a => a -> a -> a
div Year
a Year
1461
    d :: Year
d = Year -> Year -> Year
forall a. Integral a => a -> a -> a
mod Year
a 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
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 proleptic Julian year and day format.
-- Invalid day numbers will be clipped to the correct range (1 to 365 or 366).
fromJulianYearAndDay :: Year -> DayOfYear -> Day
fromJulianYearAndDay :: Year -> DayOfYear -> Day
fromJulianYearAndDay 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
isJulianLeapYear 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
678578

-- | Convert from proleptic Julian year and day format.
-- Invalid day numbers will return Nothing
fromJulianYearAndDayValid :: Year -> DayOfYear -> Maybe Day
fromJulianYearAndDayValid :: Year -> DayOfYear -> Maybe Day
fromJulianYearAndDayValid 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
isJulianLeapYear 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
678578
    Day -> Maybe Day
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (Year -> Day
ModifiedJulianDay Year
mjd)

-- | Show in proleptic Julian year and day format (yyyy-ddd)
showJulianYearAndDay :: Day -> String
showJulianYearAndDay :: Day -> String
showJulianYearAndDay 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)
toJulianYearAndDay Day
date

-- | Is this year a leap year according to the proleptic Julian calendar?
isJulianLeapYear :: Year -> Bool
isJulianLeapYear :: Year -> Bool
isJulianLeapYear 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)