options-time (empty) → 1.0
raw patch · 5 files changed
+602/−0 lines, 5 filesdep +basedep +chelldep +old-localesetup-changed
Dependencies added: base, chell, old-locale, options, options-time, time
Files
- Setup.hs +2/−0
- lib/Options/Time.hs +325/−0
- license.txt +22/−0
- options-time.cabal +60/−0
- tests/Tests.hs +193/−0
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ lib/Options/Time.hs view
@@ -0,0 +1,325 @@+{-# LANGUAGE CPP #-}++-- |+-- Module: Options.Time+-- License: MIT+--+-- The @options-time@ package provides 'OptionType' implementations and+-- associated 'SimpleOption' instances for types related to dates and times.+module Options.Time+ ( optionType_duration+ , optionType_date+ , optionType_time+ , optionType_localTime+ , optionType_utcTime+ , optionType_zonedTime+ ) where++import Data.Fixed (divMod')+import Data.Ratio (numerator)+import qualified Data.Time as T+import Options+#if MIN_VERSION_time(1,3,0)+import Data.Time.LocalTime (makeTimeOfDayValid)+#else+import Data.Fixed (Pico)+#endif+#if MIN_VERSION_time(1,5,0)+import Data.Time.Format (defaultTimeLocale)+#else+import System.Locale (defaultTimeLocale)+#endif+import qualified Text.ParserCombinators.ReadP as R++-- | Store an option as a 'T.DiffTime'. The duration is specified in a format+-- such as @\"2h30m\"@ for two hours and thirty minutes.+--+-- Available units are:+--+-- * \"h\" for hours.+-- * \"m\" for minutes.+-- * \"s\" for seconds.+-- * \"ms\" for milliseconds.+-- * \"us\" or \"µs\" for microseconds.+-- * \"ns\" for nanoseconds.+-- * \"ps\" for picoseconds.+--+-- Larger units are not available to avoid ambiguity when dealing with daylight+-- savings time.+optionType_duration :: OptionType T.DiffTime+optionType_duration = optionType "duration" 0 parseDuration formatDuration++instance SimpleOptionType T.DiffTime where+ simpleOptionType = optionType_duration++parseDuration :: String -> Either String T.DiffTime+parseDuration = parsedOrErr where+ parsedOrErr s = case R.readP_to_S parser s of+ (duration,_):_ -> Right duration+ [] -> Left (show s ++ " could not be parsed as a duration.")+ parser = orderedChoice [zero, units]+ zero = do+ _ <- R.char '0'+ R.eof+ return (toPicoseconds 0)+ units = do+ minus <- R.option 1 (R.char '-' >> return (-1))+ acc <- loop (toPicoseconds 0) 0+ return (minus * acc)+ loop acc dropIdx = do+ digits <- R.munch1 (\c -> c >= '0' && c <= '9')+ (multiplier, dropIdx') <- orderedChoice (drop dropIdx parsers)+ let acc' = acc + multiplier (read digits)+ orderedChoice [done acc', loop acc' dropIdx']+ done acc = R.eof >> return acc+ parsers =+ [ R.char 'h' >> return (toHours, 1)+ , justMinuteSuffix >> return (toMinutes, 2)+ , R.char 's' >> return (toSeconds, 3)+ , R.string "ms" >> return (toMilliseconds, 4)+ , R.string "us" >> return (toMicroseconds, 6)+ , R.string "\181s" >> return (toMicroseconds, 6)+ , R.string "ns" >> return (toNanoseconds, 7)+ , R.string "ps" >> return (toPicoseconds, 8)+ ]+ justMinuteSuffix = do+ ahead <- R.look+ case ahead of+ 'm':'s':_ -> R.pfail+ _ -> R.char 'm'++orderedChoice :: [R.ReadP a] -> R.ReadP a+orderedChoice ps = case ps of+ [] -> R.pfail+ [p] -> p+ (p:ps') -> p R.<++ orderedChoice ps'++toPicoseconds :: Integer -> T.DiffTime+toPicoseconds = T.picosecondsToDiffTime++toNanoseconds :: Integer -> T.DiffTime+toNanoseconds = toPicoseconds . (*1000)++toMicroseconds :: Integer -> T.DiffTime+toMicroseconds = toNanoseconds . (*1000)++toMilliseconds :: Integer -> T.DiffTime+toMilliseconds = toMicroseconds . (*1000)++toSeconds :: Integer -> T.DiffTime+toSeconds = T.secondsToDiffTime++toMinutes :: Integer -> T.DiffTime+toMinutes = toSeconds . (*60)++toHours :: Integer -> T.DiffTime+toHours = toMinutes . (*60)++formatDuration :: T.DiffTime -> String+formatDuration t = formatted where+ formatted = if t == 0+ then "0s"+ else concat chunks+ (negative, absolute) = if t < 0+ then (True, t * (- 1))+ else (False, t)+ (rawSeconds, rawPicoFraction) = divMod' absolute 1 :: (Integer, T.DiffTime)+ (hours, rawMinutes) = divMod rawSeconds 3600+ (minutes, seconds) = divMod rawMinutes 60+ rawPicos = numerator (toRational (rawPicoFraction*1000000000000))+ (milliseconds, rawMicros) = divMod rawPicos 1000000000+ (microseconds, rawNanos) = divMod rawMicros 1000000+ (nanoseconds, picoseconds) = divMod rawNanos 1000+ chunks =+ [ if negative then "-" else ""+ , chunk hours "h"+ , chunk minutes "m"+ , chunk seconds "s"+ , chunk milliseconds "ms"+ , chunk microseconds "us"+ , chunk nanoseconds "ns"+ , chunk picoseconds "ps"+ ]+ chunk 0 _ = ""+ chunk n suffix = show n ++ suffix++-- | Store an option as a 'T.Day'. Supported formats are:+--+-- * \"YYYY-MM-DD\"+-- * \"YYYYMMDD\"+-- * \"YYYY-DDD\"+optionType_date :: OptionType T.Day+optionType_date = optionType "date" (T.fromGregorian 1970 1 1) parseDate formatDate++instance SimpleOptionType T.Day where+ simpleOptionType = optionType_date++parseDate :: String -> Either String T.Day+parseDate s = parsedOrErr where+ parsedOrErr = case parsed of+ Just day -> Right day+ Nothing -> Left (show s ++ " could not be parsed as a date.")+ parsed = firstJust+ [ checkedParse "%Y-%m-%d" s+ , checkedParse "%Y-%j" s+ , checkedParse "%Y%m%d" s+ ]++formatDate :: T.Day -> String+formatDate = T.formatTime defaultTimeLocale "%Y-%m-%d"++-- | Store an option as a 'T.TimeOfDay'. Supported formats are:+--+-- * \"HH:MM\"+-- * \"HH:MM:SS\"+-- * \"HH:MM:SS.FFFF\"+--+-- For example, the value @\"10:11:12.5\"@ is half a second past+-- 10:11:12 AM.+optionType_time :: OptionType T.TimeOfDay+optionType_time = optionType "time" T.midnight parseTime formatTime++instance SimpleOptionType T.TimeOfDay where+ simpleOptionType = optionType_time++parseTime :: String -> Either String T.TimeOfDay+parseTime s = parsedOrErr where+ parsedOrErr = case parsed >>= validateTime of+ Just time -> Right time+ Nothing -> Left (show s ++ " could not be parsed as a time.")+ parsed = firstJust+ [ checkedParse "%H:%M" s+ , checkedParse "%H:%M:%S%Q" s+ ]++formatTime :: T.TimeOfDay -> String+formatTime = T.formatTime defaultTimeLocale "%H:%M:%S%Q"++validateTime :: T.TimeOfDay -> Maybe T.TimeOfDay+validateTime t = makeTimeOfDayValid (T.todHour t) (T.todMin t) (T.todSec t)++#if !MIN_VERSION_time(1,3,0)+-- Based on time-1.3:Data/Time/LocalTime/TimeOfDay.hs+makeTimeOfDayValid :: Int -> Int -> Pico -> Maybe T.TimeOfDay+makeTimeOfDayValid h m s = do+ _ <- clipValid 0 24 h+ _ <- clipValid 0 60 m+ _ <- clipValid 0 61 s+ return (T.TimeOfDay h m s)++-- Based on time-1.3:Data/Time/Calendar/Private.hs+clipValid :: (Ord t) => t -> t -> t -> Maybe t+clipValid a _ x | x < a = Nothing+clipValid _ b x | x >= b = Nothing+clipValid _ _ x = Just x+#endif++-- | Store an option as a 'T.LocalTime'. Supported formats are a combination+-- of those for 'optionType_date' and 'optionType_time'.+optionType_localTime :: OptionType T.LocalTime+optionType_localTime = optionType "local time" localEpoch parseLocalTime formatLocalTime++instance SimpleOptionType T.LocalTime where+ simpleOptionType = optionType_localTime++localEpoch :: T.LocalTime+localEpoch = T.LocalTime (T.fromGregorian 1970 1 1) T.midnight++parseLocalTime :: String -> Either String T.LocalTime+parseLocalTime s = parsedOrErr where+ parsedOrErr = case parsed >>= validateLocalTime of+ Just time -> Right time+ Nothing -> Left (show s ++ " could not be parsed as a local time.")+ parsed = firstJust $ do+ ymd <- ["%Y-%m-%d", "%Y-%j", "%Y%m%d"]+ hms <- ["%H:%M", "%H:%M:%S%Q", "%H%M%S%Q"]+ sep <- [" ", "T"]+ [checkedParse (ymd ++ sep ++ hms) s]++formatLocalTime :: T.LocalTime -> String+formatLocalTime = T.formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S%Q"++validateLocalTime :: T.LocalTime -> Maybe T.LocalTime+validateLocalTime t = do+ _ <- validateTime (T.localTimeOfDay t)+ return t++-- | Store an option as a 'T.UTCTime'. Supported formats are a combination+-- of those for 'optionType_date' and 'optionType_time'.+optionType_utcTime :: OptionType T.UTCTime+optionType_utcTime = optionType "utc time" utcEpoch parseUtcTime formatUtcTime++instance SimpleOptionType T.UTCTime where+ simpleOptionType = optionType_utcTime++utcEpoch :: T.UTCTime+utcEpoch = T.UTCTime (T.fromGregorian 1970 1 1) 0++parseUtcTime :: String -> Either String T.UTCTime+parseUtcTime s = parsedOrErr where+ parsedOrErr = case parsed >>= validateUtcTime of+ Just time -> Right time+ Nothing -> Left (show s ++ " could not be parsed as a UTC time.")+ parsed = firstJust $ do+ ymd <- ["%Y-%m-%d", "%Y-%j", "%Y%m%d"]+ hms <- ["%H:%M", "%H:%M:%S%Q", "%H%M%S%Q"]+ sep <- [" ", "T"]+ [checkedParse (ymd ++ sep ++ hms) s]++formatUtcTime :: T.UTCTime -> String+formatUtcTime = T.formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S%Q"++validateUtcTime :: T.UTCTime -> Maybe T.UTCTime+validateUtcTime t = case T.utctDayTime t of+ x | x >= 0 && x < 86401 -> Just t+ _ -> Nothing++-- | Store an option as a 'T.ZonedTime'. Supported formats are a combination+-- of those for 'optionType_date' and 'optionType_time'.+optionType_zonedTime :: OptionType T.ZonedTime+optionType_zonedTime = optionType "zoned time" zonedEpoch parseZonedTime formatZonedTime++instance SimpleOptionType T.ZonedTime where+ simpleOptionType = optionType_zonedTime++zonedEpoch :: T.ZonedTime+zonedEpoch = T.ZonedTime localEpoch T.utc++parseZonedTime :: String -> Either String T.ZonedTime+parseZonedTime s = parsedOrErr where+ parsedOrErr = case parsed >>= validateZonedTime of+ Just time -> Right time+ Nothing -> Left (show s ++ " could not be parsed as a zoned time.")+ parsed = firstJust $ do+ ymd <- ["%Y-%m-%d", "%Y-%j", "%Y%m%d"]+ hms <- ["%H:%M", "%H:%M:%S%Q", "%H%M%S%Q"]+ sep <- [" ", "T"]+ (tz, fixtz) <- [("", id), ("Z", setUTC), ("%z", id), (" %z", id), (" %Z", id)]+ -- TODO: This doesn't support +01:00 because checkedParse will format+ -- that to +0100 and fail.+ [fixtz `fmap` checkedParse (ymd ++ sep ++ hms ++ tz) s]+ setUTC t = t { T.zonedTimeZone = T.utc }++formatZonedTime :: T.ZonedTime -> String+formatZonedTime = T.formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S%Q %Z"++validateZonedTime :: T.ZonedTime -> Maybe T.ZonedTime+validateZonedTime t = do+ _ <- validateLocalTime (T.zonedTimeToLocalTime t)+ return t++checkedParse :: (T.FormatTime t, T.ParseTime t) => String -> String -> Maybe t+checkedParse fmt input = do+ parsed <- T.parseTime defaultTimeLocale fmt input+ -- Be fairly strict about the input format, because T.parseTime will+ -- try to silently fix invalid inputs.+ --+ -- For example, "2014-20-12" is parsed as "2014-12-12".+ if input == T.formatTime defaultTimeLocale fmt parsed+ then Just parsed+ else Nothing++firstJust :: [Maybe a] -> Maybe a+firstJust xs = case [x | Just x <- xs] of+ x:_ -> Just x+ [] -> Nothing
+ license.txt view
@@ -0,0 +1,22 @@+Copyright (c) 2012 John Millikin++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated documentation+files (the "Software"), to deal in the Software without+restriction, including without limitation the rights to use,+copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following+conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.
+ options-time.cabal view
@@ -0,0 +1,60 @@+name: options-time+version: 1.0+license: MIT+license-file: license.txt+author: John Millikin <john@john-millikin.com>+maintainer: John Millikin <john@john-millikin.com>+build-type: Simple+cabal-version: >= 1.8+category: Console+stability: stable+homepage: https://john-millikin.com/software/haskell-options/++synopsis: Command-line option types for dates and times.++source-repository head+ type: git+ location: https://john-millikin.com/code/haskell-options-time/++source-repository this+ type: git+ location: https://john-millikin.com/code/haskell-options-time/+ tag: haskell-options-time_1.0++flag old-locale++library+ ghc-options: -Wall -fno-warn-orphans+ hs-source-dirs: lib++ if impl(ghc > 7.4)+ ghc-options: -fwarn-unsafe++ build-depends:+ base >= 4.1 && < 5.0+ , options >= 1.0 && < 2.0++ if flag(old-locale)+ build-depends:+ time >= 1.1.3 && < 1.5+ , old-locale >= 1.0 && < 2.0+ else+ build-depends:+ time >= 1.5 && < 2.0++ exposed-modules:+ Options.Time++test-suite tests+ type: exitcode-stdio-1.0+ main-is: Tests.hs++ ghc-options: -Wall+ hs-source-dirs: tests++ build-depends:+ base >= 4.0 && < 5.0+ , chell >= 0.4 && < 0.5+ , options >= 1.0 && < 2.0+ , options-time+ , time >= 1.1.3 && < 2.0
+ tests/Tests.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE TemplateHaskell #-}++module Main (main) where++import qualified Data.Time as T+import Options+import Options.Time+import Test.Chell++test_Duration :: Test+test_Duration = assertions "duration" $ do+ let parse = optionTypeParse optionType_duration+ let format = optionTypeShow optionType_duration+ $expect $ equal+ (parse "10h9m8s7ms6us5ns4ps")+ (Right (sum+ [ 10*60*60+ , 9*60+ , 8+ , 7*0.001+ , 6*0.000001+ , 5*0.000000001+ , 4*0.000000000001+ ]))+ $expect $ equal (parse "1h9s") (Right 3609)+ $expect $ equal (parse "1h9ms") (Right 3600.009)+ $expect $ equal (parse "0") (Right 0)+ $expect $ equal (parse "-1h9s") (Right (-3609))+ $expect $ equal (parse "1s2us3ns") (parse "1s2\181s3ns")+ $expect $ equal+ (parse "bogus")+ (Left "\"bogus\" could not be parsed as a duration.")+ $expect $ equal+ (parse "1ps2ps")+ (Left "\"1ps2ps\" could not be parsed as a duration.")+ $expect $ equal+ (format (sum+ [ 10*60*60+ , 9*60+ , 8+ , 7*0.001+ , 6*0.000001+ , 5*0.000000001+ , 4*0.000000000001+ ]))+ "10h9m8s7ms6us5ns4ps"+ $expect $ equal (format 0) "0s"+ $expect $ equal (format 3609) "1h9s"+ $expect $ equal (format (-3609)) "-1h9s"++test_Date :: Test+test_Date = assertions "date" $ do+ let parse = optionTypeParse optionType_date+ let format = optionTypeShow optionType_date+ $expect $ equal+ (parse "2011-12-13")+ (Right (T.fromGregorian 2011 12 13))+ $expect $ equal+ (parse "20111213")+ (Right (T.fromGregorian 2011 12 13))+ $expect $ equal+ (parse "2011-347")+ (Right (T.fromGregorian 2011 12 13))+ $expect $ equal+ (parse "bogus")+ (Left "\"bogus\" could not be parsed as a date.")+ $expect $ equal+ (parse "2011-10-32")+ (Left "\"2011-10-32\" could not be parsed as a date.")+ $expect $ equal (format (T.fromGregorian 2011 12 13)) "2011-12-13"++test_Time :: Test+test_Time = assertions "time" $ do+ let parse = optionTypeParse optionType_time+ let format = optionTypeShow optionType_time+ $expect $ equal+ (parse "10:11")+ (Right (T.TimeOfDay 10 11 0))+ $expect $ equal+ (parse "20:21")+ (Right (T.TimeOfDay 20 21 0))+ $expect $ equal+ (parse "20:21:22")+ (Right (T.TimeOfDay 20 21 22))+ $expect $ equal+ (parse "20:21:22.232425")+ (Right (T.TimeOfDay 20 21 22.232425))+ $expect $ equal+ (parse "bogus")+ (Left "\"bogus\" could not be parsed as a time.")+ $expect $ equal+ (parse "25:21:00")+ (Left "\"25:21:00\" could not be parsed as a time.")+ $expect $ equal+ (parse "20:21:60")+ (Right (T.TimeOfDay 20 21 60))+ $expect $ equal+ (parse "20:21:61")+ (Left "\"20:21:61\" could not be parsed as a time.")+ $expect $ equal (format (T.TimeOfDay 20 21 0)) "20:21:00"+ $expect $ equal (format (T.TimeOfDay 20 21 22.232425)) "20:21:22.232425"++test_LocalTime :: Test+test_LocalTime = assertions "local time" $ do+ let parse = optionTypeParse optionType_localTime+ let format = optionTypeShow optionType_localTime+ let lt y mo d h mi s = T.LocalTime (T.fromGregorian y mo d) (T.TimeOfDay h mi s)+ let rlt y mo d h mi s = Right (lt y mo d h mi s)+ $expect $ equal (parse "2011-12-13 14:15") (rlt 2011 12 13 14 15 0)+ $expect $ equal (parse "2011-12-13 14:15:16") (rlt 2011 12 13 14 15 16)+ $expect $ equal (parse "2011-12-13 14:15:16.1718") (rlt 2011 12 13 14 15 16.1718)+ $expect $ equal (parse "2011-347 14:15") (rlt 2011 12 13 14 15 0)+ $expect $ equal (parse "20111213 14:15") (rlt 2011 12 13 14 15 0)+ $expect $ equal (parse "20111213T141516") (rlt 2011 12 13 14 15 16)+ $expect $ equal (parse "20111213T141516.1718") (rlt 2011 12 13 14 15 16.1718)+ $expect $ equal+ (parse "bogus")+ (Left "\"bogus\" could not be parsed as a local time.")+ $expect $ equal (format (lt 2011 12 13 14 15 0)) "2011-12-13 14:15:00"+ $expect $ equal (format (lt 2011 12 13 14 15 16)) "2011-12-13 14:15:16"+ $expect $ equal (format (lt 2011 12 13 14 15 16.1718)) "2011-12-13 14:15:16.1718"++test_UtcTime :: Test+test_UtcTime = assertions "utc time" $ do+ let parse = optionTypeParse optionType_utcTime+ let format = optionTypeShow optionType_utcTime+ let ut y mo d h mi s = T.UTCTime (T.fromGregorian y mo d) (h*60*60 + mi*60 + s)+ let rut y mo d h mi s = Right (ut y mo d h mi s)+ $expect $ equal (parse "2011-12-13 14:15") (rut 2011 12 13 14 15 0)+ $expect $ equal (parse "2011-12-13 14:15:16") (rut 2011 12 13 14 15 16)+ $expect $ equal (parse "2011-12-13 14:15:16.1718") (rut 2011 12 13 14 15 16.1718)+ $expect $ equal (parse "2011-347 14:15") (rut 2011 12 13 14 15 0)+ $expect $ equal (parse "20111213 14:15") (rut 2011 12 13 14 15 0)+ $expect $ equal (parse "20111213T141516") (rut 2011 12 13 14 15 16)+ $expect $ equal (parse "20111213T141516.1718") (rut 2011 12 13 14 15 16.1718)+ $expect $ equal (parse "2011-12-13 23:59:60") (rut 2011 12 13 23 59 60)+ $expect $ equal+ (parse "bogus")+ (Left "\"bogus\" could not be parsed as a UTC time.")+ $expect $ equal+ (parse "2011-12-13 23:59:61")+ (Left "\"2011-12-13 23:59:61\" could not be parsed as a UTC time.")+ $expect $ equal (format (ut 2011 12 13 14 15 0)) "2011-12-13 14:15:00"+ $expect $ equal (format (ut 2011 12 13 14 15 16)) "2011-12-13 14:15:16"+ $expect $ equal (format (ut 2011 12 13 14 15 16.1718)) "2011-12-13 14:15:16.1718"++test_ZonedTime :: Test+test_ZonedTime = assertions "zoned time" $ do+ let parse = optionTypeParse optionType_zonedTime+ let format = optionTypeShow optionType_zonedTime+ let zt y mo d h mi s tz = T.ZonedTime (T.LocalTime (T.fromGregorian y mo d) (T.TimeOfDay h mi s)) tz+ let rzt y mo d h mi s tz = Right (zt y mo d h mi s tz)+ let noZone = T.TimeZone 0 False ""+ let tzPlus60 = T.TimeZone 60 False ""+ let tzMinus60 = T.TimeZone (-60) False ""+ let tzABC = T.TimeZone 0 False "ABC"+ $expect $ equal (parse "2011-12-13 14:15") (rzt 2011 12 13 14 15 0 noZone)+ $expect $ equal (parse "2011-12-13 14:15Z") (rzt 2011 12 13 14 15 0 T.utc)+ $expect $ equal (parse "2011-12-13 14:15+0100") (rzt 2011 12 13 14 15 0 tzPlus60)+ $expect $ equal (parse "2011-12-13 14:15 +0100") (rzt 2011 12 13 14 15 0 tzPlus60)+ $expect $ equal (parse "2011-12-13 14:15-0100") (rzt 2011 12 13 14 15 0 tzMinus60)+ $expect $ equal (parse "2011-12-13 14:15 -0100") (rzt 2011 12 13 14 15 0 tzMinus60)+ $expect $ equal (parse "2011-12-13 14:15 ABC") (rzt 2011 12 13 14 15 0 tzABC)+ $expect $ equal (parse "2011-12-13 14:15") (rzt 2011 12 13 14 15 0 noZone)+ $expect $ equal (parse "2011-12-13 14:15:16") (rzt 2011 12 13 14 15 16 noZone)+ $expect $ equal (parse "2011-12-13 14:15:16.1718") (rzt 2011 12 13 14 15 16.1718 noZone)+ $expect $ equal (parse "2011-347 14:15") (rzt 2011 12 13 14 15 0 noZone)+ $expect $ equal (parse "20111213 14:15") (rzt 2011 12 13 14 15 0 noZone)+ $expect $ equal (parse "20111213T141516") (rzt 2011 12 13 14 15 16 noZone)+ $expect $ equal (parse "20111213T141516.1718") (rzt 2011 12 13 14 15 16.1718 noZone)+ $expect $ equal+ (parse "bogus")+ (Left "\"bogus\" could not be parsed as a zoned time.")+ $expect $ equal (format (zt 2011 12 13 14 15 0 noZone)) "2011-12-13 14:15:00 +0000"+ $expect $ equal (format (zt 2011 12 13 14 15 0 T.utc)) "2011-12-13 14:15:00 UTC"+ $expect $ equal (format (zt 2011 12 13 14 15 0 tzPlus60)) "2011-12-13 14:15:00 +0100"+ $expect $ equal (format (zt 2011 12 13 14 15 0 tzMinus60)) "2011-12-13 14:15:00 -0100"+ $expect $ equal (format (zt 2011 12 13 14 15 16 noZone)) "2011-12-13 14:15:16 +0000"+ $expect $ equal (format (zt 2011 12 13 14 15 16.1718 noZone)) "2011-12-13 14:15:16.1718 +0000"++instance Eq T.ZonedTime where+ x == y = key x == key y where+ key t = (T.zonedTimeToLocalTime t, T.zonedTimeZone t)++main :: IO ()+main = Test.Chell.defaultMain [suite "tests"+ [ test_Duration+ , test_Date+ , test_Time+ , test_LocalTime+ , test_UtcTime+ , test_ZonedTime+ ]]