{-# LANGUAGE UndecidableInstances #-}

-- | @since 0.1
module Kairos.Types.TimeFormat
  ( TimeFormat (..),
    defaultTimeFormats,
    hm,
    hm12h,
    hmTZ,
    hmTZ12h,
    rfc822,
  )
where

import Control.DeepSeq (NFData)
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.String (IsString)
import Data.Text (Text)
import GHC.Generics (Generic)
import Optics.Core (An_Iso, LabelOptic (labelOptic), iso)

-- | Time formatting string. The 'Monoid' instance behaves like 'Text'.
--
-- @since 0.1
newtype TimeFormat = MkTimeFormat
  {TimeFormat -> Text
unTimeFormat :: Text}
  deriving stock
    ( -- | @since 0.1
      TimeFormat -> TimeFormat -> Bool
(TimeFormat -> TimeFormat -> Bool)
-> (TimeFormat -> TimeFormat -> Bool) -> Eq TimeFormat
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TimeFormat -> TimeFormat -> Bool
== :: TimeFormat -> TimeFormat -> Bool
$c/= :: TimeFormat -> TimeFormat -> Bool
/= :: TimeFormat -> TimeFormat -> Bool
Eq,
      -- | @since 0.1
      (forall x. TimeFormat -> Rep TimeFormat x)
-> (forall x. Rep TimeFormat x -> TimeFormat) -> Generic TimeFormat
forall x. Rep TimeFormat x -> TimeFormat
forall x. TimeFormat -> Rep TimeFormat x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. TimeFormat -> Rep TimeFormat x
from :: forall x. TimeFormat -> Rep TimeFormat x
$cto :: forall x. Rep TimeFormat x -> TimeFormat
to :: forall x. Rep TimeFormat x -> TimeFormat
Generic,
      -- | @since 0.1
      Int -> TimeFormat -> ShowS
[TimeFormat] -> ShowS
TimeFormat -> String
(Int -> TimeFormat -> ShowS)
-> (TimeFormat -> String)
-> ([TimeFormat] -> ShowS)
-> Show TimeFormat
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TimeFormat -> ShowS
showsPrec :: Int -> TimeFormat -> ShowS
$cshow :: TimeFormat -> String
show :: TimeFormat -> String
$cshowList :: [TimeFormat] -> ShowS
showList :: [TimeFormat] -> ShowS
Show
    )
  deriving anyclass
    ( -- | @since 0.1
      TimeFormat -> ()
(TimeFormat -> ()) -> NFData TimeFormat
forall a. (a -> ()) -> NFData a
$crnf :: TimeFormat -> ()
rnf :: TimeFormat -> ()
NFData
    )
  deriving
    ( -- | @since 0.1
      Semigroup TimeFormat
TimeFormat
Semigroup TimeFormat =>
TimeFormat
-> (TimeFormat -> TimeFormat -> TimeFormat)
-> ([TimeFormat] -> TimeFormat)
-> Monoid TimeFormat
[TimeFormat] -> TimeFormat
TimeFormat -> TimeFormat -> TimeFormat
forall a.
Semigroup a =>
a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
$cmempty :: TimeFormat
mempty :: TimeFormat
$cmappend :: TimeFormat -> TimeFormat -> TimeFormat
mappend :: TimeFormat -> TimeFormat -> TimeFormat
$cmconcat :: [TimeFormat] -> TimeFormat
mconcat :: [TimeFormat] -> TimeFormat
Monoid,
      -- | @since 0.1
      String -> TimeFormat
(String -> TimeFormat) -> IsString TimeFormat
forall a. (String -> a) -> IsString a
$cfromString :: String -> TimeFormat
fromString :: String -> TimeFormat
IsString,
      -- | @since 0.1
      NonEmpty TimeFormat -> TimeFormat
TimeFormat -> TimeFormat -> TimeFormat
(TimeFormat -> TimeFormat -> TimeFormat)
-> (NonEmpty TimeFormat -> TimeFormat)
-> (forall b. Integral b => b -> TimeFormat -> TimeFormat)
-> Semigroup TimeFormat
forall b. Integral b => b -> TimeFormat -> TimeFormat
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
$c<> :: TimeFormat -> TimeFormat -> TimeFormat
<> :: TimeFormat -> TimeFormat -> TimeFormat
$csconcat :: NonEmpty TimeFormat -> TimeFormat
sconcat :: NonEmpty TimeFormat -> TimeFormat
$cstimes :: forall b. Integral b => b -> TimeFormat -> TimeFormat
stimes :: forall b. Integral b => b -> TimeFormat -> TimeFormat
Semigroup
    )
    via Text

-- | @since 0.1
instance
  (k ~ An_Iso, a ~ Text, b ~ Text) =>
  LabelOptic "unTimeFormat" k TimeFormat TimeFormat a b
  where
  labelOptic :: Optic k NoIx TimeFormat TimeFormat a b
labelOptic = (TimeFormat -> a)
-> (b -> TimeFormat) -> Iso TimeFormat TimeFormat a b
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso (\(MkTimeFormat Text
f) -> a
Text
f) b -> TimeFormat
Text -> TimeFormat
MkTimeFormat
  {-# INLINE labelOptic #-}

-- | Parses 24 hour time w/ and w/o colon (13:00, 1300) and 12 hour
-- time w/ and w/o passing (1:30 pm, 1 pm, 10:15 am).
--
-- @since 0.1
defaultTimeFormats :: NonEmpty TimeFormat
defaultTimeFormats :: NonEmpty TimeFormat
defaultTimeFormats =
  TimeFormat
hm
    TimeFormat -> [TimeFormat] -> NonEmpty TimeFormat
forall a. a -> [a] -> NonEmpty a
:| [ TimeFormat
hmNoColon, -- 0300
         TimeFormat
"%-I:%M %P", -- 1:30 pm,
         TimeFormat
"%-I:%M%P", -- 1:30pm
         TimeFormat
"%-I %P", -- 1pm
         TimeFormat
"%-I%P" -- 1 pm
       ]

-- | Format for 24-hour @hours:minutes@.
--
-- @since 0.1
hm :: TimeFormat
hm :: TimeFormat
hm = TimeFormat
"%H:%M"
{-# INLINE hm #-}

-- | Format for 24-hour @hoursminutes@ (no colon).
--
-- @since 0.1
hmNoColon :: TimeFormat
hmNoColon :: TimeFormat
hmNoColon = TimeFormat
"%H%M"
{-# INLINE hmNoColon #-}

-- | Format for 12-hour @hours:minutes am/pm@.
--
-- @since 0.1
hm12h :: TimeFormat
hm12h :: TimeFormat
hm12h = TimeFormat
"%I:%M %P"
{-# INLINE hm12h #-}

-- | Format for 24-hour @hours:minutes TZ@. As this contains a timezone
-- flag, it should be used for formatting output only. In particular, it should
-- __not__ be used with 'Kairos.Types.TimeReader.format'.
--
-- @since 0.1
hmTZ :: TimeFormat
hmTZ :: TimeFormat
hmTZ = TimeFormat
"%H:%M %Z"
{-# INLINE hmTZ #-}

-- | Format for 12-hour @hours:minutes am/pm TZ@. As this contains a timezone
-- flag, it should be used for formatting output only. In particular, it should
-- __not__ be used with 'Kairos.Types.TimeReader.format'.
--
-- @since 0.1
hmTZ12h :: TimeFormat
hmTZ12h :: TimeFormat
hmTZ12h = TimeFormat
"%I:%M %P %Z"
{-# INLINE hmTZ12h #-}

-- | Format for RFC822: @%a, %_d %b %Y %H:%M:%S %Z@. As this contains a timezone
-- flag, it should be used for formatting output only. In particular, it should
-- __not__ be used with 'Kairos.Types.TimeReader.format'.
--
-- @since 0.1
rfc822 :: TimeFormat
rfc822 :: TimeFormat
rfc822 = TimeFormat
"%a, %_d %b %Y %H:%M:%S %Z"
{-# INLINE rfc822 #-}