{-# LANGUAGE NamedFieldPuns #-}

module Canteven.ParseDate.Types (
  LocalTimeAndOffset(..),
  ltaoInTimeZone,
  ltaoInTimeZoneTZ
) where

import Data.Time (UTCTime, ParseTime (buildTime))
import Data.Time.LocalTime (LocalTime, TimeZone, localTimeToUTC)
import Data.Time.LocalTime.TimeZone.Series (TimeZoneSeries, utcToLocalTime')
import Data.Time.Zones (TZ, utcToLocalTimeTZ)


-- | A data type for parsing ISO8601 and distinguishing between with-offset or
-- without-offset formats.
data LocalTimeAndOffset = LocalTimeAndOffset {
      ltaoLocalTime :: LocalTime
    , ltaoOffset    :: Maybe TimeZone
    } deriving (Read, Show, Eq)

instance ParseTime LocalTimeAndOffset where
    buildTime l xs =
        LocalTimeAndOffset (buildTime l $ filter (not . isZoneInfo) xs)
            maybeZone
      where
        -- Apparently "%Z" matches even the empty string and parses it as 00:00.
        -- "%z" might do this too but I haven't tested it.
        isZoneInfo ('z',_) = True
        isZoneInfo ('Z',"") = False
        isZoneInfo ('Z',_) = True
        isZoneInfo _ = False
        zoneLetters = filter isZoneInfo xs
        maybeZone | null zoneLetters = Nothing
                  | otherwise = Just $ buildTime l zoneLetters

-- | Get the localtime represented by this LTAO in the given timezone.
ltaoInTimeZone :: TimeZoneSeries -> LocalTimeAndOffset -> LocalTime
ltaoInTimeZone = ltaoInTimeZoneConvert utcToLocalTime'

-- | same as @ltaoInTimeZone@ but for TZ
ltaoInTimeZoneTZ :: TZ -> LocalTimeAndOffset -> LocalTime
ltaoInTimeZoneTZ = ltaoInTimeZoneConvert utcToLocalTimeTZ



ltaoInTimeZoneConvert :: (a -> UTCTime -> LocalTime) -> a -> LocalTimeAndOffset -> LocalTime
-- If the LTAO doesn't specify an offset, it must be local time, so just use it
-- directly.
ltaoInTimeZoneConvert _ _ (LocalTimeAndOffset lTime Nothing) = lTime
-- Otherwise, this LTAO specifies a UTCTime, so convert that UTCTime to LocalTime.
ltaoInTimeZoneConvert convert tz (LocalTimeAndOffset lTime (Just off)) =
    convert tz utc
  where
    utc = localTimeToUTC off lTime