{-# LANGUAGE NumericUnderscores #-}

module DataFrame.IO.Parquet.Time where

import Data.Time
import Data.Word

import DataFrame.IO.Parquet.Binary

int96ToUTCTime :: [Word8] -> UTCTime
int96ToUTCTime :: [Word8] -> UTCTime
int96ToUTCTime [Word8]
bytes
    | [Word8] -> DayOfMonth
forall a. [a] -> DayOfMonth
forall (t :: * -> *) a. Foldable t => t a -> DayOfMonth
length [Word8]
bytes DayOfMonth -> DayOfMonth -> Bool
forall a. Eq a => a -> a -> Bool
/= DayOfMonth
12 = [Char] -> UTCTime
forall a. HasCallStack => [Char] -> a
error [Char]
"INT96 must be exactly 12 bytes"
    | Bool
otherwise =
        let ([Word8]
nanosBytes, [Word8]
julianBytes) = DayOfMonth -> [Word8] -> ([Word8], [Word8])
forall a. DayOfMonth -> [a] -> ([a], [a])
splitAt DayOfMonth
8 [Word8]
bytes
            nanosSinceMidnight :: Word64
nanosSinceMidnight = [Word8] -> Word64
littleEndianWord64 [Word8]
nanosBytes
            julianDay :: Word32
julianDay = [Word8] -> Word32
littleEndianWord32 [Word8]
julianBytes
         in Integer -> Word64 -> UTCTime
julianDayAndNanosToUTCTime (Word32 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
julianDay) Word64
nanosSinceMidnight

julianDayAndNanosToUTCTime :: Integer -> Word64 -> UTCTime
julianDayAndNanosToUTCTime :: Integer -> Word64 -> UTCTime
julianDayAndNanosToUTCTime Integer
julianDay Word64
nanosSinceMidnight =
    let day :: Day
day = Integer -> Day
julianDayToDay Integer
julianDay
        secondsSinceMidnight :: Double
secondsSinceMidnight = Word64 -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
nanosSinceMidnight Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000_000
        diffTime :: DiffTime
diffTime = Integer -> DiffTime
secondsToDiffTime (Double -> Integer
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
secondsSinceMidnight)
     in Day -> DiffTime -> UTCTime
UTCTime Day
day DiffTime
diffTime

julianDayToDay :: Integer -> Day
julianDayToDay :: Integer -> Day
julianDayToDay Integer
julianDay =
    let a :: Integer
a = Integer
julianDay Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
32_044
        b :: Integer
b = (Integer
4 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
3) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
146_097
        c :: Integer
c = Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- (Integer
146_097 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
4
        d :: Integer
d = (Integer
4 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
c Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
3) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
1461
        e :: Integer
e = Integer
c Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- (Integer
1461 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
d) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
4
        m :: Integer
m = (Integer
5 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
e Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
2) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
153
        day :: Integer
day = Integer
e Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- (Integer
153 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
m Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
2) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
5 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
1
        month :: Integer
month = Integer
m Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
3 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
12 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* (Integer
m Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
10)
        year :: Integer
year = Integer
100 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
d Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
4800 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
m Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
10
     in Integer -> DayOfMonth -> DayOfMonth -> Day
fromGregorian Integer
year (Integer -> DayOfMonth
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
month) (Integer -> DayOfMonth
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
day)

-- I include this here even though it's unused because we'll likely use
-- it for the writer. Since int96 is deprecated this is only included for completeness anyway.
utcTimeToInt96 :: UTCTime -> [Word8]
utcTimeToInt96 :: UTCTime -> [Word8]
utcTimeToInt96 (UTCTime Day
day DiffTime
diffTime) =
    let julianDay :: Integer
julianDay = Day -> Integer
dayToJulianDay Day
day
        nanosSinceMidnight :: Word64
nanosSinceMidnight = Double -> Word64
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor (DiffTime -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac DiffTime
diffTime Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
1_000_000_000)
        nanosBytes :: [Word8]
nanosBytes = Word64 -> [Word8]
word64ToLittleEndian Word64
nanosSinceMidnight
        julianBytes :: [Word8]
julianBytes = Word32 -> [Word8]
word32ToLittleEndian (Integer -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
julianDay)
     in [Word8]
nanosBytes [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Word8]
julianBytes

dayToJulianDay :: Day -> Integer
dayToJulianDay :: Day -> Integer
dayToJulianDay Day
day =
    let (Integer
year, DayOfMonth
month, DayOfMonth
dayOfMonth) = Day -> (Integer, DayOfMonth, DayOfMonth)
toGregorian Day
day
        a :: Integer
a = Integer -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Integer) -> Integer -> Integer
forall a b. (a -> b) -> a -> b
$ (Integer
14 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- DayOfMonth -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral DayOfMonth
month) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
12
        y :: Integer
y = Integer -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Integer) -> Integer -> Integer
forall a b. (a -> b) -> a -> b
$ Integer
year Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
4800 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
a
        m :: Integer
m = DayOfMonth -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (DayOfMonth -> Integer) -> DayOfMonth -> Integer
forall a b. (a -> b) -> a -> b
$ DayOfMonth
month DayOfMonth -> DayOfMonth -> DayOfMonth
forall a. Num a => a -> a -> a
+ DayOfMonth
12 DayOfMonth -> DayOfMonth -> DayOfMonth
forall a. Num a => a -> a -> a
* Integer -> DayOfMonth
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
a DayOfMonth -> DayOfMonth -> DayOfMonth
forall a. Num a => a -> a -> a
- DayOfMonth
3
     in DayOfMonth -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral DayOfMonth
dayOfMonth
            Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ (Integer
153 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
m Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
2) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
5
            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
y Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
4
            Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
y Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
100
            Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
y Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
400
            Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
32_045