{-# LANGUAGE Safe #-}

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

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

-- | 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 forall a. Num a => a -> a -> a
+ Year
678577
    quad :: Year
quad = forall a. Integral a => a -> a -> a
div Year
a Year
1461
    d :: Year
d = forall a. Integral a => a -> a -> a
mod Year
a Year
1461
    y :: Year
y = forall a. Ord a => a -> a -> a
min (forall a. Integral a => a -> a -> a
div Year
d Year
365) Year
3
    yd :: DayOfYear
yd = forall a. Num a => Year -> a
fromInteger (Year
d forall a. Num a => a -> a -> a
- (Year
y forall a. Num a => a -> a -> a
* Year
365) forall a. Num a => a -> a -> a
+ Year
1)
    year :: Year
year = Year
quad forall a. Num a => a -> a -> a
* Year
4 forall a. Num a => a -> a -> a
+ Year
y 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 forall a. Num a => a -> a -> a
- Year
1
    mjd :: Year
mjd =
        (forall a b. (Integral a, Num b) => a -> b
fromIntegral
             (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)) forall a. Num a => a -> a -> a
+
        (Year
365 forall a. Num a => a -> a -> a
* Year
y) forall a. Num a => a -> a -> a
+
        (forall a. Integral a => a -> a -> a
div Year
y Year
4) 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' <-
        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 forall a. Num a => a -> a -> a
- Year
1
        mjd :: Year
mjd = (forall a b. (Integral a, Num b) => a -> b
fromIntegral DayOfYear
day') forall a. Num a => a -> a -> a
+ (Year
365 forall a. Num a => a -> a -> a
* Year
y) forall a. Num a => a -> a -> a
+ (forall a. Integral a => a -> a -> a
div Year
y Year
4) forall a. Num a => a -> a -> a
- Year
678578
    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 = (forall t. ShowPadded t => t -> String
show4 Year
y) forall a. [a] -> [a] -> [a]
++ String
"-" forall a. [a] -> [a] -> [a]
++ (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 = (forall a. Integral a => a -> a -> a
mod Year
year Year
4 forall a. Eq a => a -> a -> Bool
== Year
0)