    module TextBuilderDev.Domains.Time where

    import TextBuilder
    import TextBuilderDev.Domains.Padding
    import TextBuilderDev.Prelude

    -- | UTC time in ISO8601 format.
    --
    -- >>> utcTimeIso8601Timestamp (read "2021-11-24 12:11:02 UTC")
    -- "2021-11-24T12:11:02Z"
    utcTimeIso8601Timestamp :: UTCTime -> TextBuilder
    utcTimeIso8601Timestamp :: UTCTime -> TextBuilder
    utcTimeIso8601Timestamp UTCTime {Day
    DiffTime
    utctDay :: Day
    utctDayTime :: DiffTime
    utctDay :: UTCTime -> Day
    utctDayTime :: UTCTime -> DiffTime
    ..} =
      let (Integer
    year, Int
    month, Int
    day) = Day -> (Integer, Int, Int)
    toGregorian Day
    utctDay
          daySeconds :: Int
    daySeconds = DiffTime -> Int
    forall b. Integral b => DiffTime -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    round DiffTime
    utctDayTime
          (Int
    dayMinutes, Int
    second) = Int -> Int -> (Int, Int)
    forall a. Integral a => a -> a -> (a, a)
    divMod Int
    daySeconds Int
    60
          (Int
    hour, Int
    minute) = Int -> Int -> (Int, Int)
    forall a. Integral a => a -> a -> (a, a)
    divMod Int
    dayMinutes Int
    60
       in Int -> Int -> Int -> Int -> Int -> Int -> TextBuilder
    utcTimestampInIso8601 (Integer -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Integer
    year) Int
    month Int
    day Int
    hour Int
    minute Int
    second

    -- |
    -- General template for formatting date values according to the ISO8601 standard.
    -- The format is the following:
    --
    -- Integrations with various time-libraries can be easily derived from that.
    --
    -- >>> utcTimestampInIso8601 2021 11 24 12 11 02
    -- "2021-11-24T12:11:02Z"
    utcTimestampInIso8601 ::
      -- | Year.
      Int ->
      -- | Month.
      Int ->
      -- | Day.
      Int ->
      -- | Hour.
      Int ->
      -- | Minute.
      Int ->
      -- | Second.
      Int ->
      TextBuilder
    utcTimestampInIso8601 :: Int -> Int -> Int -> Int -> Int -> Int -> TextBuilder
    utcTimestampInIso8601 Int
    y Int
    mo Int
    d Int
    h Int
    mi Int
    s =
      [TextBuilder] -> TextBuilder
    forall a. Monoid a => [a] -> a
    mconcat
        [ Int -> Int -> TextBuilder
    forall a. Integral a => Int -> a -> TextBuilder
    fixedLengthDecimal Int
    4 Int
    y,
          TextBuilder
    "-",
          Int -> Int -> TextBuilder
    forall a. Integral a => Int -> a -> TextBuilder
    fixedLengthDecimal Int
    2 Int
    mo,
          TextBuilder
    "-",
          Int -> Int -> TextBuilder
    forall a. Integral a => Int -> a -> TextBuilder
    fixedLengthDecimal Int
    2 Int
    d,
          TextBuilder
    "T",
          Int -> Int -> TextBuilder
    forall a. Integral a => Int -> a -> TextBuilder
    fixedLengthDecimal Int
    2 Int
    h,
          TextBuilder
    ":",
          Int -> Int -> TextBuilder
    forall a. Integral a => Int -> a -> TextBuilder
    fixedLengthDecimal Int
    2 Int
    mi,
          TextBuilder
    ":",
          Int -> Int -> TextBuilder
    forall a. Integral a => Int -> a -> TextBuilder
    fixedLengthDecimal Int
    2 Int
    s,
          TextBuilder
    "Z"
        ]

    -- |
    -- Time interval in seconds.
    -- Directly applicable to 'DiffTime' and 'NominalDiffTime'.
    --
    -- The format is the following:
    --
    -- > DD:HH:MM:SS
    --
    -- >>> realFracDdHhMmSsInterval @Double 59
    -- "00:00:00:59"
    --
    -- >>> realFracDdHhMmSsInterval @Double 90
    -- "00:00:01:30"
    --
    -- >>> realFracDdHhMmSsInterval @Double 86401
    -- "01:00:00:01"
    --
    -- >>> realFracDdHhMmSsInterval @Double (356 * 86400)
    -- "356:00:00:00"
    {-# INLINEABLE realFracDdHhMmSsInterval #-}
    realFracDdHhMmSsInterval :: (RealFrac seconds) => seconds -> TextBuilder
    realFracDdHhMmSsInterval :: forall seconds. RealFrac seconds => seconds -> TextBuilder
    realFracDdHhMmSsInterval seconds
    interval = (State Int TextBuilder -> Int -> TextBuilder)
    -> Int -> State Int TextBuilder -> TextBuilder
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip State Int TextBuilder -> Int -> TextBuilder
    forall s a. State s a -> s -> a
    evalState (seconds -> Int
    forall b. Integral b => seconds -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    round seconds
    interval :: Int) (State Int TextBuilder -> TextBuilder)
    -> State Int TextBuilder -> TextBuilder
    forall a b. (a -> b) -> a -> b
    $ do
      seconds <- (Int -> (Int, Int)) -> StateT Int Identity Int
    forall (m :: * -> *) s a. Monad m => (s -> (a, s)) -> StateT s m a
    state ((Int, Int) -> (Int, Int)
    forall a b. (a, b) -> (b, a)
    swap ((Int, Int) -> (Int, Int))
    -> (Int -> (Int, Int)) -> Int -> (Int, Int)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . (Int -> Int -> (Int, Int)) -> Int -> Int -> (Int, Int)
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Int -> Int -> (Int, Int)
    forall a. Integral a => a -> a -> (a, a)
    divMod Int
    60)
      minutes <- state (swap . flip divMod 60)
      hours <- state (swap . flip divMod 24)
      days <- get
      return
        ( mconcat
            [ padFromLeft 2 '0' (decimal days),
              ":",
              padFromLeft 2 '0' (decimal hours),
              ":",
              padFromLeft 2 '0' (decimal minutes),
              ":",
              padFromLeft 2 '0' (decimal seconds)
            ]
        )

    -- | DiffTime in a compact decimal format based on 'picoseconds'.
    diffTimeSeconds :: DiffTime -> TextBuilder
    diffTimeSeconds :: DiffTime -> TextBuilder
    diffTimeSeconds = Integer -> TextBuilder
    picoseconds (Integer -> TextBuilder)
    -> (DiffTime -> Integer) -> DiffTime -> TextBuilder
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
    Category cat =>
    cat b c -> cat a b -> cat a c
    . DiffTime -> Integer
    diffTimeToPicoseconds

    -- | Amount of picoseconds represented in a compact decimal format using suffixes.
    --
    -- E.g., the following is @1_230_000_000@ picoseconds or 1.23 milliseconds or 1230 microseconds:
    --
    -- > 1230us
    picoseconds :: Integer -> TextBuilder
    picoseconds :: Integer -> TextBuilder
    picoseconds Integer
    x =
      Integer -> TextBuilder -> TextBuilder -> TextBuilder
    attemptOr Integer
    1_000_000_000_000 TextBuilder
    "s"
        (TextBuilder -> TextBuilder) -> TextBuilder -> TextBuilder
    forall a b. (a -> b) -> a -> b
    $ Integer -> TextBuilder -> TextBuilder -> TextBuilder
    attemptOr Integer
    1_000_000_000 TextBuilder
    "ms"
        (TextBuilder -> TextBuilder) -> TextBuilder -> TextBuilder
    forall a b. (a -> b) -> a -> b
    $ Integer -> TextBuilder -> TextBuilder -> TextBuilder
    attemptOr Integer
    1_000_000 TextBuilder
    "us"
        (TextBuilder -> TextBuilder) -> TextBuilder -> TextBuilder
    forall a b. (a -> b) -> a -> b
    $ Integer -> TextBuilder -> TextBuilder -> TextBuilder
    attemptOr Integer
    1_000 TextBuilder
    "ns"
        (TextBuilder -> TextBuilder) -> TextBuilder -> TextBuilder
    forall a b. (a -> b) -> a -> b
    $ Integer -> TextBuilder
    forall a. Integral a => a -> TextBuilder
    decimal Integer
    x
        TextBuilder -> TextBuilder -> TextBuilder
    forall a. Semigroup a => a -> a -> a
    <> TextBuilder
    "ps"
      where
        attemptOr :: Integer -> TextBuilder -> TextBuilder -> TextBuilder
    attemptOr Integer
    factor TextBuilder
    suffix TextBuilder
    alternative =
          if Integer
    x Integer -> Integer -> Bool
    forall a. Eq a => a -> a -> Bool
    == Integer
    divided Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    * Integer
    factor
            then Integer -> TextBuilder
    forall a. Integral a => a -> TextBuilder
    decimal Integer
    divided TextBuilder -> TextBuilder -> TextBuilder
    forall a. Semigroup a => a -> a -> a
    <> TextBuilder
    suffix
            else TextBuilder
    alternative
          where
            divided :: Integer
    divided = Integer -> Integer -> Integer
    forall a. Integral a => a -> a -> a
    div Integer
    x Integer
    factor