{-# OPTIONS_GHC -Wno-missing-import-lists #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
module Kairos
(
readConvertTime,
readTime,
convertTime,
readTimeFormatM,
readTimeFormat,
convertZoned,
convertLocalToZoned,
Date (..),
TimeFormat (..),
TimeReader (..),
TZInput (..),
TZLabel (..),
ZonedTime (..),
ParseTimeException (..),
ParseTZInputException (..),
LocalTimeZoneException (..),
LocalTZException (..),
LocalSystemTimeException (..),
)
where
import Control.Applicative (asum)
import Control.Exception.Utils (catchSync)
import Control.Monad.Catch
( MonadCatch,
MonadThrow,
throwM,
)
import Data.List.NonEmpty (NonEmpty)
import Data.List.NonEmpty qualified as NE
import Data.String (IsString)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Time (Day)
import Data.Time.Clock (UTCTime)
import Data.Time.Format (ParseTime)
import Data.Time.Format qualified as Format
import Data.Time.LocalTime
( LocalTime (LocalTime),
TimeOfDay,
TimeZone,
ZonedTime
( ZonedTime,
zonedTimeToLocalTime,
zonedTimeZone
),
)
import Data.Time.LocalTime qualified as Local
import Data.Time.Zones (TZ)
import Data.Time.Zones qualified as Zones
import Data.Time.Zones.All (TZLabel (..))
import Data.Time.Zones.All qualified as All
import Effects.Time (MonadTime (getSystemZonedTime, getTimeZone, loadLocalTZ))
import GHC.Stack (HasCallStack)
import Kairos.Types.Date (Date (MkDateString))
import Kairos.Types.Exception
( LocalSystemTimeException (MkLocalSystemTimeException),
LocalTZException (MkLocalTZException),
LocalTimeZoneException (MkLocalTimeZoneException),
ParseTZInputException (MkParseTZInputException),
ParseTimeException (MkParseTimeException),
)
import Kairos.Types.TZInput (TZInput (TZActual, TZDatabase), locale)
import Kairos.Types.TimeFormat
( TimeFormat (MkTimeFormat, unTimeFormat),
)
import Kairos.Types.TimeReader
( TimeReader
( MkTimeReader,
date,
formats,
srcTZ,
timeString
),
)
readConvertTime ::
( HasCallStack,
MonadCatch m,
MonadTime m
) =>
Maybe TimeReader ->
Maybe TZInput ->
m ZonedTime
readConvertTime :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
Maybe TimeReader -> Maybe TZInput -> m ZonedTime
readConvertTime Maybe TimeReader
mtimeReader Maybe TZInput
destTZ =
Maybe TimeReader -> m ZonedTime
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
Maybe TimeReader -> m ZonedTime
readTime Maybe TimeReader
mtimeReader m ZonedTime -> (ZonedTime -> m ZonedTime) -> m ZonedTime
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (ZonedTime -> Maybe TZInput -> m ZonedTime
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
ZonedTime -> Maybe TZInput -> m ZonedTime
`convertTime` Maybe TZInput
destTZ)
readTime ::
( HasCallStack,
MonadCatch m,
MonadTime m
) =>
Maybe TimeReader ->
m ZonedTime
readTime :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
Maybe TimeReader -> m ZonedTime
readTime (Just TimeReader
timeReader) = TimeReader -> m ZonedTime
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
TimeReader -> m ZonedTime
readTimeString TimeReader
timeReader
readTime Maybe TimeReader
Nothing = m ZonedTime
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
m ZonedTime
getSystemZonedTimeKairos
convertTime ::
( HasCallStack,
MonadCatch m,
MonadTime m
) =>
ZonedTime ->
Maybe TZInput ->
m ZonedTime
convertTime :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
ZonedTime -> Maybe TZInput -> m ZonedTime
convertTime ZonedTime
inTime Maybe TZInput
Nothing = do
let inTimeUtc :: UTCTime
inTimeUtc = ZonedTime -> UTCTime
Local.zonedTimeToUTC ZonedTime
inTime
TimeZone
currTZ <- UTCTime -> m TimeZone
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
UTCTime -> m TimeZone
getTimeZoneKairos UTCTime
inTimeUtc
pure $ TimeZone -> UTCTime -> ZonedTime
Local.utcToZonedTime TimeZone
currTZ UTCTime
inTimeUtc
convertTime ZonedTime
inTime (Just TZInput
tzOut) = ZonedTime -> m ZonedTime
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ZonedTime -> m ZonedTime) -> ZonedTime -> m ZonedTime
forall a b. (a -> b) -> a -> b
$ ZonedTime -> TZInput -> ZonedTime
convertZoned ZonedTime
inTime TZInput
tzOut
readTimeString ::
forall m.
( HasCallStack,
MonadCatch m,
MonadTime m
) =>
TimeReader ->
m ZonedTime
readTimeString :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
TimeReader -> m ZonedTime
readTimeString TimeReader
timeReader = case (TimeReader
timeReader.date, TimeReader
timeReader.srcTZ) of
(Maybe Date
Nothing, Maybe TZInput
Nothing) -> m ZonedTime
HasCallStack => m ZonedTime
onNoInputs
(Maybe Date
Nothing, Just TZInput
srcTZ) -> HasCallStack => TZInput -> m ZonedTime
TZInput -> m ZonedTime
onSrcTZ TZInput
srcTZ
(Just Date
date, Maybe TZInput
Nothing) -> HasCallStack => Date -> m ZonedTime
Date -> m ZonedTime
onDate Date
date
(Just Date
date, Just TZInput
srcTZ) -> HasCallStack => Date -> TZInput -> m ZonedTime
Date -> TZInput -> m ZonedTime
onDateAndSrcTZ Date
date TZInput
srcTZ
where
onNoInputs :: (HasCallStack) => m ZonedTime
onNoInputs :: HasCallStack => m ZonedTime
onNoInputs = do
ZonedTime (LocalTime Day
currSysDay TimeOfDay
_) TimeZone
currSysTz <- m ZonedTime
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
m ZonedTime
getSystemZonedTimeKairos
TimeOfDay
givenTod <- NonEmpty TimeFormat -> Text -> m TimeOfDay
forall t (m :: * -> *).
(HasCallStack, MonadThrow m, ParseTime t) =>
NonEmpty TimeFormat -> Text -> m t
readTimeFormatM NonEmpty TimeFormat
formats TimeReader
timeReader.timeString
pure $ LocalTime -> TimeZone -> ZonedTime
ZonedTime (Day -> TimeOfDay -> LocalTime
LocalTime Day
currSysDay TimeOfDay
givenTod) TimeZone
currSysTz
onSrcTZ :: (HasCallStack) => TZInput -> m ZonedTime
onSrcTZ :: HasCallStack => TZInput -> m ZonedTime
onSrcTZ TZInput
src = do
ZonedTime
currSysZonedTime <- m ZonedTime
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
m ZonedTime
getSystemZonedTimeKairos
let ZonedTime (LocalTime Day
currSrcDay TimeOfDay
_) TimeZone
curSrcTz =
ZonedTime -> TZInput -> ZonedTime
convertZoned ZonedTime
currSysZonedTime TZInput
src
TimeOfDay
givenTod <- NonEmpty TimeFormat -> Text -> m TimeOfDay
forall t (m :: * -> *).
(HasCallStack, MonadThrow m, ParseTime t) =>
NonEmpty TimeFormat -> Text -> m t
readTimeFormatM NonEmpty TimeFormat
formats TimeReader
timeReader.timeString
pure $ LocalTime -> TimeZone -> ZonedTime
ZonedTime (Day -> TimeOfDay -> LocalTime
LocalTime Day
currSrcDay TimeOfDay
givenTod) TimeZone
curSrcTz
onDate :: (HasCallStack) => Date -> m ZonedTime
onDate :: HasCallStack => Date -> m ZonedTime
onDate (MkDateString Text
date) = do
Day
givenDay <- forall t (m :: * -> *).
(HasCallStack, MonadThrow m, ParseTime t) =>
NonEmpty TimeFormat -> Text -> m t
readTimeFormatM @Day NonEmpty TimeFormat
dayFmt Text
date
TimeOfDay
givenTod <- forall t (m :: * -> *).
(HasCallStack, MonadThrow m, ParseTime t) =>
NonEmpty TimeFormat -> Text -> m t
readTimeFormatM @TimeOfDay NonEmpty TimeFormat
formats TimeReader
timeReader.timeString
let givenLocalTime :: LocalTime
givenLocalTime = Day -> TimeOfDay -> LocalTime
LocalTime Day
givenDay TimeOfDay
givenTod
TZ
localTZ <- m TZ
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
m TZ
loadLocalTZKairos
let givenUtcTime :: UTCTime
givenUtcTime = TZ -> LocalTime -> UTCTime
Zones.localTimeToUTCTZ TZ
localTZ LocalTime
givenLocalTime
timeZone :: TimeZone
timeZone = TZ -> UTCTime -> TimeZone
Zones.timeZoneForUTCTime TZ
localTZ UTCTime
givenUtcTime
ZonedTime -> m ZonedTime
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ZonedTime -> m ZonedTime) -> ZonedTime -> m ZonedTime
forall a b. (a -> b) -> a -> b
$ LocalTime -> TimeZone -> ZonedTime
ZonedTime LocalTime
givenLocalTime TimeZone
timeZone
onDateAndSrcTZ :: (HasCallStack) => Date -> TZInput -> m ZonedTime
onDateAndSrcTZ :: HasCallStack => Date -> TZInput -> m ZonedTime
onDateAndSrcTZ (MkDateString Text
date) TZInput
srcTZ = do
Day
givenDay <- forall t (m :: * -> *).
(HasCallStack, MonadThrow m, ParseTime t) =>
NonEmpty TimeFormat -> Text -> m t
readTimeFormatM @Day NonEmpty TimeFormat
dayFmt Text
date
TimeOfDay
givenTod <- forall t (m :: * -> *).
(HasCallStack, MonadThrow m, ParseTime t) =>
NonEmpty TimeFormat -> Text -> m t
readTimeFormatM @TimeOfDay NonEmpty TimeFormat
formats TimeReader
timeReader.timeString
let givenLocalTime :: LocalTime
givenLocalTime = Day -> TimeOfDay -> LocalTime
LocalTime Day
givenDay TimeOfDay
givenTod
ZonedTime -> m ZonedTime
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ZonedTime -> m ZonedTime) -> ZonedTime -> m ZonedTime
forall a b. (a -> b) -> a -> b
$ LocalTime -> TZInput -> ZonedTime
convertLocalToZoned LocalTime
givenLocalTime TZInput
srcTZ
formats :: NonEmpty TimeFormat
formats :: NonEmpty TimeFormat
formats = TimeReader
timeReader.formats
dayFmt :: NonEmpty TimeFormat
dayFmt :: NonEmpty TimeFormat
dayFmt = TimeFormat -> NonEmpty TimeFormat
forall a. a -> NonEmpty a
NE.singleton TimeFormat
forall s. IsString s => s
dateString
dateString :: (IsString s) => s
dateString :: forall s. IsString s => s
dateString = s
"%Y-%m-%d"
readTimeFormatM ::
forall t m.
( HasCallStack,
MonadThrow m,
ParseTime t
) =>
NonEmpty TimeFormat ->
Text ->
m t
readTimeFormatM :: forall t (m :: * -> *).
(HasCallStack, MonadThrow m, ParseTime t) =>
NonEmpty TimeFormat -> Text -> m t
readTimeFormatM NonEmpty TimeFormat
formats Text
timeStr = case NonEmpty TimeFormat -> Text -> Maybe t
forall t. ParseTime t => NonEmpty TimeFormat -> Text -> Maybe t
readTimeFormat NonEmpty TimeFormat
formats Text
timeStr of
Maybe t
Nothing -> ParseTimeException -> m t
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (ParseTimeException -> m t) -> ParseTimeException -> m t
forall a b. (a -> b) -> a -> b
$ NonEmpty TimeFormat -> Text -> ParseTimeException
MkParseTimeException NonEmpty TimeFormat
formats Text
timeStr
Just t
t -> t -> m t
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure t
t
readTimeFormat :: (ParseTime t) => NonEmpty TimeFormat -> Text -> Maybe t
readTimeFormat :: forall t. ParseTime t => NonEmpty TimeFormat -> Text -> Maybe t
readTimeFormat NonEmpty TimeFormat
formats Text
timeStr =
NonEmpty (Maybe t) -> Maybe t
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum (String -> Maybe t
parseFn (String -> Maybe t)
-> (TimeFormat -> String) -> TimeFormat -> Maybe t
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeFormat -> String
toFmtStr (TimeFormat -> Maybe t)
-> NonEmpty TimeFormat -> NonEmpty (Maybe t)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty TimeFormat
formats)
where
parseFn :: String -> Maybe t
parseFn String
f = Bool -> TimeLocale -> String -> String -> Maybe t
forall (m :: * -> *) t.
(MonadFail m, ParseTime t) =>
Bool -> TimeLocale -> String -> String -> m t
Format.parseTimeM Bool
True TimeLocale
locale String
f String
timeStr'
toFmtStr :: TimeFormat -> String
toFmtStr :: TimeFormat -> String
toFmtStr = Text -> String
T.unpack (Text -> String) -> (TimeFormat -> Text) -> TimeFormat -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (.unTimeFormat)
timeStr' :: String
timeStr' = Text -> String
T.unpack Text
timeStr
convertLocalToZoned :: LocalTime -> TZInput -> ZonedTime
convertLocalToZoned :: LocalTime -> TZInput -> ZonedTime
convertLocalToZoned LocalTime
localTime TZInput
tzInput =
case TZInput
tzInput of
TZDatabase TZLabel
label -> (TZ -> LocalTime -> UTCTime) -> LocalTime -> TZLabel -> ZonedTime
forall a. (TZ -> a -> UTCTime) -> a -> TZLabel -> ZonedTime
convertFromLabel TZ -> LocalTime -> UTCTime
Zones.localTimeToUTCTZ LocalTime
localTime TZLabel
label
TZActual TimeZone
timeZone -> LocalTime -> TimeZone -> ZonedTime
ZonedTime LocalTime
localTime TimeZone
timeZone
convertZoned :: ZonedTime -> TZInput -> ZonedTime
convertZoned :: ZonedTime -> TZInput -> ZonedTime
convertZoned ZonedTime
zonedTime TZInput
tzInput =
case TZInput
tzInput of
TZDatabase TZLabel
label -> (TZ -> ZonedTime -> UTCTime) -> ZonedTime -> TZLabel -> ZonedTime
forall a. (TZ -> a -> UTCTime) -> a -> TZLabel -> ZonedTime
convertFromLabel TZ -> ZonedTime -> UTCTime
forall a. a -> ZonedTime -> UTCTime
f ZonedTime
zonedTime TZLabel
label
TZActual TimeZone
timeZone -> ZonedTime -> TimeZone -> ZonedTime
convertFromActual ZonedTime
zonedTime TimeZone
timeZone
where
f :: forall a. a -> ZonedTime -> UTCTime
f :: forall a. a -> ZonedTime -> UTCTime
f = (ZonedTime -> UTCTime) -> a -> ZonedTime -> UTCTime
forall a b. a -> b -> a
const ZonedTime -> UTCTime
Local.zonedTimeToUTC
convertFromActual :: ZonedTime -> TimeZone -> ZonedTime
convertFromActual :: ZonedTime -> TimeZone -> ZonedTime
convertFromActual ZonedTime
zt TimeZone
timeZone = TimeZone -> UTCTime -> ZonedTime
Local.utcToZonedTime TimeZone
timeZone UTCTime
utcTime
where
utcTime :: UTCTime
utcTime = ZonedTime -> UTCTime
Local.zonedTimeToUTC ZonedTime
zt
convertFromLabel :: (TZ -> a -> UTCTime) -> a -> TZLabel -> ZonedTime
convertFromLabel :: forall a. (TZ -> a -> UTCTime) -> a -> TZLabel -> ZonedTime
convertFromLabel TZ -> a -> UTCTime
toUtcTime a
t TZLabel
tzLabel = TimeZone -> UTCTime -> ZonedTime
Local.utcToZonedTime TimeZone
timeZone UTCTime
utcTime
where
tz :: TZ
tz = TZLabel -> TZ
All.tzByLabel TZLabel
tzLabel
utcTime :: UTCTime
utcTime = TZ -> a -> UTCTime
toUtcTime TZ
tz a
t
timeZone :: TimeZone
timeZone = TZ -> UTCTime -> TimeZone
Zones.timeZoneForUTCTime TZ
tz UTCTime
utcTime
getSystemZonedTimeKairos ::
( HasCallStack,
MonadCatch m,
MonadTime m
) =>
m ZonedTime
getSystemZonedTimeKairos :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
m ZonedTime
getSystemZonedTimeKairos =
m ZonedTime
forall (m :: * -> *). (MonadTime m, HasCallStack) => m ZonedTime
getSystemZonedTime m ZonedTime -> (SomeException -> m ZonedTime) -> m ZonedTime
forall (m :: * -> *) a.
(HasCallStack, MonadCatch m) =>
m a -> (SomeException -> m a) -> m a
`catchSync` (LocalSystemTimeException -> m ZonedTime
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (LocalSystemTimeException -> m ZonedTime)
-> (SomeException -> LocalSystemTimeException)
-> SomeException
-> m ZonedTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> LocalSystemTimeException
forall e. Exception e => e -> LocalSystemTimeException
MkLocalSystemTimeException)
getTimeZoneKairos ::
( HasCallStack,
MonadCatch m,
MonadTime m
) =>
UTCTime ->
m TimeZone
getTimeZoneKairos :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
UTCTime -> m TimeZone
getTimeZoneKairos UTCTime
utc =
UTCTime -> m TimeZone
forall (m :: * -> *).
(MonadTime m, HasCallStack) =>
UTCTime -> m TimeZone
getTimeZone UTCTime
utc m TimeZone -> (SomeException -> m TimeZone) -> m TimeZone
forall (m :: * -> *) a.
(HasCallStack, MonadCatch m) =>
m a -> (SomeException -> m a) -> m a
`catchSync` (LocalTimeZoneException -> m TimeZone
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (LocalTimeZoneException -> m TimeZone)
-> (SomeException -> LocalTimeZoneException)
-> SomeException
-> m TimeZone
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> LocalTimeZoneException
forall e. Exception e => e -> LocalTimeZoneException
MkLocalTimeZoneException)
loadLocalTZKairos :: (HasCallStack, MonadCatch m, MonadTime m) => m TZ
loadLocalTZKairos :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadTime m) =>
m TZ
loadLocalTZKairos =
m TZ
forall (m :: * -> *). (MonadTime m, HasCallStack) => m TZ
loadLocalTZ m TZ -> (SomeException -> m TZ) -> m TZ
forall (m :: * -> *) a.
(HasCallStack, MonadCatch m) =>
m a -> (SomeException -> m a) -> m a
`catchSync` (LocalTZException -> m TZ
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (LocalTZException -> m TZ)
-> (SomeException -> LocalTZException) -> SomeException -> m TZ
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> LocalTZException
forall e. Exception e => e -> LocalTZException
MkLocalTZException)