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

-- | Convert to proleptic Julian year and day format. First element of result is year (proleptic Julian calendar),
-- second is the day of the year, with 1 for Jan 1, and 365 (or 366 in leap years) for Dec 31.
toJulianYearAndDay :: Day -> (Integer,Int)
toJulianYearAndDay :: Day -> (Integer, Int)
toJulianYearAndDay (ModifiedJulianDay Integer
mjd) = (Integer
year,Int
yd) where
    a :: Integer
a = Integer
mjd Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
678577
    quad :: Integer
quad = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
div Integer
a Integer
1461
    d :: Integer
d = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
mod Integer
a Integer
1461
    y :: Integer
y = Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
min (Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
div Integer
d Integer
365) Integer
3
    yd :: Int
yd = Integer -> Int
forall a. Num a => Integer -> a
fromInteger (Integer
d Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- (Integer
y Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
365) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
1)
    year :: Integer
year = Integer
quad Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
4 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
y Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
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 :: Integer -> Int -> Day
fromJulianYearAndDay :: Integer -> Int -> Day
fromJulianYearAndDay Integer
year Int
day = Integer -> Day
ModifiedJulianDay Integer
mjd where
    y :: Integer
y = Integer
year Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1
    mjd :: Integer
mjd = (Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int -> Int -> Int
forall t. Ord t => t -> t -> t -> t
clip Int
1 (if Integer -> Bool
isJulianLeapYear Integer
year then Int
366 else Int
365) Int
day)) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ (Integer
365 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
y) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ (Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
div Integer
y Integer
4) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
678578

-- | Convert from proleptic Julian year and day format.
-- Invalid day numbers will return Nothing
fromJulianYearAndDayValid :: Integer -> Int -> Maybe Day
fromJulianYearAndDayValid :: Integer -> Int -> Maybe Day
fromJulianYearAndDayValid Integer
year Int
day = do
    Int
day' <- Int -> Int -> Int -> Maybe Int
forall t. Ord t => t -> t -> t -> Maybe t
clipValid Int
1 (if Integer -> Bool
isJulianLeapYear Integer
year then Int
366 else Int
365) Int
day
    let
        y :: Integer
y = Integer
year Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1
        mjd :: Integer
mjd = (Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
day') Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ (Integer
365 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
y) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ (Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
div Integer
y Integer
4) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
678578
    Day -> Maybe Day
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Day
ModifiedJulianDay Integer
mjd)

-- | Show in proleptic Julian year and day format (yyyy-ddd)
showJulianYearAndDay :: Day -> String
showJulianYearAndDay :: Day -> String
showJulianYearAndDay Day
date = (Integer -> String
forall t. ShowPadded t => t -> String
show4 Integer
y) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"-" String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Int -> String
forall t. ShowPadded t => t -> String
show3 Int
d) where
    (Integer
y,Int
d) = Day -> (Integer, Int)
toJulianYearAndDay Day
date

-- | Is this year a leap year according to the proleptic Julian calendar?
isJulianLeapYear :: Integer -> Bool
isJulianLeapYear :: Integer -> Bool
isJulianLeapYear Integer
year = (Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
mod Integer
year Integer
4 Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
0)