File: Time.hs

package info (click to toggle)
haskell-text-builder-dev 0.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 104 kB
  • sloc: haskell: 587; makefile: 3
file content (118 lines) | stat: -rw-r--r-- 3,135 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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 {..} =
  let (year, month, day) = toGregorian utctDay
      daySeconds = round utctDayTime
      (dayMinutes, second) = divMod daySeconds 60
      (hour, minute) = divMod dayMinutes 60
   in utcTimestampInIso8601 (fromIntegral year) month day hour minute 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 y mo d h mi s =
  mconcat
    [ fixedLengthDecimal 4 y,
      "-",
      fixedLengthDecimal 2 mo,
      "-",
      fixedLengthDecimal 2 d,
      "T",
      fixedLengthDecimal 2 h,
      ":",
      fixedLengthDecimal 2 mi,
      ":",
      fixedLengthDecimal 2 s,
      "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 interval = flip evalState (round interval :: Int) $ do
  seconds <- state (swap . flip divMod 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 = picoseconds . 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 x =
  attemptOr 1_000_000_000_000 "s"
    $ attemptOr 1_000_000_000 "ms"
    $ attemptOr 1_000_000 "us"
    $ attemptOr 1_000 "ns"
    $ decimal x
    <> "ps"
  where
    attemptOr factor suffix alternative =
      if x == divided * factor
        then decimal divided <> suffix
        else alternative
      where
        divided = div x factor