{-# LANGUAGE CPP #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Fmt.Time
(
timeF,
tzF,
tzNameF,
dateTimeF,
hmF,
hmsF,
hmsLF,
hmsPLF,
dayHalfF,
dayHalfUF,
hour24F,
hour12F,
hour24SF,
hour12SF,
minuteF,
secondF,
picosecondF,
subsecondF,
epochF,
dateSlashF,
dateDashF,
dateSlashLF,
yearF,
yyF,
centuryF,
monthNameF,
monthNameShortF,
monthF,
dayOfMonthF,
dayOfMonthOrdF,
dayOfMonthSF,
dayF,
weekYearF,
weekYYF,
weekCenturyF,
weekF,
dayOfWeekF,
dayNameShortF,
dayNameF,
weekFromZeroF,
dayOfWeekFromZeroF,
weekOfYearMonF,
diffF,
yearsF,
daysF,
hoursF,
minutesF,
secondsF,
)
where
import Data.List (find)
#if !MIN_VERSION_base(4,9,0)
import Data.Monoid ((<>))
#endif
import Data.Text (Text)
import qualified Data.Text as T
import Formatting.Buildable (build)
import Data.Text.Lazy.Builder (Builder)
import Data.Time
#if !MIN_VERSION_time(1,5,0)
import Data.Time.Locale.Compat
#endif
import Fmt.Internal.Numeric (fixedF, ordinalF)
timeF :: FormatTime a => Text -> a -> Builder
timeF :: Text -> a -> Builder
timeF Text
f = Text -> Builder
forall p. Buildable p => p -> Builder
build (Text -> Builder) -> (a -> Text) -> a -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (a -> String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeLocale -> String -> a -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale (Text -> String
T.unpack Text
f)
tzF :: FormatTime a => a -> Builder
tzF :: a -> Builder
tzF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%z"
tzNameF :: FormatTime a => a -> Builder
tzNameF :: a -> Builder
tzNameF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%Z"
dateTimeF :: FormatTime a => a -> Builder
dateTimeF :: a -> Builder
dateTimeF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%c"
hmF :: FormatTime a => a -> Builder
hmF :: a -> Builder
hmF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%R"
hmsF :: FormatTime a => a -> Builder
hmsF :: a -> Builder
hmsF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%T"
hmsLF :: FormatTime a => a -> Builder
hmsLF :: a -> Builder
hmsLF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%X"
hmsPLF :: FormatTime a => a -> Builder
hmsPLF :: a -> Builder
hmsPLF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%r"
dayHalfF :: FormatTime a => a -> Builder
dayHalfF :: a -> Builder
dayHalfF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%P"
dayHalfUF :: FormatTime a => a -> Builder
dayHalfUF :: a -> Builder
dayHalfUF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%p"
hour24F :: FormatTime a => a -> Builder
hour24F :: a -> Builder
hour24F = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%H"
hour12F :: FormatTime a => a -> Builder
hour12F :: a -> Builder
hour12F = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%I"
hour24SF :: FormatTime a => a -> Builder
hour24SF :: a -> Builder
hour24SF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%k"
hour12SF :: FormatTime a => a -> Builder
hour12SF :: a -> Builder
hour12SF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%l"
minuteF :: FormatTime a => a -> Builder
minuteF :: a -> Builder
minuteF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%M"
secondF :: FormatTime a => a -> Builder
secondF :: a -> Builder
secondF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%S"
picosecondF :: FormatTime a => a -> Builder
picosecondF :: a -> Builder
picosecondF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%q"
subsecondF :: FormatTime a => a -> Builder
subsecondF :: a -> Builder
subsecondF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%Q"
epochF :: FormatTime a => a -> Builder
epochF :: a -> Builder
epochF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%s"
dateSlashF :: FormatTime a => a -> Builder
dateSlashF :: a -> Builder
dateSlashF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%D"
dateDashF :: FormatTime a => a -> Builder
dateDashF :: a -> Builder
dateDashF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%F"
dateSlashLF :: FormatTime a => a -> Builder
dateSlashLF :: a -> Builder
dateSlashLF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%x"
yearF :: FormatTime a => a -> Builder
yearF :: a -> Builder
yearF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%Y"
yyF :: FormatTime a => a -> Builder
yyF :: a -> Builder
yyF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%y"
centuryF :: FormatTime a => a -> Builder
centuryF :: a -> Builder
centuryF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%C"
monthNameF :: FormatTime a => a -> Builder
monthNameF :: a -> Builder
monthNameF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%B"
monthNameShortF :: FormatTime a => a -> Builder
monthNameShortF :: a -> Builder
monthNameShortF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%b"
monthF :: FormatTime a => a -> Builder
monthF :: a -> Builder
monthF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%m"
dayOfMonthF :: FormatTime a => a -> Builder
dayOfMonthF :: a -> Builder
dayOfMonthF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%d"
dayOfMonthOrdF :: FormatTime a => a -> Builder
dayOfMonthOrdF :: a -> Builder
dayOfMonthOrdF = Int -> Builder
forall a. (Buildable a, Integral a) => a -> Builder
ordinalF (Int -> Builder) -> (a -> Int) -> a -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Int
forall a. FormatTime a => a -> Int
timeToInt
where
timeToInt :: FormatTime a => a -> Int
timeToInt :: a -> Int
timeToInt = String -> Int
forall a. Read a => String -> a
read (String -> Int) -> (a -> String) -> a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeLocale -> String -> a -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%d"
dayOfMonthSF :: FormatTime a => a -> Builder
dayOfMonthSF :: a -> Builder
dayOfMonthSF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%e"
dayF :: FormatTime a => a -> Builder
dayF :: a -> Builder
dayF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%j"
weekYearF :: FormatTime a => a -> Builder
weekYearF :: a -> Builder
weekYearF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%G"
weekYYF :: FormatTime a => a -> Builder
weekYYF :: a -> Builder
weekYYF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%g"
weekCenturyF :: FormatTime a => a -> Builder
weekCenturyF :: a -> Builder
weekCenturyF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%f"
weekF :: FormatTime a => a -> Builder
weekF :: a -> Builder
weekF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%V"
dayOfWeekF :: FormatTime a => a -> Builder
dayOfWeekF :: a -> Builder
dayOfWeekF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%u"
dayNameShortF :: FormatTime a => a -> Builder
dayNameShortF :: a -> Builder
dayNameShortF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%a"
dayNameF :: FormatTime a => a -> Builder
dayNameF :: a -> Builder
dayNameF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%A"
weekFromZeroF :: FormatTime a => a -> Builder
weekFromZeroF :: a -> Builder
weekFromZeroF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%U"
dayOfWeekFromZeroF :: FormatTime a => a -> Builder
dayOfWeekFromZeroF :: a -> Builder
dayOfWeekFromZeroF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%w"
weekOfYearMonF :: FormatTime a => a -> Builder
weekOfYearMonF :: a -> Builder
weekOfYearMonF = Text -> a -> Builder
forall a. FormatTime a => Text -> a -> Builder
timeF Text
"%W"
diffF :: forall n . RealFrac n
=> Bool
-> n
-> Builder
diffF :: Bool -> n -> Builder
diffF Bool
fix = n -> Builder
RealFrac n => n -> Builder
diffed
where
diffed :: RealFrac n => n -> Builder
diffed :: n -> Builder
diffed n
ts =
case ((n, Int -> Builder, n) -> Bool)
-> [(n, Int -> Builder, n)] -> Maybe (n, Int -> Builder, n)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\(n
s,Int -> Builder
_,n
_) -> n -> n
forall a. Num a => a -> a
abs n
ts n -> n -> Bool
forall a. Ord a => a -> a -> Bool
>= n
s) ([(n, Int -> Builder, n)] -> [(n, Int -> Builder, n)]
forall a. [a] -> [a]
reverse [(n, Int -> Builder, n)]
RealFrac n => [(n, Int -> Builder, n)]
ranges) of
Maybe (n, Int -> Builder, n)
Nothing -> Builder
"unknown"
Just (n
_, Int -> Builder
f, n
base) -> Builder
prefix Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Int -> Builder
f (n -> n -> Int
RealFrac n => n -> n -> Int
toInt n
ts n
base) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
suffix
where
prefix :: Builder
prefix = if Bool
fix Bool -> Bool -> Bool
&& n
ts n -> n -> Bool
forall a. Ord a => a -> a -> Bool
> n
0 then Builder
"in " else Builder
""
suffix :: Builder
suffix = if Bool
fix Bool -> Bool -> Bool
&& n
ts n -> n -> Bool
forall a. Ord a => a -> a -> Bool
< n
0 then Builder
" ago" else Builder
""
toInt :: RealFrac n => n -> n -> Int
toInt :: n -> n -> Int
toInt n
ts n
base = Int -> Int
forall a. Num a => a -> a
abs (n -> Int
forall a b. (RealFrac a, Integral b) => a -> b
round (n
ts n -> n -> n
forall a. Fractional a => a -> a -> a
/ n
base))
intF :: Builder -> Int -> Builder
intF :: Builder -> Int -> Builder
intF Builder
t Int
n = Int -> Builder
forall p. Buildable p => p -> Builder
build Int
n Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
t
ranges :: RealFrac n => [(n, Int -> Builder, n)]
ranges :: [(n, Int -> Builder, n)]
ranges =
[ (n
0 , Builder -> Int -> Builder
intF Builder
" milliseconds" , n
0.001 )
, (n
1 , Builder -> Int -> Builder
intF Builder
" seconds" , n
1 )
, (n
minute , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"a minute" , n
0 )
, (n
minute n -> n -> n
forall a. Num a => a -> a -> a
* n
2 , Builder -> Int -> Builder
intF Builder
" minutes" , n
minute)
, (n
minute n -> n -> n
forall a. Num a => a -> a -> a
* n
30 , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"half an hour" , n
0 )
, (n
minute n -> n -> n
forall a. Num a => a -> a -> a
* n
31 , Builder -> Int -> Builder
intF Builder
" minutes" , n
minute)
, (n
hour , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"an hour" , n
0 )
, (n
hour n -> n -> n
forall a. Num a => a -> a -> a
* n
2 , Builder -> Int -> Builder
intF Builder
" hours" , n
hour )
, (n
hour n -> n -> n
forall a. Num a => a -> a -> a
* n
3 , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"a few hours" , n
0 )
, (n
hour n -> n -> n
forall a. Num a => a -> a -> a
* n
4 , Builder -> Int -> Builder
intF Builder
" hours" , n
hour )
, (n
day , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"a day" , n
0 )
, (n
day n -> n -> n
forall a. Num a => a -> a -> a
* n
2 , Builder -> Int -> Builder
intF Builder
" days" , n
day )
, (n
week , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"a week" , n
0 )
, (n
week n -> n -> n
forall a. Num a => a -> a -> a
* n
2 , Builder -> Int -> Builder
intF Builder
" weeks" , n
week )
, (n
month , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"a month" , n
0 )
, (n
month n -> n -> n
forall a. Num a => a -> a -> a
* n
2 , Builder -> Int -> Builder
intF Builder
" months" , n
month )
, (n
year , Builder -> Int -> Builder
forall a b. a -> b -> a
const Builder
"a year" , n
0 )
, (n
year n -> n -> n
forall a. Num a => a -> a -> a
* n
2 , Builder -> Int -> Builder
intF Builder
" years" , n
year )
]
where year :: n
year = n
month n -> n -> n
forall a. Num a => a -> a -> a
* n
12
month :: n
month = n
day n -> n -> n
forall a. Num a => a -> a -> a
* n
30
week :: n
week = n
day n -> n -> n
forall a. Num a => a -> a -> a
* n
7
day :: n
day = n
hour n -> n -> n
forall a. Num a => a -> a -> a
* n
24
hour :: n
hour = n
minute n -> n -> n
forall a. Num a => a -> a -> a
* n
60
minute :: n
minute = n
60
yearsF :: RealFrac n
=> Int
-> n
-> Builder
yearsF :: Int -> n -> Builder
yearsF Int
n = Int -> n -> Builder
forall a. Real a => Int -> a -> Builder
fixedF Int
n (n -> Builder) -> (n -> n) -> n -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Num a => a -> a
abs (n -> n) -> (n -> n) -> n -> n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Fractional a => a -> a
count
where count :: a -> a
count a
x = a
x a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
365 a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
24 a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
60 a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
60
daysF :: RealFrac n
=> Int
-> n
-> Builder
daysF :: Int -> n -> Builder
daysF Int
n = Int -> n -> Builder
forall a. Real a => Int -> a -> Builder
fixedF Int
n (n -> Builder) -> (n -> n) -> n -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Num a => a -> a
abs (n -> n) -> (n -> n) -> n -> n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Fractional a => a -> a
count
where count :: a -> a
count a
x = a
x a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
24 a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
60 a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
60
hoursF :: RealFrac n
=> Int
-> n
-> Builder
hoursF :: Int -> n -> Builder
hoursF Int
n = Int -> n -> Builder
forall a. Real a => Int -> a -> Builder
fixedF Int
n (n -> Builder) -> (n -> n) -> n -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Num a => a -> a
abs (n -> n) -> (n -> n) -> n -> n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Fractional a => a -> a
count
where count :: a -> a
count a
x = a
x a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
60 a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
60
minutesF :: RealFrac n
=> Int
-> n
-> Builder
minutesF :: Int -> n -> Builder
minutesF Int
n = Int -> n -> Builder
forall a. Real a => Int -> a -> Builder
fixedF Int
n (n -> Builder) -> (n -> n) -> n -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Num a => a -> a
abs (n -> n) -> (n -> n) -> n -> n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Fractional a => a -> a
count
where count :: a -> a
count a
x = a
x a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
60
secondsF :: RealFrac n
=> Int
-> n
-> Builder
secondsF :: Int -> n -> Builder
secondsF Int
n = Int -> n -> Builder
forall a. Real a => Int -> a -> Builder
fixedF Int
n (n -> Builder) -> (n -> n) -> n -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> n
forall a. Num a => a -> a
abs