dotnet-timespan-0.0.1.0: Data/DotNet/TimeSpan/Internal.hs
--------------------------------------------------------------------------------
-- |
-- Module : Data.DotNet.TimeSpan.Internal
-- Copyright : (C) 2016 Yorick Laupa
-- License : (see the file LICENSE)
--
-- Maintainer : Yorick Laupa <yo.eight@gmail.com>
-- Stability : provisional
-- Portability : non-portable
--
-- .NET TimeSpan implemented in Haskell.
--------------------------------------------------------------------------------
module Data.DotNet.TimeSpan.Internal where
--------------------------------------------------------------------------------
import Data.Int
import Data.Monoid
import Prelude
--------------------------------------------------------------------------------
-- | .NET TimeSpan: Represents a time interval.
newtype TimeSpan = TimeSpan Int64 deriving (Eq, Ord)
--------------------------------------------------------------------------------
instance Show TimeSpan where
show = timeSpanString
--------------------------------------------------------------------------------
millisPerSecond :: Int64
millisPerSecond = 1000
--------------------------------------------------------------------------------
millisPerMinute :: Int64
millisPerMinute = millisPerSecond * 60
--------------------------------------------------------------------------------
millisPerHour :: Int64
millisPerHour = millisPerMinute * 60
--------------------------------------------------------------------------------
millisPerDay :: Int64
millisPerDay = millisPerHour * 24
--------------------------------------------------------------------------------
ticksPerMillisecond :: Int64
ticksPerMillisecond = 10000
--------------------------------------------------------------------------------
ticksPerSecond :: Int64
ticksPerSecond = ticksPerMillisecond * 1000
--------------------------------------------------------------------------------
ticksPerMinute :: Int64
ticksPerMinute = ticksPerSecond * 60
--------------------------------------------------------------------------------
ticksPerHour :: Int64
ticksPerHour = ticksPerMinute * 60
--------------------------------------------------------------------------------
ticksPerDay :: Int64
ticksPerDay = ticksPerHour * 24
--------------------------------------------------------------------------------
daysPerTick :: Double
daysPerTick = 1 / (realToFrac ticksPerDay)
--------------------------------------------------------------------------------
hoursPerTick :: Double
hoursPerTick = 1 / (realToFrac ticksPerHour)
--------------------------------------------------------------------------------
minutesPerTick :: Double
minutesPerTick = 1 / (realToFrac ticksPerMinute)
--------------------------------------------------------------------------------
secondsPerTick :: Double
secondsPerTick = 1 / (realToFrac ticksPerSecond)
--------------------------------------------------------------------------------
millisPerTick :: Double
millisPerTick = 1 / (realToFrac ticksPerMillisecond)
--------------------------------------------------------------------------------
maxMillis :: Int64
maxMillis =
truncate
(((realToFrac (maxBound :: Int64) :: Double)
/ realToFrac ticksPerMillisecond) :: Double)
--------------------------------------------------------------------------------
minMillis :: Int64
minMillis =
truncate
(((realToFrac (minBound :: Int64) :: Double)
/ realToFrac ticksPerMillisecond) :: Double)
--------------------------------------------------------------------------------
-- | Initializes a new instance of the TimeSpan structure to the specified
-- number of ticks.
timeSpanTicks :: Int64 -> TimeSpan
timeSpanTicks = TimeSpan
--------------------------------------------------------------------------------
-- | Initializes a new instance of the TimeSpan structure to a specified number
-- of hours, minutes, and seconds.
timeSpanHoursMinsSecs :: Int64 -> Int64 -> Int64 -> TimeSpan
timeSpanHoursMinsSecs hh mm ss = TimeSpan $ totalSecs * ticksPerSecond
where
totalSecs = (hh * 3600) + (mm * 60) + ss
--------------------------------------------------------------------------------
-- | Initializes a new instance of the TimeSpan structure to a specified number
-- of days, hours, minutes, and seconds.
timeSpanDaysHoursMinsSecs :: Int64 -> Int64 -> Int64 -> Int64 -> TimeSpan
timeSpanDaysHoursMinsSecs dd hh mm ss =
timeSpanDaysHoursMinsSecsMillis dd hh mm ss 0
--------------------------------------------------------------------------------
-- | Initializes a new instance of the TimeSpan structure to a specified number
-- of days, hours, minutes, seconds, and milliseconds.
timeSpanDaysHoursMinsSecsMillis :: Int64
-> Int64
-> Int64
-> Int64
-> Int64
-> TimeSpan
timeSpanDaysHoursMinsSecsMillis dd hh mm ss ms =
TimeSpan $ _totalMillis * ticksPerMillisecond
where
_totalMillis = ((dd * 3600 * 24) +
(hh * 3600) +
(mm * 60) +
ss) * 1000 + ms
--------------------------------------------------------------------------------
-- | Gets the number of ticks that represent the value of the current 'TimeSpan'
-- structure.
ticks :: TimeSpan -> Int64
ticks (TimeSpan i) = i
--------------------------------------------------------------------------------
-- | Gets the days component of the time interval represented by the current
-- 'TimeSpan' structure.
days :: TimeSpan -> Int64
days (TimeSpan i) = truncate $ (realToFrac i :: Double) /
(realToFrac ticksPerDay)
--------------------------------------------------------------------------------
-- | Gets the hours component of the time interval represented by the current
-- 'TimeSpan' structure.
hours :: TimeSpan -> Int64
hours (TimeSpan i) = mod (truncate $
(realToFrac i :: Double) /
(realToFrac ticksPerHour)) 24
--------------------------------------------------------------------------------
-- | Gets the minutes component of the time interval represented by the current
-- 'TimeSpan' structure.
minutes :: TimeSpan -> Int64
minutes (TimeSpan i) = mod (truncate $
(realToFrac i :: Double) /
(realToFrac ticksPerMinute)) 60
--------------------------------------------------------------------------------
-- | Gets the seconds component of the time interval represented by the current
-- 'TimeSpan' structure.
seconds :: TimeSpan -> Int64
seconds (TimeSpan i) = mod (truncate $
(realToFrac i :: Double) /
(realToFrac ticksPerSecond)) 60
--------------------------------------------------------------------------------
-- | Gets the milliseconds component of the time interval represented by the
-- current 'TimeSpan' structure.
millis :: TimeSpan -> Int64
millis (TimeSpan i) = mod (truncate $
(realToFrac i :: Double) /
(realToFrac ticksPerMillisecond)) 1000
--------------------------------------------------------------------------------
-- | Returns a 'TimeSpan' that represents a specified number of seconds, where
-- the specification is accurate to the nearest millisecond.
fromSeconds :: Double -> TimeSpan
fromSeconds i = interval i millisPerSecond
--------------------------------------------------------------------------------
-- | Returns a 'TimeSpan' that represents a specified number of minutes, where
-- the specification is accurate to the nearest millisecond.
fromMinutes :: Double -> TimeSpan
fromMinutes i = interval i millisPerMinute
--------------------------------------------------------------------------------
-- | Returns a 'TimeSpan' that represents a specified number of hours, where the
-- specification is accurate to the nearest millisecond.
fromHours :: Double -> TimeSpan
fromHours i = interval i millisPerHour
--------------------------------------------------------------------------------
-- | Returns a 'TimeSpan' that represents a specified number of days, where the
-- specification is accurate to the nearest millisecond.
fromDays :: Double -> TimeSpan
fromDays i = interval i millisPerDay
--------------------------------------------------------------------------------
-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
-- fractional days.
totalDays :: TimeSpan -> Double
totalDays (TimeSpan i) = (realToFrac i) * daysPerTick
--------------------------------------------------------------------------------
-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
-- fractional hours.
totalHours :: TimeSpan -> Double
totalHours (TimeSpan i) = (realToFrac i) * hoursPerTick
--------------------------------------------------------------------------------
-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
-- fractional minutes.
totalMinutes :: TimeSpan -> Double
totalMinutes (TimeSpan i) = (realToFrac i) * minutesPerTick
--------------------------------------------------------------------------------
-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
-- fractional seconds.
totalSeconds :: TimeSpan -> Double
totalSeconds (TimeSpan i) = (realToFrac i) * secondsPerTick
--------------------------------------------------------------------------------
-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
-- fractional milliseconds.
totalMillis :: TimeSpan -> Double
totalMillis (TimeSpan i) =
let tmp = (realToFrac i) * millisPerTick in
if tmp > (realToFrac maxMillis) then realToFrac maxMillis
else if tmp < (realToFrac minMillis) then realToFrac minMillis
else tmp
--------------------------------------------------------------------------------
data FormatLiteral = Positive | Negative
--------------------------------------------------------------------------------
padded :: Int -> a -> [a] -> [a]
padded n p xs = replicate diff p ++ xs
where
len_xs = length xs
diff = n - len_xs
--------------------------------------------------------------------------------
timeSpanString :: TimeSpan -> String
timeSpanString (TimeSpan _ticks) =
start <>
genDay <>
genHours <>
genMins <>
genSecs <>
genFract
where
ticksPerHourD = realToFrac ticksPerHour :: Double
ticksPerDayD = realToFrac ticksPerDay :: Double
ticksPerMinuteD = realToFrac ticksPerMinute :: Double
ticksPerSecondD = realToFrac ticksPerSecond :: Double
day :: Int64
day = truncate $ realToFrac _ticks / ticksPerDayD
time = _ticks `mod` ticksPerDay
cday = if _ticks < 0 then negate day else day
ctime = if _ticks < 0 then negate time else time
_hours :: Int64
_hours = mod (truncate (realToFrac ctime / ticksPerHourD)) 24
mins :: Int64
mins = mod (truncate (realToFrac ctime / ticksPerMinuteD)) 60
secs :: Int64
secs = mod (truncate (realToFrac ctime / ticksPerSecondD)) 60
fraction :: Int64
fraction = ctime `mod` ticksPerSecond
literal = if _ticks < 0 then Negative else Positive
start =
case literal of
Positive -> ""
Negative -> "-"
genDay =
if cday /= 0
then show cday <> "."
else mempty
genHours = padded 2 '0' (show _hours) <> ":"
genMins = padded 2 '0' (show mins) <> ":"
genSecs = padded 2 '0' (show secs)
genFract =
if fraction /= 0
then "." <> padded 7 '0' (show fraction)
else mempty
--------------------------------------------------------------------------------
interval :: Double -> Int64 -> TimeSpan
interval value scale =
let tmp = value * (realToFrac scale)
_millis = tmp + (if value >= 0 then 0.5 else (-0.5))
res = truncate (_millis * (realToFrac ticksPerMillisecond)) in
TimeSpan res