hourglass 0.2.9 → 0.2.10
raw patch · 11 files changed
+455/−346 lines, 11 files
Files
- Data/Hourglass.hs +1/−1
- Data/Hourglass/Compat.hs +4/−70
- Data/Hourglass/Diff.hs +10/−4
- Data/Hourglass/Internal/Win.hs +10/−3
- Data/Hourglass/Types.hs +4/−220
- System/Hourglass.hs +4/−46
- Time/Compat.hs +104/−0
- Time/System.hs +58/−0
- Time/Types.hs +244/−0
- hourglass.cabal +5/−2
- tests/Tests.hs +11/−0
Data/Hourglass.hs view
@@ -37,4 +37,4 @@ import Data.Hourglass.Types import Data.Hourglass.Local import Data.Hourglass.Zone-import Data.Hourglass.Calendar (isLeapYear, getWeekDay, getDayOfTheYear)+import Data.Hourglass.Calendar (isLeapYear, getWeekDay, getDayOfTheYear, daysInMonth)
Data/Hourglass/Compat.hs view
@@ -29,76 +29,10 @@ -- > -- > offsetTime = H.TimezoneOffset $ fromIntegral $ T.timeZoneMinutes $ T.zonedTimeZone oldTime --+-- This module will be depreciated in favor of Time.Compat+-- module Data.Hourglass.Compat- ( dateFromPOSIXEpoch- , dateFromTAIEpoch- , diffTimeToTimeOfDay+ ( module Time.Compat ) where -import Data.Hourglass---- | Convert an integer which represent the Number of days (To/From) POSIX Epoch--- to a Date (POSIX Epoch is 1970-01-01).-dateFromPOSIXEpoch :: Integer -- ^ number of days since POSIX Epoch- -> Date-dateFromPOSIXEpoch day = do- let sec = Elapsed $ fromIntegral $ day * 86400- timeConvert sec---- | Number of days between POSIX Epoch and TAI Epoch--- (between 1858-11-17 and 1970-01-01)-daysTAItoPOSIX :: Integer-daysTAItoPOSIX = 40587---- | Convert an integer which represents the Number of days (To/From) TAI Epoch--- This function allows use of the package time to easily convert the Day into--- the Hourglass Date representation (TAI Epoch is 1858-11-17).--- --- This function allows user to easily convert a Data.Time.Calendar.Day into Date------ > import qualified Data.Time.Calendar as T--- >--- > timeDay :: T.Day--- >--- > dateFromTAIEpoch $ T.toModifiedJulianDay timeDay-dateFromTAIEpoch :: Integer -- ^ number of days since TAI Epoch- -> Date-dateFromTAIEpoch dtai =- dateFromPOSIXEpoch (dtai - daysTAItoPOSIX)---- | Convert of differential of time of a day.--- (it convers a Data.Time.Clock.DiffTime into a TimeOfDay)------ Example with DiffTime type from time:------ > import qualified Data.Time.Clock as T--- >--- > difftime :: T.DiffTime--- >--- > diffTimeToTimeOfDay difftime------ Example with the TimeOfDay type from time:------ > import qualified Data.Time.Clock as T--- >--- > timeofday :: T.TimeOfDay--- >--- > diffTimeToTimeOfDay $ T.timeOfDayToTime timeofday-diffTimeToTimeOfDay :: Real t- => t -- ^ number of seconds of the time of the day- -> TimeOfDay-diffTimeToTimeOfDay dt = do- TimeOfDay- { todHour = fromIntegral hours- , todMin = fromIntegral minutes- , todSec = fromIntegral seconds- , todNSec = fromIntegral nsecs- }- where- r :: Rational- r = toRational dt- (secs, nR) = properFraction r :: (Integer, Rational)- nsecs :: Integer- nsecs = round (nR * 1000000000)- (minsofday, seconds) = secs `divMod` 60 :: (Integer, Integer)- (hours, minutes) = minsofday `divMod` 60 :: (Integer, Integer)+import Time.Compat
Data/Hourglass/Diff.hs view
@@ -34,7 +34,7 @@ { periodYears :: !Int , periodMonths :: !Int , periodDays :: !Int- } deriving (Read,Eq,Ord,Data,Typeable)+ } deriving (Show,Read,Eq,Ord,Data,Typeable) instance NFData Period where rnf (Period y m d) = y `seq` m `seq` d `seq` ()@@ -50,7 +50,7 @@ , durationMinutes :: !Minutes -- ^ number of minutes , durationSeconds :: !Seconds -- ^ number of seconds , durationNs :: !NanoSeconds -- ^ number of nanoseconds- } deriving (Read,Eq,Ord,Data,Typeable)+ } deriving (Show,Read,Eq,Ord,Data,Typeable) instance NFData Duration where rnf (Duration h m s ns) = h `seq` m `seq` s `seq` ns `seq` ()@@ -86,10 +86,16 @@ where (yDiffAcc,mStartPos) = (fromEnum mOrig + mDiff) `divMod` 12 loop y m d- | d < dMonth = Date y (toEnum m) d+ | d <= 0 =+ let (m', y') = if m == 0+ then (11, y - 1)+ else (m - 1, y)+ in+ loop y' m' (daysInMonth y' (toEnum m') + d)+ | d <= dMonth = Date y (toEnum m) d | otherwise = let newDiff = d - dMonth- in if m == 12+ in if m == 11 then loop (y+1) 0 newDiff else loop y (m+1) newDiff where dMonth = daysInMonth y (toEnum m)
Data/Hourglass/Internal/Win.hs view
@@ -61,9 +61,16 @@ systemGetTimezone :: IO TimezoneOffset systemGetTimezone = do- (_,tzInfo) <- getTimeZoneInformation- return $ TimezoneOffset $ getTzOffset tzInfo- where getTzOffset tzInfo = fromIntegral (tziBias tzInfo - tziDaylightBias tzInfo)+ (tzMode,tzInfo) <- getTimeZoneInformation+ case tzMode of+ TzIdDaylight -> return $ toTO (tziBias tzInfo + tziDaylightBias tzInfo)+ TzIdStandard -> return $ toTO (tziBias tzInfo + tziStandardBias tzInfo)+ TzIdUnknown -> return $ toTO (tziBias tzInfo)+ where+ -- a negative value represent value how to go from local to UTC,+ -- whereas TimezoneOffset represent the offset to go from UTC to local.+ -- here we negate the bias to get the proper value represented.+ toTO = TimezoneOffset . fromIntegral . negate systemGetElapsedP :: IO ElapsedP systemGetElapsedP = toElapsedP `fmap` getSystemTimeAsFileTime
Data/Hourglass/Types.hs view
@@ -14,226 +14,10 @@ -- Most units use the unix epoch referential, but by no means reduce portability. -- the unix referential works under the Windows platform or any other platforms. --+-- This module will be depreciated in favor of Time.Types+-- module Data.Hourglass.Types- (- -- * Time units- NanoSeconds(..)- , Seconds(..)- , Minutes(..)- , Hours(..)- , TimeInterval(..)- -- * Time enumeration- , Month(..)- , WeekDay(..)- -- * Timezone- , TimezoneOffset(..)- , timezoneOffsetToSeconds- , timezone_UTC- -- * Computer friendly format- -- ** Unix elapsed- , Elapsed(..)- , ElapsedP(..)- -- * Human friendly format- -- ** Calendar time- , Date(..)- , TimeOfDay(..)- , DateTime(..)+ ( module Time.Types ) where -import Data.Int-import Data.Data-import Data.Ratio-import Control.DeepSeq-import Data.Hourglass.Utils (pad2)---- | Represent any time interval that has an--- equivalent value to a number of seconds.-class TimeInterval i where- toSeconds :: i -> Seconds- fromSeconds :: Seconds -> (i, Seconds)---- | Nanoseconds-newtype NanoSeconds = NanoSeconds Int64- deriving (Read,Eq,Ord,Num,Data,Typeable,NFData)--instance Show NanoSeconds where- show (NanoSeconds v) = shows v "ns"--instance TimeInterval NanoSeconds where- toSeconds (NanoSeconds ns) = Seconds (ns `div` 1000000000)- fromSeconds (Seconds s) = (NanoSeconds (s * 1000000000), 0)---- | Number of seconds without a referential.------ Can hold a number between [-2^63,2^63-1], which should--- be good for some billions of years.------ However, because of limitation in the calendar conversion--- currently used, seconds should be in the range [-2^55,2^55-1],--- which is good for only 1 billion of year.-newtype Seconds = Seconds Int64- deriving (Read,Eq,Ord,Enum,Num,Real,Integral,Data,Typeable,NFData)--instance Show Seconds where- show (Seconds s) = shows s "s"--instance TimeInterval Seconds where- toSeconds = id- fromSeconds s = (s,0)---- | Number of minutes without a referential.-newtype Minutes = Minutes Int64- deriving (Read,Eq,Ord,Enum,Num,Real,Integral,Data,Typeable,NFData)--instance Show Minutes where- show (Minutes s) = shows s "m"--instance TimeInterval Minutes where- toSeconds (Minutes m) = Seconds (m * 60)- fromSeconds (Seconds s) = (Minutes m, Seconds s')- where (m, s') = s `divMod` 60---- | Number of hours without a referential.-newtype Hours = Hours Int64- deriving (Read,Eq,Ord,Enum,Num,Real,Integral,Data,Typeable,NFData)--instance Show Hours where- show (Hours s) = shows s "h"--instance TimeInterval Hours where- toSeconds (Hours h) = Seconds (h * 3600)- fromSeconds (Seconds s) = (Hours h, Seconds s')- where (h, s') = s `divMod` 3600---- | A number of seconds elapsed since the unix epoch.-newtype Elapsed = Elapsed Seconds- deriving (Read,Eq,Ord,Num,Data,Typeable,NFData)--instance Show Elapsed where- show (Elapsed s) = show s---- | A number of seconds and nanoseconds elapsed since the unix epoch.-data ElapsedP = ElapsedP {-# UNPACK #-} !Elapsed {-# UNPACK #-} !NanoSeconds- deriving (Read,Eq,Ord,Data,Typeable)--instance Show ElapsedP where- show (ElapsedP e ns) = shows e ('.' : show ns)--instance NFData ElapsedP where rnf e = e `seq` ()--instance Num ElapsedP where- (+) = addElapsedP- (-) = subElapsedP- (ElapsedP e1 ns1) * (ElapsedP e2 ns2) = ElapsedP (e1*e2) (ns1*ns2)- negate (ElapsedP e ns) = ElapsedP (negate e) ns- abs (ElapsedP e ns) = ElapsedP (abs e) ns- signum (ElapsedP e ns) = ElapsedP (signum e) ns- fromInteger i = ElapsedP (Elapsed (fromIntegral i)) 0--addElapsedP :: ElapsedP -> ElapsedP -> ElapsedP-addElapsedP (ElapsedP e1 (NanoSeconds ns1)) (ElapsedP e2 (NanoSeconds ns2)) =- let notNormalizedNS = ns1 + ns2- (retainedNS, ns) = notNormalizedNS `divMod` 1000000000- in ElapsedP (e1 + e2 + (Elapsed $ Seconds retainedNS)) (NanoSeconds ns)--subElapsedP :: ElapsedP -> ElapsedP -> ElapsedP-subElapsedP (ElapsedP e1 (NanoSeconds ns1)) (ElapsedP e2 (NanoSeconds ns2)) =- let notNormalizedNS = ns1 - ns2- notNormalizedS = e1 - e2- in if notNormalizedNS < 0- then ElapsedP (notNormalizedS - oneSecond) (NanoSeconds (1000000000 + notNormalizedNS))- else ElapsedP notNormalizedS (NanoSeconds notNormalizedNS)- where- oneSecond :: Elapsed- oneSecond = Elapsed $ Seconds 1--instance Real ElapsedP where- -- FIXME- toRational (ElapsedP (Elapsed (Seconds s)) (NanoSeconds ns)) =- fromIntegral s + (1000000000 % fromIntegral ns)---- | Month of the year-data Month =- January- | February- | March- | April- | May- | June- | July- | August- | September- | October- | November- | December- deriving (Show,Eq,Ord,Enum,Data,Typeable)---- | Day of the week------ the enumeration starts on Sunday.-data WeekDay =- Sunday- | Monday- | Tuesday- | Wednesday- | Thursday- | Friday- | Saturday- deriving (Show,Read,Eq,Ord,Enum,Data,Typeable)---- | Offset against UTC in minutes------ * a positive number represent a location East of UTC.------ * a negative number represent a location West of UTC.------ LocalTime t (-300) = t represent a time at UTC-5--- LocalTime t (+480) = t represent a time at UTC+8------ should be between -11H and +14H-newtype TimezoneOffset = TimezoneOffset- { timezoneOffsetToMinutes :: Int -- ^ return the number of minutes- } deriving (Eq,Ord,Data,Typeable,NFData)---- | Return the number of seconds associated with a timezone-timezoneOffsetToSeconds :: TimezoneOffset -> Seconds-timezoneOffsetToSeconds (TimezoneOffset ofs) = Seconds (fromIntegral ofs * 60)--instance Show TimezoneOffset where- show (TimezoneOffset tz) =- concat [if tz < 0 then "-" else "+", pad2 tzH, pad2 tzM]- where (tzH, tzM) = abs tz `divMod` 60---- | The UTC timezone. offset of 0-timezone_UTC :: TimezoneOffset-timezone_UTC = TimezoneOffset 0---- | human date representation using common calendar-data Date = Date- { dateYear :: {-# UNPACK #-} !Int -- ^ year (Common Era)- , dateMonth :: !Month -- ^ month of the year- , dateDay :: {-# UNPACK #-} !Int -- ^ day of the month, between 1 to 31- } deriving (Show,Eq,Ord,Data,Typeable)--instance NFData Date where- rnf (Date y m d) = y `seq` m `seq` d `seq` ()---- | human time representation of hour, minutes, seconds in a day.-data TimeOfDay = TimeOfDay- { todHour :: {-# UNPACK #-} !Hours -- ^ hours, between 0 and 23- , todMin :: {-# UNPACK #-} !Minutes -- ^ minutes, between 0 and 59- , todSec :: {-# UNPACK #-} !Seconds -- ^ seconds, between 0 and 59. 60 when having leap second */- , todNSec :: {-# UNPACK #-} !NanoSeconds -- ^ nanoseconds, between 0 and 999999999 */- } deriving (Show,Eq,Ord,Data,Typeable)--instance NFData TimeOfDay where- rnf (TimeOfDay h m s ns) = h `seq` m `seq` s `seq` ns `seq` ()---- | Date and Time-data DateTime = DateTime- { dtDate :: Date- , dtTime :: TimeOfDay- } deriving (Show,Eq,Ord,Data,Typeable)--instance NFData DateTime where- rnf (DateTime d t) = rnf d `seq` rnf t `seq` ()+import Time.Types
System/Hourglass.hs view
@@ -7,52 +7,10 @@ -- -- Get the system timezone and current time value in multiple formats --+-- This module will be depreciated in favor of Time.System+-- module System.Hourglass- (- -- * Current time in computer friendly format- timeCurrent- , timeCurrentP- -- * Current time in human friendly DateTime format- , dateCurrent- , localDateCurrent- , localDateCurrentAt- -- * System timezone- , timezoneCurrent+ ( module Time.System ) where -import Control.Applicative-import Data.Hourglass.Types-import Data.Hourglass.Time-import Data.Hourglass.Local-import Data.Hourglass.Internal (systemGetElapsedP, systemGetElapsed, systemGetTimezone)---- | Get the current elapsed seconds since epoch-timeCurrent :: IO Elapsed-timeCurrent = systemGetElapsed---- | Get the current elapsed seconds (precise to the nanosecond) since epoch-timeCurrentP :: IO ElapsedP-timeCurrentP = systemGetElapsedP---- | Get the current global date------ This is equivalent to:------ > timeGetDateTimeOfDay `fmap` timeCurrentP-dateCurrent :: IO DateTime-dateCurrent = timeGetDateTimeOfDay <$> timeCurrentP---- | Get the localized date by using 'timezoneCurrent' and 'dateCurrent'-localDateCurrent :: IO (LocalTime DateTime)-localDateCurrent = localTimeSetTimezone <$> timezoneCurrent- <*> (localTimeFromGlobal <$> dateCurrent)---- | Get the localized date at a specific timezone offset.-localDateCurrentAt :: TimezoneOffset -> IO (LocalTime DateTime)-localDateCurrentAt tz = localTimeSetTimezone tz . localTimeFromGlobal <$> dateCurrent---- | Get the current timezone offset------ This include daylight saving time when in operation.-timezoneCurrent :: IO TimezoneOffset-timezoneCurrent = systemGetTimezone+import Time.System
+ Time/Compat.hs view
@@ -0,0 +1,104 @@+-- |+-- Module : Time.Compat+-- License : BSD-style+-- Maintainer : Nicolas DI PRIMA <nicolas@di-prima.fr>+--+-- Basic Time conversion compatibility.+--+-- This module aims to help conversion between the types from the package+-- time to the package hourglass.+--+-- Example of use (extracted from file Example/Time/Compat.hs):+--+-- > import Data.Hourglass as H+-- > import Data.Hourglass.Compat as C+-- > import Data.Time as T+-- >+-- > transpose :: T.ZonedTime+-- > -> H.LocalTime H.DateTime+-- > transpose oldTime =+-- > H.localTime+-- > offsetTime+-- > (H.DateTime newDate timeofday)+-- > where+-- > newDate :: H.Date+-- > newDate = C.dateFromTAIEpoch $ T.toModifiedJulianDay $ T.localDay $ T.zonedTimeToLocalTime oldTime+-- >+-- > timeofday :: H.TimeOfDay+-- > timeofday = C.diffTimeToTimeOfDay $ T.timeOfDayToTime $ T.localTimeOfDay $ T.zonedTimeToLocalTime oldTime+-- >+-- > offsetTime = H.TimezoneOffset $ fromIntegral $ T.timeZoneMinutes $ T.zonedTimeZone oldTime+--+module Time.Compat+ ( dateFromPOSIXEpoch+ , dateFromTAIEpoch+ , diffTimeToTimeOfDay+ ) where++import Data.Hourglass++-- | Convert an integer which represent the Number of days (To/From) POSIX Epoch+-- to a Date (POSIX Epoch is 1970-01-01).+dateFromPOSIXEpoch :: Integer -- ^ number of days since POSIX Epoch+ -> Date+dateFromPOSIXEpoch day = do+ let sec = Elapsed $ fromIntegral $ day * 86400+ timeConvert sec++-- | Number of days between POSIX Epoch and TAI Epoch+-- (between 1858-11-17 and 1970-01-01)+daysTAItoPOSIX :: Integer+daysTAItoPOSIX = 40587++-- | Convert an integer which represents the Number of days (To/From) TAI Epoch+-- This function allows use of the package time to easily convert the Day into+-- the Hourglass Date representation (TAI Epoch is 1858-11-17).+-- +-- This function allows user to easily convert a Data.Time.Calendar.Day into Date+--+-- > import qualified Data.Time.Calendar as T+-- >+-- > timeDay :: T.Day+-- >+-- > dateFromTAIEpoch $ T.toModifiedJulianDay timeDay+dateFromTAIEpoch :: Integer -- ^ number of days since TAI Epoch+ -> Date+dateFromTAIEpoch dtai =+ dateFromPOSIXEpoch (dtai - daysTAItoPOSIX)++-- | Convert of differential of time of a day.+-- (it convers a Data.Time.Clock.DiffTime into a TimeOfDay)+--+-- Example with DiffTime type from time:+--+-- > import qualified Data.Time.Clock as T+-- >+-- > difftime :: T.DiffTime+-- >+-- > diffTimeToTimeOfDay difftime+--+-- Example with the TimeOfDay type from time:+--+-- > import qualified Data.Time.Clock as T+-- >+-- > timeofday :: T.TimeOfDay+-- >+-- > diffTimeToTimeOfDay $ T.timeOfDayToTime timeofday+diffTimeToTimeOfDay :: Real t+ => t -- ^ number of seconds of the time of the day+ -> TimeOfDay+diffTimeToTimeOfDay dt = do+ TimeOfDay+ { todHour = fromIntegral hours+ , todMin = fromIntegral minutes+ , todSec = fromIntegral seconds+ , todNSec = fromIntegral nsecs+ }+ where+ r :: Rational+ r = toRational dt+ (secs, nR) = properFraction r :: (Integer, Rational)+ nsecs :: Integer+ nsecs = round (nR * 1000000000)+ (minsofday, seconds) = secs `divMod` 60 :: (Integer, Integer)+ (hours, minutes) = minsofday `divMod` 60 :: (Integer, Integer)
+ Time/System.hs view
@@ -0,0 +1,58 @@+-- |+-- Module : Time.System+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+--+-- Get the system timezone and current time value in multiple formats+--+module Time.System+ (+ -- * Current time in computer friendly format+ timeCurrent+ , timeCurrentP+ -- * Current time in human friendly DateTime format+ , dateCurrent+ , localDateCurrent+ , localDateCurrentAt+ -- * System timezone+ , timezoneCurrent+ ) where++import Control.Applicative+import Time.Types+import Data.Hourglass.Time+import Data.Hourglass.Local+import Data.Hourglass.Internal (systemGetElapsedP, systemGetElapsed, systemGetTimezone)++-- | Get the current elapsed seconds since epoch+timeCurrent :: IO Elapsed+timeCurrent = systemGetElapsed++-- | Get the current elapsed seconds (precise to the nanosecond) since epoch+timeCurrentP :: IO ElapsedP+timeCurrentP = systemGetElapsedP++-- | Get the current global date+--+-- This is equivalent to:+--+-- > timeGetDateTimeOfDay `fmap` timeCurrentP+dateCurrent :: IO DateTime+dateCurrent = timeGetDateTimeOfDay <$> timeCurrentP++-- | Get the localized date by using 'timezoneCurrent' and 'dateCurrent'+localDateCurrent :: IO (LocalTime DateTime)+localDateCurrent = localTimeSetTimezone <$> timezoneCurrent+ <*> (localTimeFromGlobal <$> dateCurrent)++-- | Get the localized date at a specific timezone offset.+localDateCurrentAt :: TimezoneOffset -> IO (LocalTime DateTime)+localDateCurrentAt tz = localTimeSetTimezone tz . localTimeFromGlobal <$> dateCurrent++-- | Get the current timezone offset+--+-- This include daylight saving time when in operation.+timezoneCurrent :: IO TimezoneOffset+timezoneCurrent = systemGetTimezone
+ Time/Types.hs view
@@ -0,0 +1,244 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveDataTypeable #-}+-- |+-- Module : Time.Types+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+--+-- Basic times units and types.+--+-- While pratically some units could hold infinite values, for practical+-- and efficient purpose they are limited to int64 types for seconds+-- and int types for years.+--+-- Most units use the unix epoch referential, but by no means reduce portability.+-- the unix referential works under the Windows platform or any other platforms.+--+module Time.Types+ (+ -- * Time units+ NanoSeconds(..)+ , Seconds(..)+ , Minutes(..)+ , Hours(..)+ , TimeInterval(..)+ -- * Time enumeration+ , Month(..)+ , WeekDay(..)+ -- * Timezone+ , TimezoneOffset(..)+ , timezoneOffsetToSeconds+ , timezone_UTC+ -- * Computer friendly format+ -- ** Unix elapsed+ , Elapsed(..)+ , ElapsedP(..)+ -- * Human friendly format+ -- ** Calendar time+ , Date(..)+ , TimeOfDay(..)+ , DateTime(..)+ ) where++import Data.Int+import Data.Data+import Data.Ratio+import Control.DeepSeq+import Data.Hourglass.Utils (pad2)++-- | Represent any time interval that has an+-- equivalent value to a number of seconds.+class TimeInterval i where+ toSeconds :: i -> Seconds+ fromSeconds :: Seconds -> (i, Seconds)++-- | Nanoseconds+newtype NanoSeconds = NanoSeconds Int64+ deriving (Read,Eq,Ord,Num,Data,Typeable,NFData)++instance Show NanoSeconds where+ show (NanoSeconds v) = shows v "ns"++instance TimeInterval NanoSeconds where+ toSeconds (NanoSeconds ns) = Seconds (ns `div` 1000000000)+ fromSeconds (Seconds s) = (NanoSeconds (s * 1000000000), 0)++-- | Number of seconds without a referential.+--+-- Can hold a number between [-2^63,2^63-1], which should+-- be good for some billions of years.+--+-- However, because of limitation in the calendar conversion+-- currently used, seconds should be in the range [-2^55,2^55-1],+-- which is good for only 1 billion of year.+newtype Seconds = Seconds Int64+ deriving (Read,Eq,Ord,Enum,Num,Real,Integral,Data,Typeable,NFData)++instance Show Seconds where+ show (Seconds s) = shows s "s"++instance TimeInterval Seconds where+ toSeconds = id+ fromSeconds s = (s,0)++-- | Number of minutes without a referential.+newtype Minutes = Minutes Int64+ deriving (Read,Eq,Ord,Enum,Num,Real,Integral,Data,Typeable,NFData)++instance Show Minutes where+ show (Minutes s) = shows s "m"++instance TimeInterval Minutes where+ toSeconds (Minutes m) = Seconds (m * 60)+ fromSeconds (Seconds s) = (Minutes m, Seconds s')+ where (m, s') = s `divMod` 60++-- | Number of hours without a referential.+newtype Hours = Hours Int64+ deriving (Read,Eq,Ord,Enum,Num,Real,Integral,Data,Typeable,NFData)++instance Show Hours where+ show (Hours s) = shows s "h"++instance TimeInterval Hours where+ toSeconds (Hours h) = Seconds (h * 3600)+ fromSeconds (Seconds s) = (Hours h, Seconds s')+ where (h, s') = s `divMod` 3600++-- | A number of seconds elapsed since the unix epoch.+newtype Elapsed = Elapsed Seconds+ deriving (Read,Eq,Ord,Num,Data,Typeable,NFData)++instance Show Elapsed where+ show (Elapsed s) = show s++-- | A number of seconds and nanoseconds elapsed since the unix epoch.+data ElapsedP = ElapsedP {-# UNPACK #-} !Elapsed {-# UNPACK #-} !NanoSeconds+ deriving (Read,Eq,Ord,Data,Typeable)++instance Show ElapsedP where+ show (ElapsedP e ns) = shows e ('.' : show ns)++instance NFData ElapsedP where rnf e = e `seq` ()++instance Num ElapsedP where+ (+) = addElapsedP+ (-) = subElapsedP+ (ElapsedP e1 ns1) * (ElapsedP e2 ns2) = ElapsedP (e1*e2) (ns1*ns2)+ negate (ElapsedP e ns) = ElapsedP (negate e) ns+ abs (ElapsedP e ns) = ElapsedP (abs e) ns+ signum (ElapsedP e ns) = ElapsedP (signum e) ns+ fromInteger i = ElapsedP (Elapsed (fromIntegral i)) 0++addElapsedP :: ElapsedP -> ElapsedP -> ElapsedP+addElapsedP (ElapsedP e1 (NanoSeconds ns1)) (ElapsedP e2 (NanoSeconds ns2)) =+ let notNormalizedNS = ns1 + ns2+ (retainedNS, ns) = notNormalizedNS `divMod` 1000000000+ in ElapsedP (e1 + e2 + (Elapsed $ Seconds retainedNS)) (NanoSeconds ns)++subElapsedP :: ElapsedP -> ElapsedP -> ElapsedP+subElapsedP (ElapsedP e1 (NanoSeconds ns1)) (ElapsedP e2 (NanoSeconds ns2)) =+ let notNormalizedNS = ns1 - ns2+ notNormalizedS = e1 - e2+ in if notNormalizedNS < 0+ then ElapsedP (notNormalizedS - oneSecond) (NanoSeconds (1000000000 + notNormalizedNS))+ else ElapsedP notNormalizedS (NanoSeconds notNormalizedNS)+ where+ oneSecond :: Elapsed+ oneSecond = Elapsed $ Seconds 1++instance Real ElapsedP where+ -- FIXME+ toRational (ElapsedP (Elapsed (Seconds s)) (NanoSeconds ns)) =+ fromIntegral s + (1000000000 % fromIntegral ns)++-- | Month of the year+data Month =+ January+ | February+ | March+ | April+ | May+ | June+ | July+ | August+ | September+ | October+ | November+ | December+ deriving (Show,Read,Eq,Ord,Enum,Data,Typeable,Bounded)++-- | Day of the week+--+-- the enumeration starts on Sunday.+data WeekDay =+ Sunday+ | Monday+ | Tuesday+ | Wednesday+ | Thursday+ | Friday+ | Saturday+ deriving (Show,Read,Eq,Ord,Enum,Data,Typeable,Bounded)++-- | Offset against UTC in minutes to obtain from UTC time, local time.+--+-- * a positive number represent a location East of UTC.+--+-- * a negative number represent a location West of UTC.+--+-- LocalTime t (-300) = t represent a time at UTC-5+-- LocalTime t (+480) = t represent a time at UTC+8+--+-- should be between -11H and +14H+--+-- Example:+-- in AUSEDT (UTC+1000 with daylight = UTC+1100), local time is 15:47;+-- Thus, UTC time is 04:47, and TimezoneOffset is +660 (minutes)+--+newtype TimezoneOffset = TimezoneOffset+ { timezoneOffsetToMinutes :: Int -- ^ return the number of minutes+ } deriving (Eq,Ord,Data,Typeable,NFData)++-- | Return the number of seconds associated with a timezone+timezoneOffsetToSeconds :: TimezoneOffset -> Seconds+timezoneOffsetToSeconds (TimezoneOffset ofs) = Seconds (fromIntegral ofs * 60)++instance Show TimezoneOffset where+ show (TimezoneOffset tz) =+ concat [if tz < 0 then "-" else "+", pad2 tzH, pad2 tzM]+ where (tzH, tzM) = abs tz `divMod` 60++-- | The UTC timezone. offset of 0+timezone_UTC :: TimezoneOffset+timezone_UTC = TimezoneOffset 0++-- | human date representation using common calendar+data Date = Date+ { dateYear :: {-# UNPACK #-} !Int -- ^ year (Common Era)+ , dateMonth :: !Month -- ^ month of the year+ , dateDay :: {-# UNPACK #-} !Int -- ^ day of the month, between 1 to 31+ } deriving (Show,Read,Eq,Ord,Data,Typeable)++instance NFData Date where+ rnf (Date y m d) = y `seq` m `seq` d `seq` ()++-- | human time representation of hour, minutes, seconds in a day.+data TimeOfDay = TimeOfDay+ { todHour :: {-# UNPACK #-} !Hours -- ^ hours, between 0 and 23+ , todMin :: {-# UNPACK #-} !Minutes -- ^ minutes, between 0 and 59+ , todSec :: {-# UNPACK #-} !Seconds -- ^ seconds, between 0 and 59. 60 when having leap second */+ , todNSec :: {-# UNPACK #-} !NanoSeconds -- ^ nanoseconds, between 0 and 999999999 */+ } deriving (Show,Read,Eq,Ord,Data,Typeable)++instance NFData TimeOfDay where+ rnf (TimeOfDay h m s ns) = h `seq` m `seq` s `seq` ns `seq` ()++-- | Date and Time+data DateTime = DateTime+ { dtDate :: Date+ , dtTime :: TimeOfDay+ } deriving (Show,Read,Eq,Ord,Data,Typeable)++instance NFData DateTime where+ rnf (DateTime d t) = rnf d `seq` rnf t `seq` ()
hourglass.cabal view
@@ -1,5 +1,5 @@ Name: hourglass-Version: 0.2.9+Version: 0.2.10 Synopsis: simple performant time related library Description: Simple time library focusing on simple but powerful and performant API@@ -23,7 +23,10 @@ , tests/TimeDB.hs Library- Exposed-modules: Data.Hourglass+ Exposed-modules: Time.Types+ , Time.System+ , Time.Compat+ , Data.Hourglass , Data.Hourglass.Types , Data.Hourglass.Epoch , Data.Hourglass.Compat
tests/Tests.hs view
@@ -51,6 +51,15 @@ (h' , mi') = dt' `divMod` 60 (DateTime (Date y m d) (TimeOfDay h mi sec _)) = localTimeToGlobal localtime +-- | The @Date@ type is able to represent some values that aren't actually legal,+-- specifically dates with a day field outside of the range of dates in the+-- month. This function validates a @Date@. It is conservative; it only verifies+-- that the day is less than 31. TODO: It would be nice to tighten this up a+-- bit. There's a daysInMonth function we could use for this,+-- but Data.Hourglass.Calendar, but it isn't exposed.+isValidDate :: Date -> Bool+isValidDate (Date _ _ d) = d > 0 && d <= 31+ -- windows native functions to convert time cannot handle time before year 1601 #ifdef WINDOWS loElapsed = -11644473600 -- ~ year 1601@@ -183,6 +192,8 @@ (toEnum ((fromEnum m+1) `mod` 12) `eq` m') && (if m == December then (y+1) `eq` y' else y `eq` y') -}+ , testProperty "dateAddPeriod" $ (\date period ->+ isValidDate (date `dateAddPeriod` period)) ] , testGroup "formating" [ testProperty "iso8601 date" $ \(e :: Elapsed) ->