time 1.16 → 1.16.0.1
raw patch · 25 files changed
+1203/−148 lines, 25 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- changelog.md +10/−0
- configure +9/−9
- configure.ac +1/−1
- lib/Data/Format.hs +36/−7
- lib/Data/Time/Calendar/Easter.hs +1/−1
- lib/Data/Time/Calendar/Gregorian.hs +1/−1
- lib/Data/Time/Calendar/Julian.hs +1/−1
- lib/Data/Time/Clock/POSIX.hs +6/−1
- lib/Data/Time/Format/ISO8601.hs +1/−1
- lib/Data/Time/Format/Parse/Instances.hs +20/−9
- lib/Data/Time/LocalTime/Internal/Foreign.hs +22/−11
- lib/cbits/HsTime.c +3/−3
- test/ForeignCalls.hs +46/−0
- test/main/Test/Calendar/Duration.hs +16/−1
- test/main/Test/Calendar/Easter.hs +115/−27
- test/main/Test/Calendar/EasterData/easter-reference-1900-2099.csv +201/−0
- test/main/Test/Calendar/EasterData/easter500.txt +500/−0
- test/main/Test/Calendar/EasterRef.hs +0/−63
- test/main/Test/Clock/Conversion.hs +23/−0
- test/main/Test/Format/ISO8601.hs +7/−0
- test/main/Test/Format/ParseTime.hs +25/−1
- test/unix/Test/LocalTime/HsTime.h +14/−0
- test/unix/Test/LocalTime/TimeZone.hs +92/−8
- test/unix/Test/LocalTime/TimeZoneFallback.c +45/−0
- time.cabal +8/−3
changelog.md view
@@ -1,5 +1,15 @@ # Change Log +## [1.16.0.1] - 2026-07-20++- fix gregorianPaschalMoon and gregorianEaster+- fix negative rollover differences in diffGregorianDurationRollOver and diffJulianDurationRollOver+- fix case-insensitive parsing for AM/PM markers, UTC, and known timezone names+- fix Unix timezone fallback offset sign and DST abbreviation+- fix JavaScript getTimeZone offsets and summer flag around DST transitions+- fix numeric parsers accepting overflowing bounded values+- fix systemToPOSIXTime for SystemTime leap-second values+ ## [1.16] - 2026-05-03 - add periodIn
configure view
@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles.-# Generated by GNU Autoconf 2.71 for Haskell time package 1.16.+# Generated by GNU Autoconf 2.71 for Haskell time package 1.16.0.1. # # Report bugs to <ashley@semantic.org>. #@@ -610,8 +610,8 @@ # Identity of this package. PACKAGE_NAME='Haskell time package' PACKAGE_TARNAME='time'-PACKAGE_VERSION='1.16'-PACKAGE_STRING='Haskell time package 1.16'+PACKAGE_VERSION='1.16.0.1'+PACKAGE_STRING='Haskell time package 1.16.0.1' PACKAGE_BUGREPORT='ashley@semantic.org' PACKAGE_URL='' @@ -1258,7 +1258,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF-\`configure' configures Haskell time package 1.16 to adapt to many kinds of systems.+\`configure' configures Haskell time package 1.16.0.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1320,7 +1320,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in- short | recursive ) echo "Configuration of Haskell time package 1.16:";;+ short | recursive ) echo "Configuration of Haskell time package 1.16.0.1:";; esac cat <<\_ACEOF @@ -1406,7 +1406,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF-Haskell time package configure 1.16+Haskell time package configure 1.16.0.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc.@@ -1736,7 +1736,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Haskell time package $as_me 1.16, which was+It was created by Haskell time package $as_me 1.16.0.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw@@ -4334,7 +4334,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log="-This file was extended by Haskell time package $as_me 1.16, which was+This file was extended by Haskell time package $as_me 1.16.0.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES@@ -4389,7 +4389,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\-Haskell time package config.status 1.16+Haskell time package config.status 1.16.0.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\"
configure.ac view
@@ -1,4 +1,4 @@-AC_INIT([Haskell time package],[1.16],[ashley@semantic.org],[time])+AC_INIT([Haskell time package],[1.16.0.1],[ashley@semantic.org],[time]) # Safety check: Ensure that we are in the correct source directory. AC_CONFIG_SRCDIR([lib/include/HsTime.h])
lib/Data/Format.hs view
@@ -29,6 +29,7 @@ import Data.Char import Data.Void import Text.ParserCombinators.ReadP+import Text.Read (readMaybe) import Prelude hiding (fail) class IsoVariant f where@@ -213,13 +214,40 @@ readSign NegSign = option id $ char '-' >> return negate readSign PosNegSign = (char '+' >> return id) +++ (char '-' >> return negate) +readDigits :: Maybe Int -> ReadP String+readDigits mdigitcount =+ case mdigitcount of+ Just digitcount -> count digitcount $ satisfy isDigit+ Nothing -> munch1 isDigit++readMaybeP :: Read t => String -> ReadP t+readMaybeP str =+ case readMaybe str of+ Just t -> return t+ Nothing -> pfail++fromIntegerExact :: Integral t => Integer -> Maybe t+fromIntegerExact i =+ let+ t = fromInteger i+ in+ if toInteger t == i+ then Just t+ else Nothing++readIntegerNumber :: Integral t => SignOption -> Maybe Int -> ReadP t+readIntegerNumber signOpt mdigitcount = do+ sign <- readSign signOpt+ digits <- readDigits mdigitcount+ i <- readMaybeP digits+ case fromIntegerExact $ sign i of+ Just t -> return t+ Nothing -> pfail+ readNumber :: (Num t, Read t) => SignOption -> Maybe Int -> Bool -> ReadP t readNumber signOpt mdigitcount allowDecimal = do sign <- readSign signOpt- digits <-- case mdigitcount of- Just digitcount -> count digitcount $ satisfy isDigit- Nothing -> munch1 isDigit+ digits <- readDigits mdigitcount moredigits <- case allowDecimal of False -> return ""@@ -228,7 +256,8 @@ _ <- char '.' +++ char ',' dd <- munch1 isDigit return $ '.' : dd- return $ sign $ read $ digits ++ moredigits+ a <- readMaybeP $ digits ++ moredigits+ return $ sign a zeroPad :: Maybe Int -> String -> Maybe String zeroPad Nothing s = Just s@@ -269,8 +298,8 @@ PosNegSign -> '+' : s _ -> s -integerFormat :: (Show t, Read t, Num t) => SignOption -> Maybe Int -> Format t-integerFormat signOpt mdigitcount = MkFormat (showNumber signOpt mdigitcount) (readNumber signOpt mdigitcount False)+integerFormat :: (Show t, Integral t) => SignOption -> Maybe Int -> Format t+integerFormat signOpt mdigitcount = MkFormat (showNumber signOpt mdigitcount) (readIntegerNumber signOpt mdigitcount) decimalFormat :: (Show t, Read t, Num t) => SignOption -> Maybe Int -> Format t decimalFormat signOpt mdigitcount = MkFormat (showNumber signOpt mdigitcount) (readNumber signOpt mdigitcount True)
lib/Data/Time/Calendar/Easter.hs view
@@ -37,7 +37,7 @@ century = (div year 100) + 1 shiftedEpact = mod (14 + 11 * (mod year 19) - (div (3 * century) 4) + (div (5 + 8 * century) 25)) 30 adjustedEpact =- if shiftedEpact == 0 || ((shiftedEpact == 1) && (mod year 19 < 10))+ if shiftedEpact == 0 || ((shiftedEpact == 1) && (mod year 19 > 10)) then shiftedEpact + 1 else shiftedEpact
lib/Data/Time/Calendar/Gregorian.hs view
@@ -137,7 +137,7 @@ dayAllowed = addGregorianDurationRollOver (CalendarDiffDays mdiff 0) day1 dd = diffDays day2 dayAllowed in- if dd <= 0 then CalendarDiffDays mdiff dd else findpos (succ mdiff)+ if dd <= 0 then CalendarDiffDays mdiff dd else findneg (succ mdiff) in if day2 >= day1 then findpos ymdiff
lib/Data/Time/Calendar/Julian.hs view
@@ -168,7 +168,7 @@ dayAllowed = addJulianDurationRollOver (CalendarDiffDays mdiff 0) day1 dd = diffDays day2 dayAllowed in- if dd <= 0 then CalendarDiffDays mdiff dd else findpos (succ mdiff)+ if dd <= 0 then CalendarDiffDays mdiff dd else findneg (succ mdiff) in if day2 >= day1 then findpos ymdiff
lib/Data/Time/Clock/POSIX.hs view
@@ -45,7 +45,12 @@ (fromInteger (diffDays d systemEpochDay) * posixDayLength) + min posixDayLength (realToFrac t) systemToPOSIXTime :: SystemTime -> POSIXTime-systemToPOSIXTime (MkSystemTime s ns) = (fromIntegral s) + (fromIntegral ns) * 1E-9+systemToPOSIXTime (MkSystemTime s ns) =+ let+ (days, timeSeconds) = s `divMod` 86400+ time = fromIntegral timeSeconds + fromIntegral ns * 1E-9+ in+ fromIntegral days * posixDayLength + min posixDayLength time -- | Get the current POSIX time from the system clock. getPOSIXTime :: IO POSIXTime
lib/Data/Time/Format/ISO8601.hs view
@@ -328,7 +328,7 @@ timeAndOffsetFormat :: Format t -> FormatExtension -> Format (t, TimeZone) timeAndOffsetFormat ft fe = ft <**> timeOffsetFormat fe -intDesignator :: (Eq t, Show t, Read t, Num t) => Char -> Format t+intDesignator :: (Show t, Integral t) => Char -> Format t intDesignator c = optionalFormat 0 $ integerFormat NegSign Nothing <** literalFormat [c] decDesignator :: (Eq t, Show t, Read t, Num t) => Char -> Format t
lib/Data/Time/Format/Parse/Instances.hs view
@@ -122,15 +122,27 @@ readSpec_Z :: TimeLocale -> String -> Maybe TimeZone readSpec_Z _ str | Just offset <- readTzOffset str = Just $ TimeZone offset False "" readSpec_Z l str | Just zone <- getKnownTimeZone l str = Just zone-readSpec_Z _ "UTC" = Just utc+readSpec_Z _ str | eqCI str "UTC" = Just utc readSpec_Z _ [c] | Just zone <- getMilZone c = Just zone readSpec_Z _ _ = Nothing +eqCI :: String -> String -> Bool+eqCI x y = map toUpper x == map toUpper y++readIntegralMaybe :: Integral a => String -> Maybe a+readIntegralMaybe str = do+ i <- readMaybe str+ let+ a = fromInteger i+ if toInteger a == i+ then Just a+ else Nothing+ makeDayFact :: TimeLocale -> Char -> String -> Maybe [DayFact] makeDayFact l c x = let- ra :: Read a => Maybe a- ra = readMaybe x+ ra :: Integral a => Maybe a+ ra = readIntegralMaybe x zeroBasedListIndex :: [String] -> Maybe Int zeroBasedListIndex ss = elemIndex (map toUpper x) $ fmap (map toUpper) ss oneBasedListIndex :: [String] -> Maybe Int@@ -408,17 +420,16 @@ makeTimeFact :: TimeLocale -> Char -> String -> Maybe [TimeFact] makeTimeFact l c x = let- ra :: Read a => Maybe a- ra = readMaybe x+ ra :: Integral a => Maybe a+ ra = readIntegralMaybe x getAmPm = let- upx = map toUpper x (amStr, pmStr) = amPm l in- if upx == amStr+ if x `eqCI` amStr then Just [AMAPMTimeFact AM] else- if upx == pmStr+ if x `eqCI` pmStr then Just [AMAPMTimeFact PM] else Nothing in@@ -541,7 +552,7 @@ return $ TimeZone (hours * 60) False [yc] getKnownTimeZone :: TimeLocale -> String -> Maybe TimeZone-getKnownTimeZone locale x = find (\tz -> map toUpper x == timeZoneName tz) (knownTimeZones locale)+getKnownTimeZone locale x = find (\tz -> x `eqCI` timeZoneName tz) (knownTimeZones locale) instance ParseTime TimeZone where substituteTimeSpecifier _ = timeSubstituteTimeSpecifier
lib/Data/Time/LocalTime/Internal/Foreign.hs view
@@ -22,24 +22,35 @@ #if defined(javascript_HOST_ARCH) -foreign import javascript "((dy,dm,dd,th,tm,ts) => { return new Date(dy,dm,dd,th,tm,ts).getTimezoneOffset(); })"- js_get_timezone_minutes :: Int -> Int -> Int -> Int -> Int -> Int -> IO Int+foreign import javascript "((dy,dm,dd,th,tm,ts) => { const d = new Date(0); d.setUTCFullYear(dy,dm,dd); d.setUTCHours(th,tm,ts,0); return -d.getTimezoneOffset(); })"+ js_get_timezone_minutes :: Int -> Int -> Int -> Int -> Int -> Int -> IO Int -get_timezone_minutes :: UTCTime -> IO Int-get_timezone_minutes ut = let- lt :: LocalTime- lt = utcToLocalTime utc ut- in case lt of- LocalTime (YearMonthDay dy dm dd) (TimeOfDay th tm ts) ->- js_get_timezone_minutes (fromInteger dy) (pred dm) dd th tm (floor ts)+foreign import javascript "((dy,dm,dd,th,tm,ts) => { const d = new Date(0); d.setUTCFullYear(dy,dm,dd); d.setUTCHours(th,tm,ts,0); const jan = new Date(0); jan.setFullYear(d.getFullYear(),0,1); jan.setHours(0,0,0,0); const jul = new Date(0); jul.setFullYear(d.getFullYear(),6,1); jul.setHours(0,0,0,0); return d.getTimezoneOffset() < Math.max(jan.getTimezoneOffset(),jul.getTimezoneOffset()) ? 1 : 0; })"+ js_get_timezone_summer :: Int -> Int -> Int -> Int -> Int -> Int -> IO Int +get_timezone :: UTCTime -> IO (Int, Bool)+get_timezone ut =+ let+ lt :: LocalTime+ lt = utcToLocalTime utc ut+ in+ case lt of+ LocalTime (YearMonthDay dy dm dd) (TimeOfDay th tm ts) -> do+ let+ dy' = fromInteger dy+ dm' = pred dm+ ts' = floor ts+ mins <- js_get_timezone_minutes dy' dm' dd th tm ts'+ summer <- js_get_timezone_summer dy' dm' dd th tm ts'+ return (mins, summer /= 0)+ getTimeZoneCTime :: CTime -> IO TimeZone getTimeZoneCTime ct = do let ut :: UTCTime ut = posixSecondsToUTCTime $ secondsToNominalDiffTime $ fromIntegral $ fromCTime ct- mins <- get_timezone_minutes ut- return $ TimeZone mins False ""+ (mins, summer) <- get_timezone ut+ return $ TimeZone mins summer "" fromCTime :: CTime -> Int64 fromCTime (CTime tt) = fromIntegral tt
lib/cbits/HsTime.c view
@@ -36,14 +36,14 @@ return - (dst ? _timezone - 3600 : _timezone); #else # if HAVE_TZNAME || defined(__MHS__)- *pname = *tzname;+ *pname = tzname[dst ? 1 : 0]; # else # error "Don't know how to get timezone name on your OS" # endif # if HAVE_DECL_ALTZONE- return dst ? altzone : timezone;+ return - (dst ? altzone : timezone); # else- return dst ? timezone - 3600 : timezone;+ return - (dst ? timezone - 3600 : timezone); # endif #endif // HAVE_TM_ZONE }
test/ForeignCalls.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE CPP #-}+ module Main (main) where import Control.Exception@@ -10,12 +12,22 @@ import Data.Time.Clock.TAI import Data.Traversable import System.Exit+#if defined(javascript_HOST_ARCH)+import System.Environment+#endif import System.IO data Test = MkTest String (IO ()) tests :: [Test] tests =+ foreignCallTests+#if defined(javascript_HOST_ARCH)+ ++ jsTimezoneTests+#endif++foreignCallTests :: [Test]+foreignCallTests = [ MkTest "getCurrentTime" $ void $ getCurrentTime , MkTest "getZonedTime" $ void $ getZonedTime , MkTest "getCurrentTimeZone" $ void $ getCurrentTimeZone@@ -26,6 +38,40 @@ , MkTest "taiClock time" $ for_ taiClock $ \(_, getTime) -> void $ getTime , MkTest "taiClock resolution" $ for_ taiClock $ \(res, _) -> void $ evaluate res ]++#if defined(javascript_HOST_ARCH)+jsTimezoneTests :: [Test]+jsTimezoneTests =+ [ MkTest "getTimeZone JavaScript DST transition" $ do+ setEnv "TZ" "America/New_York"+ let+ transitionTime h m =+ UTCTime (fromGregorian 2024 3 10) $+ secondsToDiffTime $+ h * 3600 + m * 60+ zoneBefore <- getTimeZone $ transitionTime 6 30+ assertTimeZone "before DST transition" (-300) False zoneBefore+ zoneAfter <- getTimeZone $ transitionTime 7 30+ assertTimeZone "after DST transition" (-240) True zoneAfter+ ]++assertTimeZone :: String -> Int -> Bool -> TimeZone -> IO ()+assertTimeZone label expectedMinutes expectedSummer zone = do+ when (timeZoneMinutes zone /= expectedMinutes) $+ fail $+ label+ <> ": expected minutes "+ <> show expectedMinutes+ <> ", got "+ <> show (timeZoneMinutes zone)+ when (timeZoneSummerOnly zone /= expectedSummer) $+ fail $+ label+ <> ": expected summer flag "+ <> show expectedSummer+ <> ", got "+ <> show (timeZoneSummerOnly zone)+#endif runTest :: Test -> IO Bool runTest (MkTest name action) = do
test/main/Test/Calendar/Duration.hs view
@@ -62,6 +62,20 @@ "positive-diff" $ fmap testPositiveDiff addDiffs +testNegativeDiff :: CalendarAddDiff -> TestTree+testNegativeDiff (MkAddDiff{..}, _) = testProperty adName $ \day1 (MkSmallish i) ->+ let+ day2 = addDays (negate i) day1+ r = adDifference day2 day1+ in+ property $ cdMonths r <= 0 && cdDays r <= 0++testNegativeDiffs :: TestTree+testNegativeDiffs =+ testGroup+ "negative-diff"+ $ fmap testNegativeDiff addDiffs+ testSpecific :: CalendarAddDiff -> (Integer, Int, Int) -> (Integer, Int, Int) -> (Integer, Integer) -> TestTree testSpecific (MkAddDiff{..}, fromYMD) (y2, m2, d2) (y1, m1, d1) (em, ed) = let@@ -89,6 +103,7 @@ testGroup "specific" [ testSpecificPair (2017, 04, 07) (2017, 04, 07) (0, 0) (0, 0)+ , testSpecificPair (1999, 12, 31) (2000, 01, 01) (0, -1) (0, -1) , testSpecific gregorianClip (2017, 04, 07) (2017, 04, 01) (0, 6) , testSpecific gregorianClip (2017, 04, 01) (2017, 04, 07) (0, -6) , testSpecific gregorianClip (2017, 04, 07) (2017, 02, 01) (2, 6)@@ -103,4 +118,4 @@ ] testDuration :: TestTree-testDuration = testGroup "CalendarDiffDays" [testAddDiffs, testPositiveDiffs, testSpecifics]+testDuration = testGroup "CalendarDiffDays" [testAddDiffs, testPositiveDiffs, testNegativeDiffs, testSpecifics]
test/main/Test/Calendar/Easter.hs view
@@ -2,38 +2,126 @@ testEaster, ) where +import Data.Foldable import Data.Time.Calendar import Data.Time.Calendar.Easter-import Data.Time.Format-import Test.Calendar.EasterRef import Test.Tasty import Test.Tasty.HUnit+import Text.Read ----days :: [Day]-days = [ModifiedJulianDay 53000 .. ModifiedJulianDay 53014]+easterDatesFile :: FilePath+easterDatesFile = "test/main/Test/Calendar/EasterData/easter500.txt" -showWithWDay :: Day -> String-showWithWDay = formatTime defaultTimeLocale "%F %A"+allEasterDatesFile :: FilePath+allEasterDatesFile = "test/main/Test/Calendar/EasterData/easter-reference-1900-2099.csv" +parseField :: Read a => String -> IO a+parseField field =+ case readMaybe field of+ Just value -> return value+ Nothing -> fail $ "invalid integer field: " ++ show field++parseEasterDates :: [String] -> IO [Day]+parseEasterDates [] = return []+parseEasterDates (monthText : dayText : yearText : rest) = do+ month <- parseField monthText+ day <- parseField dayText+ year <- parseField yearText+ easterDay <-+ maybe (fail $ "invalid date: " ++ unwords [yearText, monthText, dayText]) return $+ fromGregorianValid year month day+ (easterDay :) <$> parseEasterDates rest+parseEasterDates fields = fail $ "easter500.txt: expected month/day/year triples, leftover fields: " ++ show fields++getEasterDates :: IO [Day]+getEasterDates = do+ contents <- readFile easterDatesFile+ parseEasterDates $ words contents++splitCommas :: String -> [String]+splitCommas [] = [""]+splitCommas (',' : rest) = "" : splitCommas rest+splitCommas (char : rest) =+ case splitCommas rest of+ [] -> [[char]]+ field : fields -> (char : field) : fields++testGregorianEaster :: TestTree+testGregorianEaster =+ testCase "Gregorian Easter" $ do+ easterDates <- getEasterDates+ for_ easterDates $ \expectedDate@(YearMonthDay y _ _) -> do+ let+ foundDate = gregorianEaster y+ assertEqual "Gregorian Easter" expectedDate foundDate++getAllEasterDates :: IO [(Day, Day, Day, Day)]+getAllEasterDates = do+ contents <- readFile allEasterDatesFile+ parseAllEasterDates $ lines contents++parseAllEasterDates :: [String] -> IO [(Day, Day, Day, Day)]+parseAllEasterDates [] = fail $ allEasterDatesFile ++ ": empty file"+parseAllEasterDates (header : rows) = do+ let+ expectedHeader =+ [ "year"+ , "golden_number"+ , "gregorian_pfm"+ , "gregorian_easter"+ , "orthodox_pfm_gregorian"+ , "orthodox_easter_gregorian"+ ]+ if splitCommas header == expectedHeader+ then traverse parseAllEasterDateLine $ zip [2 :: Int ..] rows+ else fail $ allEasterDatesFile ++ ": unexpected header: " ++ show header++parseAllEasterDateLine :: (Int, String) -> IO (Day, Day, Day, Day)+parseAllEasterDateLine (lineNumber, line) =+ case splitCommas line of+ [yearText, _, gregorianMoonText, gregorianEasterText, orthodoxMoonText, orthodoxEasterText] -> do+ year <- parseField yearText+ gregorianMoon <- parseDayField lineNumber "gregorian_pfm" gregorianMoonText+ gregorianEasterDay <- parseDayField lineNumber "gregorian_easter" gregorianEasterText+ orthodoxMoon <- parseDayField lineNumber "orthodox_pfm_gregorian" orthodoxMoonText+ orthodoxEasterDay <- parseDayField lineNumber "orthodox_easter_gregorian" orthodoxEasterText+ let+ YearMonthDay gregorianMoonYear _ _ = gregorianMoon+ if gregorianMoonYear == year+ then return (gregorianMoon, gregorianEasterDay, orthodoxMoon, orthodoxEasterDay)+ else fail $ allEasterDatesFile ++ ":" ++ show lineNumber ++ ": row year does not match Gregorian Paschal moon"+ fields -> fail $ allEasterDatesFile ++ ":" ++ show lineNumber ++ ": expected 6 CSV fields, found " ++ show (length fields)++parseDayField :: Int -> String -> String -> IO Day+parseDayField lineNumber fieldName field =+ case splitDate field of+ [yearText, monthText, dayText] -> do+ year <- parseField yearText+ month <- parseField monthText+ day <- parseField dayText+ maybe+ (fail $ allEasterDatesFile ++ ":" ++ show lineNumber ++ ": invalid " ++ fieldName ++ ": " ++ show field)+ return+ $ fromGregorianValid year month day+ _ -> fail $ allEasterDatesFile ++ ":" ++ show lineNumber ++ ": invalid " ++ fieldName ++ ": " ++ show field++splitDate :: String -> [String]+splitDate [] = [""]+splitDate ('-' : rest) = "" : splitDate rest+splitDate (char : rest) =+ case splitDate rest of+ [] -> [[char]]+ field : fields -> (char : field) : fields++testAllEasters :: TestTree+testAllEasters =+ testCase "all Easters" $ do+ allEasterDates <- getAllEasterDates+ for_ allEasterDates $ \(expectedGM@(YearMonthDay y _ _), expectedGE, expectedOM, expectedOE) -> do+ assertEqual "Gregorian Paschal moon" expectedGM $ gregorianPaschalMoon y+ assertEqual "Gregorian Easter" expectedGE $ gregorianEaster y+ assertEqual "Orthodox Paschal moon" expectedOM $ orthodoxPaschalMoon y+ assertEqual "Orthodox Easter" expectedOE $ orthodoxEaster y+ testEaster :: TestTree-testEaster =- testCase "testEaster" $- let- ds = unlines $ map (\day -> unwords [showWithWDay day, "->", showWithWDay (sundayAfter day)]) days- f y =- unwords- [ show y ++ ", Gregorian: moon,"- , show (gregorianPaschalMoon y) ++ ": Easter,"- , showWithWDay (gregorianEaster y)- ]- ++ "\n"- g y =- unwords- [ show y ++ ", Orthodox : moon,"- , show (orthodoxPaschalMoon y) ++ ": Easter,"- , showWithWDay (orthodoxEaster y)- ]- ++ "\n"- in- assertEqual "" testEasterRef $ ds ++ concatMap (\y -> f y ++ g y) [2000 .. 2020]+testEaster = testGroup "testEaster" [testGregorianEaster, testAllEasters]
+ test/main/Test/Calendar/EasterData/easter-reference-1900-2099.csv view
@@ -0,0 +1,201 @@+year,golden_number,gregorian_pfm,gregorian_easter,orthodox_pfm_gregorian,orthodox_easter_gregorian+1900,1,1900-04-14,1900-04-15,1900-04-18,1900-04-22+1901,2,1901-04-03,1901-04-07,1901-04-07,1901-04-14+1902,3,1902-03-23,1902-03-30,1902-04-26,1902-04-27+1903,4,1903-04-11,1903-04-12,1903-04-15,1903-04-19+1904,5,1904-03-31,1904-04-03,1904-04-04,1904-04-10+1905,6,1905-04-18,1905-04-23,1905-04-23,1905-04-30+1906,7,1906-04-08,1906-04-15,1906-04-12,1906-04-15+1907,8,1907-03-28,1907-03-31,1907-05-01,1907-05-05+1908,9,1908-04-16,1908-04-19,1908-04-20,1908-04-26+1909,10,1909-04-05,1909-04-11,1909-04-09,1909-04-11+1910,11,1910-03-25,1910-03-27,1910-04-28,1910-05-01+1911,12,1911-04-13,1911-04-16,1911-04-17,1911-04-23+1912,13,1912-04-02,1912-04-07,1912-04-06,1912-04-07+1913,14,1913-03-22,1913-03-23,1913-04-25,1913-04-27+1914,15,1914-04-10,1914-04-12,1914-04-14,1914-04-19+1915,16,1915-03-30,1915-04-04,1915-04-03,1915-04-04+1916,17,1916-04-17,1916-04-23,1916-04-22,1916-04-23+1917,18,1917-04-07,1917-04-08,1917-04-11,1917-04-15+1918,19,1918-03-27,1918-03-31,1918-04-30,1918-05-05+1919,1,1919-04-14,1919-04-20,1919-04-18,1919-04-20+1920,2,1920-04-03,1920-04-04,1920-04-07,1920-04-11+1921,3,1921-03-23,1921-03-27,1921-04-26,1921-05-01+1922,4,1922-04-11,1922-04-16,1922-04-15,1922-04-16+1923,5,1923-03-31,1923-04-01,1923-04-04,1923-04-08+1924,6,1924-04-18,1924-04-20,1924-04-23,1924-04-27+1925,7,1925-04-08,1925-04-12,1925-04-12,1925-04-19+1926,8,1926-03-28,1926-04-04,1926-05-01,1926-05-02+1927,9,1927-04-16,1927-04-17,1927-04-20,1927-04-24+1928,10,1928-04-05,1928-04-08,1928-04-09,1928-04-15+1929,11,1929-03-25,1929-03-31,1929-04-28,1929-05-05+1930,12,1930-04-13,1930-04-20,1930-04-17,1930-04-20+1931,13,1931-04-02,1931-04-05,1931-04-06,1931-04-12+1932,14,1932-03-22,1932-03-27,1932-04-25,1932-05-01+1933,15,1933-04-10,1933-04-16,1933-04-14,1933-04-16+1934,16,1934-03-30,1934-04-01,1934-04-03,1934-04-08+1935,17,1935-04-17,1935-04-21,1935-04-22,1935-04-28+1936,18,1936-04-07,1936-04-12,1936-04-11,1936-04-12+1937,19,1937-03-27,1937-03-28,1937-04-30,1937-05-02+1938,1,1938-04-14,1938-04-17,1938-04-18,1938-04-24+1939,2,1939-04-03,1939-04-09,1939-04-07,1939-04-09+1940,3,1940-03-23,1940-03-24,1940-04-26,1940-04-28+1941,4,1941-04-11,1941-04-13,1941-04-15,1941-04-20+1942,5,1942-03-31,1942-04-05,1942-04-04,1942-04-05+1943,6,1943-04-18,1943-04-25,1943-04-23,1943-04-25+1944,7,1944-04-08,1944-04-09,1944-04-12,1944-04-16+1945,8,1945-03-28,1945-04-01,1945-05-01,1945-05-06+1946,9,1946-04-16,1946-04-21,1946-04-20,1946-04-21+1947,10,1947-04-05,1947-04-06,1947-04-09,1947-04-13+1948,11,1948-03-25,1948-03-28,1948-04-28,1948-05-02+1949,12,1949-04-13,1949-04-17,1949-04-17,1949-04-24+1950,13,1950-04-02,1950-04-09,1950-04-06,1950-04-09+1951,14,1951-03-22,1951-03-25,1951-04-25,1951-04-29+1952,15,1952-04-10,1952-04-13,1952-04-14,1952-04-20+1953,16,1953-03-30,1953-04-05,1953-04-03,1953-04-05+1954,17,1954-04-17,1954-04-18,1954-04-22,1954-04-25+1955,18,1955-04-07,1955-04-10,1955-04-11,1955-04-17+1956,19,1956-03-27,1956-04-01,1956-04-30,1956-05-06+1957,1,1957-04-14,1957-04-21,1957-04-18,1957-04-21+1958,2,1958-04-03,1958-04-06,1958-04-07,1958-04-13+1959,3,1959-03-23,1959-03-29,1959-04-26,1959-05-03+1960,4,1960-04-11,1960-04-17,1960-04-15,1960-04-17+1961,5,1961-03-31,1961-04-02,1961-04-04,1961-04-09+1962,6,1962-04-18,1962-04-22,1962-04-23,1962-04-29+1963,7,1963-04-08,1963-04-14,1963-04-12,1963-04-14+1964,8,1964-03-28,1964-03-29,1964-05-01,1964-05-03+1965,9,1965-04-16,1965-04-18,1965-04-20,1965-04-25+1966,10,1966-04-05,1966-04-10,1966-04-09,1966-04-10+1967,11,1967-03-25,1967-03-26,1967-04-28,1967-04-30+1968,12,1968-04-13,1968-04-14,1968-04-17,1968-04-21+1969,13,1969-04-02,1969-04-06,1969-04-06,1969-04-13+1970,14,1970-03-22,1970-03-29,1970-04-25,1970-04-26+1971,15,1971-04-10,1971-04-11,1971-04-14,1971-04-18+1972,16,1972-03-30,1972-04-02,1972-04-03,1972-04-09+1973,17,1973-04-17,1973-04-22,1973-04-22,1973-04-29+1974,18,1974-04-07,1974-04-14,1974-04-11,1974-04-14+1975,19,1975-03-27,1975-03-30,1975-04-30,1975-05-04+1976,1,1976-04-14,1976-04-18,1976-04-18,1976-04-25+1977,2,1977-04-03,1977-04-10,1977-04-07,1977-04-10+1978,3,1978-03-23,1978-03-26,1978-04-26,1978-04-30+1979,4,1979-04-11,1979-04-15,1979-04-15,1979-04-22+1980,5,1980-03-31,1980-04-06,1980-04-04,1980-04-06+1981,6,1981-04-18,1981-04-19,1981-04-23,1981-04-26+1982,7,1982-04-08,1982-04-11,1982-04-12,1982-04-18+1983,8,1983-03-28,1983-04-03,1983-05-01,1983-05-08+1984,9,1984-04-16,1984-04-22,1984-04-20,1984-04-22+1985,10,1985-04-05,1985-04-07,1985-04-09,1985-04-14+1986,11,1986-03-25,1986-03-30,1986-04-28,1986-05-04+1987,12,1987-04-13,1987-04-19,1987-04-17,1987-04-19+1988,13,1988-04-02,1988-04-03,1988-04-06,1988-04-10+1989,14,1989-03-22,1989-03-26,1989-04-25,1989-04-30+1990,15,1990-04-10,1990-04-15,1990-04-14,1990-04-15+1991,16,1991-03-30,1991-03-31,1991-04-03,1991-04-07+1992,17,1992-04-17,1992-04-19,1992-04-22,1992-04-26+1993,18,1993-04-07,1993-04-11,1993-04-11,1993-04-18+1994,19,1994-03-27,1994-04-03,1994-04-30,1994-05-01+1995,1,1995-04-14,1995-04-16,1995-04-18,1995-04-23+1996,2,1996-04-03,1996-04-07,1996-04-07,1996-04-14+1997,3,1997-03-23,1997-03-30,1997-04-26,1997-04-27+1998,4,1998-04-11,1998-04-12,1998-04-15,1998-04-19+1999,5,1999-03-31,1999-04-04,1999-04-04,1999-04-11+2000,6,2000-04-18,2000-04-23,2000-04-23,2000-04-30+2001,7,2001-04-08,2001-04-15,2001-04-12,2001-04-15+2002,8,2002-03-28,2002-03-31,2002-05-01,2002-05-05+2003,9,2003-04-16,2003-04-20,2003-04-20,2003-04-27+2004,10,2004-04-05,2004-04-11,2004-04-09,2004-04-11+2005,11,2005-03-25,2005-03-27,2005-04-28,2005-05-01+2006,12,2006-04-13,2006-04-16,2006-04-17,2006-04-23+2007,13,2007-04-02,2007-04-08,2007-04-06,2007-04-08+2008,14,2008-03-22,2008-03-23,2008-04-25,2008-04-27+2009,15,2009-04-10,2009-04-12,2009-04-14,2009-04-19+2010,16,2010-03-30,2010-04-04,2010-04-03,2010-04-04+2011,17,2011-04-17,2011-04-24,2011-04-22,2011-04-24+2012,18,2012-04-07,2012-04-08,2012-04-11,2012-04-15+2013,19,2013-03-27,2013-03-31,2013-04-30,2013-05-05+2014,1,2014-04-14,2014-04-20,2014-04-18,2014-04-20+2015,2,2015-04-03,2015-04-05,2015-04-07,2015-04-12+2016,3,2016-03-23,2016-03-27,2016-04-26,2016-05-01+2017,4,2017-04-11,2017-04-16,2017-04-15,2017-04-16+2018,5,2018-03-31,2018-04-01,2018-04-04,2018-04-08+2019,6,2019-04-18,2019-04-21,2019-04-23,2019-04-28+2020,7,2020-04-08,2020-04-12,2020-04-12,2020-04-19+2021,8,2021-03-28,2021-04-04,2021-05-01,2021-05-02+2022,9,2022-04-16,2022-04-17,2022-04-20,2022-04-24+2023,10,2023-04-05,2023-04-09,2023-04-09,2023-04-16+2024,11,2024-03-25,2024-03-31,2024-04-28,2024-05-05+2025,12,2025-04-13,2025-04-20,2025-04-17,2025-04-20+2026,13,2026-04-02,2026-04-05,2026-04-06,2026-04-12+2027,14,2027-03-22,2027-03-28,2027-04-25,2027-05-02+2028,15,2028-04-10,2028-04-16,2028-04-14,2028-04-16+2029,16,2029-03-30,2029-04-01,2029-04-03,2029-04-08+2030,17,2030-04-17,2030-04-21,2030-04-22,2030-04-28+2031,18,2031-04-07,2031-04-13,2031-04-11,2031-04-13+2032,19,2032-03-27,2032-03-28,2032-04-30,2032-05-02+2033,1,2033-04-14,2033-04-17,2033-04-18,2033-04-24+2034,2,2034-04-03,2034-04-09,2034-04-07,2034-04-09+2035,3,2035-03-23,2035-03-25,2035-04-26,2035-04-29+2036,4,2036-04-11,2036-04-13,2036-04-15,2036-04-20+2037,5,2037-03-31,2037-04-05,2037-04-04,2037-04-05+2038,6,2038-04-18,2038-04-25,2038-04-23,2038-04-25+2039,7,2039-04-08,2039-04-10,2039-04-12,2039-04-17+2040,8,2040-03-28,2040-04-01,2040-05-01,2040-05-06+2041,9,2041-04-16,2041-04-21,2041-04-20,2041-04-21+2042,10,2042-04-05,2042-04-06,2042-04-09,2042-04-13+2043,11,2043-03-25,2043-03-29,2043-04-28,2043-05-03+2044,12,2044-04-13,2044-04-17,2044-04-17,2044-04-24+2045,13,2045-04-02,2045-04-09,2045-04-06,2045-04-09+2046,14,2046-03-22,2046-03-25,2046-04-25,2046-04-29+2047,15,2047-04-10,2047-04-14,2047-04-14,2047-04-21+2048,16,2048-03-30,2048-04-05,2048-04-03,2048-04-05+2049,17,2049-04-17,2049-04-18,2049-04-22,2049-04-25+2050,18,2050-04-07,2050-04-10,2050-04-11,2050-04-17+2051,19,2051-03-27,2051-04-02,2051-04-30,2051-05-07+2052,1,2052-04-14,2052-04-21,2052-04-18,2052-04-21+2053,2,2053-04-03,2053-04-06,2053-04-07,2053-04-13+2054,3,2054-03-23,2054-03-29,2054-04-26,2054-05-03+2055,4,2055-04-11,2055-04-18,2055-04-15,2055-04-18+2056,5,2056-03-31,2056-04-02,2056-04-04,2056-04-09+2057,6,2057-04-18,2057-04-22,2057-04-23,2057-04-29+2058,7,2058-04-08,2058-04-14,2058-04-12,2058-04-14+2059,8,2059-03-28,2059-03-30,2059-05-01,2059-05-04+2060,9,2060-04-16,2060-04-18,2060-04-20,2060-04-25+2061,10,2061-04-05,2061-04-10,2061-04-09,2061-04-10+2062,11,2062-03-25,2062-03-26,2062-04-28,2062-04-30+2063,12,2063-04-13,2063-04-15,2063-04-17,2063-04-22+2064,13,2064-04-02,2064-04-06,2064-04-06,2064-04-13+2065,14,2065-03-22,2065-03-29,2065-04-25,2065-04-26+2066,15,2066-04-10,2066-04-11,2066-04-14,2066-04-18+2067,16,2067-03-30,2067-04-03,2067-04-03,2067-04-10+2068,17,2068-04-17,2068-04-22,2068-04-22,2068-04-29+2069,18,2069-04-07,2069-04-14,2069-04-11,2069-04-14+2070,19,2070-03-27,2070-03-30,2070-04-30,2070-05-04+2071,1,2071-04-14,2071-04-19,2071-04-18,2071-04-19+2072,2,2072-04-03,2072-04-10,2072-04-07,2072-04-10+2073,3,2073-03-23,2073-03-26,2073-04-26,2073-04-30+2074,4,2074-04-11,2074-04-15,2074-04-15,2074-04-22+2075,5,2075-03-31,2075-04-07,2075-04-04,2075-04-07+2076,6,2076-04-18,2076-04-19,2076-04-23,2076-04-26+2077,7,2077-04-08,2077-04-11,2077-04-12,2077-04-18+2078,8,2078-03-28,2078-04-03,2078-05-01,2078-05-08+2079,9,2079-04-16,2079-04-23,2079-04-20,2079-04-23+2080,10,2080-04-05,2080-04-07,2080-04-09,2080-04-14+2081,11,2081-03-25,2081-03-30,2081-04-28,2081-05-04+2082,12,2082-04-13,2082-04-19,2082-04-17,2082-04-19+2083,13,2083-04-02,2083-04-04,2083-04-06,2083-04-11+2084,14,2084-03-22,2084-03-26,2084-04-25,2084-04-30+2085,15,2085-04-10,2085-04-15,2085-04-14,2085-04-15+2086,16,2086-03-30,2086-03-31,2086-04-03,2086-04-07+2087,17,2087-04-17,2087-04-20,2087-04-22,2087-04-27+2088,18,2088-04-07,2088-04-11,2088-04-11,2088-04-18+2089,19,2089-03-27,2089-04-03,2089-04-30,2089-05-01+2090,1,2090-04-14,2090-04-16,2090-04-18,2090-04-23+2091,2,2091-04-03,2091-04-08,2091-04-07,2091-04-08+2092,3,2092-03-23,2092-03-30,2092-04-26,2092-04-27+2093,4,2093-04-11,2093-04-12,2093-04-15,2093-04-19+2094,5,2094-03-31,2094-04-04,2094-04-04,2094-04-11+2095,6,2095-04-18,2095-04-24,2095-04-23,2095-04-24+2096,7,2096-04-08,2096-04-15,2096-04-12,2096-04-15+2097,8,2097-03-28,2097-03-31,2097-05-01,2097-05-05+2098,9,2098-04-16,2098-04-20,2098-04-20,2098-04-27+2099,10,2099-04-05,2099-04-12,2099-04-09,2099-04-12
+ test/main/Test/Calendar/EasterData/easter500.txt view
@@ -0,0 +1,500 @@+ 4 2 1600 + 4 22 1601 + 4 7 1602 + 3 30 1603 + 4 18 1604 + 4 10 1605 + 3 26 1606 + 4 15 1607 + 4 6 1608 + 4 19 1609 + 4 11 1610 + 4 3 1611 + 4 22 1612 + 4 7 1613 + 3 30 1614 + 4 19 1615 + 4 3 1616 + 3 26 1617 + 4 15 1618 + 3 31 1619 + 4 19 1620 + 4 11 1621 + 3 27 1622 + 4 16 1623 + 4 7 1624 + 3 30 1625 + 4 12 1626 + 4 4 1627 + 4 23 1628 + 4 15 1629 + 3 31 1630 + 4 20 1631 + 4 11 1632 + 3 27 1633 + 4 16 1634 + 4 8 1635 + 3 23 1636 + 4 12 1637 + 4 4 1638 + 4 24 1639 + 4 8 1640 + 3 31 1641 + 4 20 1642 + 4 5 1643 + 3 27 1644 + 4 16 1645 + 4 1 1646 + 4 21 1647 + 4 12 1648 + 4 4 1649 + 4 17 1650 + 4 9 1651 + 3 31 1652 + 4 13 1653 + 4 5 1654 + 3 28 1655 + 4 16 1656 + 4 1 1657 + 4 21 1658 + 4 13 1659 + 3 28 1660 + 4 17 1661 + 4 9 1662 + 3 25 1663 + 4 13 1664 + 4 5 1665 + 4 25 1666 + 4 10 1667 + 4 1 1668 + 4 21 1669 + 4 6 1670 + 3 29 1671 + 4 17 1672 + 4 2 1673 + 3 25 1674 + 4 14 1675 + 4 5 1676 + 4 18 1677 + 4 10 1678 + 4 2 1679 + 4 21 1680 + 4 6 1681 + 3 29 1682 + 4 18 1683 + 4 2 1684 + 4 22 1685 + 4 14 1686 + 3 30 1687 + 4 18 1688 + 4 10 1689 + 3 26 1690 + 4 15 1691 + 4 6 1692 + 3 22 1693 + 4 11 1694 + 4 3 1695 + 4 22 1696 + 4 7 1697 + 3 30 1698 + 4 19 1699 + 4 11 1700 + 3 27 1701 + 4 16 1702 + 4 8 1703 + 3 23 1704 + 4 12 1705 + 4 4 1706 + 4 24 1707 + 4 8 1708 + 3 31 1709 + 4 20 1710 + 4 5 1711 + 3 27 1712 + 4 16 1713 + 4 1 1714 + 4 21 1715 + 4 12 1716 + 3 28 1717 + 4 17 1718 + 4 9 1719 + 3 31 1720 + 4 13 1721 + 4 5 1722 + 3 28 1723 + 4 16 1724 + 4 1 1725 + 4 21 1726 + 4 13 1727 + 3 28 1728 + 4 17 1729 + 4 9 1730 + 3 25 1731 + 4 13 1732 + 4 5 1733 + 4 25 1734 + 4 10 1735 + 4 1 1736 + 4 21 1737 + 4 6 1738 + 3 29 1739 + 4 17 1740 + 4 2 1741 + 3 25 1742 + 4 14 1743 + 4 5 1744 + 4 18 1745 + 4 10 1746 + 4 2 1747 + 4 14 1748 + 4 6 1749 + 3 29 1750 + 4 11 1751 + 4 2 1752 + 4 22 1753 + 4 14 1754 + 3 30 1755 + 4 18 1756 + 4 10 1757 + 3 26 1758 + 4 15 1759 + 4 6 1760 + 3 22 1761 + 4 11 1762 + 4 3 1763 + 4 22 1764 + 4 7 1765 + 3 30 1766 + 4 19 1767 + 4 3 1768 + 3 26 1769 + 4 15 1770 + 3 31 1771 + 4 19 1772 + 4 11 1773 + 4 3 1774 + 4 16 1775 + 4 7 1776 + 3 30 1777 + 4 19 1778 + 4 4 1779 + 3 26 1780 + 4 15 1781 + 3 31 1782 + 4 20 1783 + 4 11 1784 + 3 27 1785 + 4 16 1786 + 4 8 1787 + 3 23 1788 + 4 12 1789 + 4 4 1790 + 4 24 1791 + 4 8 1792 + 3 31 1793 + 4 20 1794 + 4 5 1795 + 3 27 1796 + 4 16 1797 + 4 8 1798 + 3 24 1799 + 4 13 1800 + 4 5 1801 + 4 18 1802 + 4 10 1803 + 4 1 1804 + 4 14 1805 + 4 6 1806 + 3 29 1807 + 4 17 1808 + 4 2 1809 + 4 22 1810 + 4 14 1811 + 3 29 1812 + 4 18 1813 + 4 10 1814 + 3 26 1815 + 4 14 1816 + 4 6 1817 + 3 22 1818 + 4 11 1819 + 4 2 1820 + 4 22 1821 + 4 7 1822 + 3 30 1823 + 4 18 1824 + 4 3 1825 + 3 26 1826 + 4 15 1827 + 4 6 1828 + 4 19 1829 + 4 11 1830 + 4 3 1831 + 4 22 1832 + 4 7 1833 + 3 30 1834 + 4 19 1835 + 4 3 1836 + 3 26 1837 + 4 15 1838 + 3 31 1839 + 4 19 1840 + 4 11 1841 + 3 27 1842 + 4 16 1843 + 4 7 1844 + 3 23 1845 + 4 12 1846 + 4 4 1847 + 4 23 1848 + 4 8 1849 + 3 31 1850 + 4 20 1851 + 4 11 1852 + 3 27 1853 + 4 16 1854 + 4 8 1855 + 3 23 1856 + 4 12 1857 + 4 4 1858 + 4 24 1859 + 4 8 1860 + 3 31 1861 + 4 20 1862 + 4 5 1863 + 3 27 1864 + 4 16 1865 + 4 1 1866 + 4 21 1867 + 4 12 1868 + 3 28 1869 + 4 17 1870 + 4 9 1871 + 3 31 1872 + 4 13 1873 + 4 5 1874 + 3 28 1875 + 4 16 1876 + 4 1 1877 + 4 21 1878 + 4 13 1879 + 3 28 1880 + 4 17 1881 + 4 9 1882 + 3 25 1883 + 4 13 1884 + 4 5 1885 + 4 25 1886 + 4 10 1887 + 4 1 1888 + 4 21 1889 + 4 6 1890 + 3 29 1891 + 4 17 1892 + 4 2 1893 + 3 25 1894 + 4 14 1895 + 4 5 1896 + 4 18 1897 + 4 10 1898 + 4 2 1899 + 4 15 1900 + 4 7 1901 + 3 30 1902 + 4 12 1903 + 4 3 1904 + 4 23 1905 + 4 15 1906 + 3 31 1907 + 4 19 1908 + 4 11 1909 + 3 27 1910 + 4 16 1911 + 4 7 1912 + 3 23 1913 + 4 12 1914 + 4 4 1915 + 4 23 1916 + 4 8 1917 + 3 31 1918 + 4 20 1919 + 4 4 1920 + 3 27 1921 + 4 16 1922 + 4 1 1923 + 4 20 1924 + 4 12 1925 + 4 4 1926 + 4 17 1927 + 4 8 1928 + 3 31 1929 + 4 20 1930 + 4 5 1931 + 3 27 1932 + 4 16 1933 + 4 1 1934 + 4 21 1935 + 4 12 1936 + 3 28 1937 + 4 17 1938 + 4 9 1939 + 3 24 1940 + 4 13 1941 + 4 5 1942 + 4 25 1943 + 4 9 1944 + 4 1 1945 + 4 21 1946 + 4 6 1947 + 3 28 1948 + 4 17 1949 + 4 9 1950 + 3 25 1951 + 4 13 1952 + 4 5 1953 + 4 18 1954 + 4 10 1955 + 4 1 1956 + 4 21 1957 + 4 6 1958 + 3 29 1959 + 4 17 1960 + 4 2 1961 + 4 22 1962 + 4 14 1963 + 3 29 1964 + 4 18 1965 + 4 10 1966 + 3 26 1967 + 4 14 1968 + 4 6 1969 + 3 29 1970 + 4 11 1971 + 4 2 1972 + 4 22 1973 + 4 14 1974 + 3 30 1975 + 4 18 1976 + 4 10 1977 + 3 26 1978 + 4 15 1979 + 4 6 1980 + 4 19 1981 + 4 11 1982 + 4 3 1983 + 4 22 1984 + 4 7 1985 + 3 30 1986 + 4 19 1987 + 4 3 1988 + 3 26 1989 + 4 15 1990 + 3 31 1991 + 4 19 1992 + 4 11 1993 + 4 3 1994 + 4 16 1995 + 4 7 1996 + 3 30 1997 + 4 12 1998 + 4 4 1999 + 4 23 2000 + 4 15 2001 + 3 31 2002 + 4 20 2003 + 4 11 2004 + 3 27 2005 + 4 16 2006 + 4 8 2007 + 3 23 2008 + 4 12 2009 + 4 4 2010 + 4 24 2011 + 4 8 2012 + 3 31 2013 + 4 20 2014 + 4 5 2015 + 3 27 2016 + 4 16 2017 + 4 1 2018 + 4 21 2019 + 4 12 2020 + 4 4 2021 + 4 17 2022 + 4 9 2023 + 3 31 2024 + 4 20 2025 + 4 5 2026 + 3 28 2027 + 4 16 2028 + 4 1 2029 + 4 21 2030 + 4 13 2031 + 3 28 2032 + 4 17 2033 + 4 9 2034 + 3 25 2035 + 4 13 2036 + 4 5 2037 + 4 25 2038 + 4 10 2039 + 4 1 2040 + 4 21 2041 + 4 6 2042 + 3 29 2043 + 4 17 2044 + 4 9 2045 + 3 25 2046 + 4 14 2047 + 4 5 2048 + 4 18 2049 + 4 10 2050 + 4 2 2051 + 4 21 2052 + 4 6 2053 + 3 29 2054 + 4 18 2055 + 4 2 2056 + 4 22 2057 + 4 14 2058 + 3 30 2059 + 4 18 2060 + 4 10 2061 + 3 26 2062 + 4 15 2063 + 4 6 2064 + 3 29 2065 + 4 11 2066 + 4 3 2067 + 4 22 2068 + 4 14 2069 + 3 30 2070 + 4 19 2071 + 4 10 2072 + 3 26 2073 + 4 15 2074 + 4 7 2075 + 4 19 2076 + 4 11 2077 + 4 3 2078 + 4 23 2079 + 4 7 2080 + 3 30 2081 + 4 19 2082 + 4 4 2083 + 3 26 2084 + 4 15 2085 + 3 31 2086 + 4 20 2087 + 4 11 2088 + 4 3 2089 + 4 16 2090 + 4 8 2091 + 3 30 2092 + 4 12 2093 + 4 4 2094 + 4 24 2095 + 4 15 2096 + 3 31 2097 + 4 20 2098 + 4 12 2099
− test/main/Test/Calendar/EasterRef.hs
@@ -1,63 +0,0 @@-module Test.Calendar.EasterRef where--testEasterRef :: String-testEasterRef =- unlines- [ "2003-12-27 Saturday -> 2003-12-28 Sunday"- , "2003-12-28 Sunday -> 2004-01-04 Sunday"- , "2003-12-29 Monday -> 2004-01-04 Sunday"- , "2003-12-30 Tuesday -> 2004-01-04 Sunday"- , "2003-12-31 Wednesday -> 2004-01-04 Sunday"- , "2004-01-01 Thursday -> 2004-01-04 Sunday"- , "2004-01-02 Friday -> 2004-01-04 Sunday"- , "2004-01-03 Saturday -> 2004-01-04 Sunday"- , "2004-01-04 Sunday -> 2004-01-11 Sunday"- , "2004-01-05 Monday -> 2004-01-11 Sunday"- , "2004-01-06 Tuesday -> 2004-01-11 Sunday"- , "2004-01-07 Wednesday -> 2004-01-11 Sunday"- , "2004-01-08 Thursday -> 2004-01-11 Sunday"- , "2004-01-09 Friday -> 2004-01-11 Sunday"- , "2004-01-10 Saturday -> 2004-01-11 Sunday"- , "2000, Gregorian: moon, 2000-04-18: Easter, 2000-04-23 Sunday"- , "2000, Orthodox : moon, 2000-04-23: Easter, 2000-04-30 Sunday"- , "2001, Gregorian: moon, 2001-04-08: Easter, 2001-04-15 Sunday"- , "2001, Orthodox : moon, 2001-04-12: Easter, 2001-04-15 Sunday"- , "2002, Gregorian: moon, 2002-03-28: Easter, 2002-03-31 Sunday"- , "2002, Orthodox : moon, 2002-05-01: Easter, 2002-05-05 Sunday"- , "2003, Gregorian: moon, 2003-04-16: Easter, 2003-04-20 Sunday"- , "2003, Orthodox : moon, 2003-04-20: Easter, 2003-04-27 Sunday"- , "2004, Gregorian: moon, 2004-04-05: Easter, 2004-04-11 Sunday"- , "2004, Orthodox : moon, 2004-04-09: Easter, 2004-04-11 Sunday"- , "2005, Gregorian: moon, 2005-03-25: Easter, 2005-03-27 Sunday"- , "2005, Orthodox : moon, 2005-04-28: Easter, 2005-05-01 Sunday"- , "2006, Gregorian: moon, 2006-04-13: Easter, 2006-04-16 Sunday"- , "2006, Orthodox : moon, 2006-04-17: Easter, 2006-04-23 Sunday"- , "2007, Gregorian: moon, 2007-04-02: Easter, 2007-04-08 Sunday"- , "2007, Orthodox : moon, 2007-04-06: Easter, 2007-04-08 Sunday"- , "2008, Gregorian: moon, 2008-03-22: Easter, 2008-03-23 Sunday"- , "2008, Orthodox : moon, 2008-04-25: Easter, 2008-04-27 Sunday"- , "2009, Gregorian: moon, 2009-04-10: Easter, 2009-04-12 Sunday"- , "2009, Orthodox : moon, 2009-04-14: Easter, 2009-04-19 Sunday"- , "2010, Gregorian: moon, 2010-03-30: Easter, 2010-04-04 Sunday"- , "2010, Orthodox : moon, 2010-04-03: Easter, 2010-04-04 Sunday"- , "2011, Gregorian: moon, 2011-04-18: Easter, 2011-04-24 Sunday"- , "2011, Orthodox : moon, 2011-04-22: Easter, 2011-04-24 Sunday"- , "2012, Gregorian: moon, 2012-04-07: Easter, 2012-04-08 Sunday"- , "2012, Orthodox : moon, 2012-04-11: Easter, 2012-04-15 Sunday"- , "2013, Gregorian: moon, 2013-03-27: Easter, 2013-03-31 Sunday"- , "2013, Orthodox : moon, 2013-04-30: Easter, 2013-05-05 Sunday"- , "2014, Gregorian: moon, 2014-04-14: Easter, 2014-04-20 Sunday"- , "2014, Orthodox : moon, 2014-04-18: Easter, 2014-04-20 Sunday"- , "2015, Gregorian: moon, 2015-04-03: Easter, 2015-04-05 Sunday"- , "2015, Orthodox : moon, 2015-04-07: Easter, 2015-04-12 Sunday"- , "2016, Gregorian: moon, 2016-03-23: Easter, 2016-03-27 Sunday"- , "2016, Orthodox : moon, 2016-04-26: Easter, 2016-05-01 Sunday"- , "2017, Gregorian: moon, 2017-04-11: Easter, 2017-04-16 Sunday"- , "2017, Orthodox : moon, 2017-04-15: Easter, 2017-04-16 Sunday"- , "2018, Gregorian: moon, 2018-03-31: Easter, 2018-04-01 Sunday"- , "2018, Orthodox : moon, 2018-04-04: Easter, 2018-04-08 Sunday"- , "2019, Gregorian: moon, 2019-04-18: Easter, 2019-04-21 Sunday"- , "2019, Orthodox : moon, 2019-04-23: Easter, 2019-04-28 Sunday"- , "2020, Gregorian: moon, 2020-04-08: Easter, 2020-04-12 Sunday"- , "2020, Orthodox : moon, 2020-04-12: Easter, 2020-04-19 Sunday"- ]
test/main/Test/Clock/Conversion.hs view
@@ -3,6 +3,7 @@ ) where import Data.Time.Clock+import Data.Time.Clock.POSIX import Data.Time.Clock.System import Test.Tasty import Test.Tasty.HUnit@@ -17,6 +18,19 @@ [ testCase "systemToUTCTime" $ assertEqual (show ut) ut $ systemToUTCTime st , testCase "utcToSystemTime" $ assertEqual (show ut) st $ utcToSystemTime ut ]+ testPOSIXTime :: (SystemTime, POSIXTime) -> TestTree+ testPOSIXTime (st, pt) =+ testGroup+ (show st)+ [ testCase "expected" $+ assertEqual (show st) pt $+ systemToPOSIXTime st+ , testCase "matches systemToUTCTime" $+ assertEqual+ (show st)+ (utcTimeToPOSIXSeconds $ systemToUTCTime st)+ (systemToPOSIXTime st)+ ] in [ testPair (MkSystemTime 0 0, UTCTime systemEpochDay 0) , testPair (MkSystemTime 86399 0, UTCTime systemEpochDay 86399)@@ -24,4 +38,13 @@ , testPair (MkSystemTime 86399 1000000000, UTCTime systemEpochDay 86400) , testPair (MkSystemTime 86399 1999999999, UTCTime systemEpochDay 86400.999999999) , testPair (MkSystemTime 86400 0, UTCTime (succ systemEpochDay) 0)+ , testGroup+ "systemToPOSIXTime"+ [ testPOSIXTime (MkSystemTime 0 0, 0)+ , testPOSIXTime (MkSystemTime 86399 999999999, 86399.999999999)+ , testPOSIXTime (MkSystemTime 86399 1000000000, 86400)+ , testPOSIXTime (MkSystemTime 86399 1500000000, 86400)+ , testPOSIXTime (MkSystemTime 86399 1999999999, 86400)+ , testPOSIXTime (MkSystemTime 86400 0, 86400)+ ] ]
test/main/Test/Format/ISO8601.hs view
@@ -160,6 +160,9 @@ testReadFormat :: (Show t, Eq t) => String -> Format t -> String -> t -> TestTree testReadFormat name fmt str val = nameTest (name ++ ": " ++ str) $ assertEqual "" (Just val) $ formatParseM fmt str +testReadFormatFails :: (Show t, Eq t) => String -> Format t -> String -> TestTree+testReadFormatFails name fmt str = nameTest (name ++ ": " ++ str) $ assertEqual "" Nothing $ formatParseM fmt str+ testShowFormats :: TestTree testShowFormats = nameTest@@ -240,6 +243,10 @@ (recurringIntervalFormat (calendarFormat ExtendedFormat) durationDaysFormat) "R74/2015-06-13/P1Y2M7D" (74, fromGregorian 2015 6 13, CalendarDiffDays 14 7)+ , testReadFormatFails+ "recurringIntervalFormat etc."+ (recurringIntervalFormat (calendarFormat ExtendedFormat) (calendarFormat ExtendedFormat))+ "R18446744073709551616/2024-01-01/2024-01-02" , testShowReadFormat "timeOffsetFormat" iso8601Format "-06:30" (minutesToTimeZone (-390)) , testShowReadFormat "timeOffsetFormat" iso8601Format "-06:00" (minutesToTimeZone (-360)) , testReadFormat "timeOffsetFormat" iso8601Format "-06" (minutesToTimeZone (-360))
test/main/Test/Format/ParseTime.hs view
@@ -271,10 +271,34 @@ "particular" [ parseTest @Day True Nothing "%-d%-m%0Y" "2122012" -- ISSUE #232 , parseTest @Day True Nothing "%-d%-m%0Y" "2132012" -- ISSUE #232+ , Test.Tasty.HUnit.testCase "#303 lower-case AM/PM locale parses its own %p output" $+ assertEqual+ ""+ (Just $ TimeOfDay 13 0 0)+ (parseTimeM False lowerAmPmLocale "%I %p" "01 pm")+ , Test.Tasty.HUnit.testCase "#303 UTC zone parsing is case-insensitive" $+ assertEqual+ ""+ (Just utc)+ (parseTimeM False defaultTimeLocale "%Z" "utc")+ , Test.Tasty.HUnit.testCase "#303 known time zone names are case-insensitive" $+ assertEqual+ ""+ (Just lowerKnownZone)+ (parseTimeM False lowerKnownZoneLocale "%Z" "foo") ]+ where+ lowerAmPmLocale = defaultTimeLocale{amPm = ("am", "pm")}+ lowerKnownZone = TimeZone 90 False "foo"+ lowerKnownZoneLocale = defaultTimeLocale{knownTimeZones = [lowerKnownZone]} badParseTests :: TestTree-badParseTests = testGroup "bad" [parseTest False (Nothing :: Maybe Day) "%Y" ""]+badParseTests =+ testGroup+ "bad"+ [ parseTest False (Nothing :: Maybe Day) "%Y" ""+ , parseTest False (Nothing :: Maybe TimeOfDay) "%-H" "18446744073709551616"+ ] {- parseYMD :: Day -> TestTree
+ test/unix/Test/LocalTime/HsTime.h view
@@ -0,0 +1,14 @@+#ifndef TEST_UNIX_LOCALTIME_HSTIME_H+#define TEST_UNIX_LOCALTIME_HSTIME_H++#include <time.h>++#define HAVE_LOCALTIME_R 1+#define HAVE_TZSET 1+#define HAVE_TM_ZONE 0+#define HAVE_TZNAME 1+#define HAVE_DECL_ALTZONE 0++long int get_current_timezone_seconds(time_t, int *pdst, char const **pname);++#endif
test/unix/Test/LocalTime/TimeZone.hs view
@@ -1,19 +1,103 @@+{-# LANGUAGE ForeignFunctionInterface #-}+ module Test.LocalTime.TimeZone ( testTimeZone, ) where import Data.Time+import Foreign+import Foreign.C import System.Environment (setEnv) import Test.Tasty import Test.Tasty.HUnit +type GetCurrentTimezoneSeconds =+ CTime -> Ptr CInt -> Ptr CString -> IO CLong++foreign import ccall unsafe "test_time_zone_fallback_set_state"+ test_time_zone_fallback_set_state :: CLong -> CLong -> CInt -> IO ()++foreign import ccall unsafe "test_get_current_timezone_seconds_no_altzone"+ test_get_current_timezone_seconds_no_altzone :: GetCurrentTimezoneSeconds++foreign import ccall unsafe "test_get_current_timezone_seconds_altzone"+ test_get_current_timezone_seconds_altzone :: GetCurrentTimezoneSeconds+ testTimeZone :: TestTree testTimeZone =- testCase "getTimeZone respects TZ env var" $ do- let- epoch = UTCTime (ModifiedJulianDay 57000) 0- setEnv "TZ" "UTC+0"- zone1 <- getTimeZone epoch- setEnv "TZ" "EST+5"- zone2 <- getTimeZone epoch- assertBool "zone not changed" $ zone1 /= zone2+ testGroup+ "TimeZone"+ [ testCase "getTimeZone respects TZ env var" $ do+ let+ epoch = UTCTime (ModifiedJulianDay 57000) 0+ setEnv "TZ" "UTC+0"+ zone1 <- getTimeZone epoch+ setEnv "TZ" "EST+5"+ zone2 <- getTimeZone epoch+ assertBool "zone not changed" $ zone1 /= zone2+ , dependentTestGroup+ "get_current_timezone_seconds fallback"+ AllFinish+ [ testFallback+ "without altzone, standard time"+ test_get_current_timezone_seconds_no_altzone+ 18000+ 14400+ 0+ (-18000)+ "EST"+ , testFallback+ "without altzone, daylight time"+ test_get_current_timezone_seconds_no_altzone+ 18000+ 14400+ 1+ (-14400)+ "EDT"+ , testFallback+ "with altzone, standard time"+ test_get_current_timezone_seconds_altzone+ 18000+ 14400+ 0+ (-18000)+ "EST"+ , testFallback+ "with altzone, daylight time"+ test_get_current_timezone_seconds_altzone+ 18000+ 14400+ 1+ (-14400)+ "EDT"+ ]+ ]++testFallback ::+ String ->+ GetCurrentTimezoneSeconds ->+ CLong ->+ CLong ->+ CInt ->+ CLong ->+ String ->+ TestTree+testFallback+ name+ getTimezoneSeconds+ timezoneSeconds+ altzoneSeconds+ isDst+ expectedSeconds+ expectedName =+ testCase name $+ with 0 $ \pdst ->+ with nullPtr $ \pcname -> do+ test_time_zone_fallback_set_state timezoneSeconds altzoneSeconds isDst+ seconds <- getTimezoneSeconds 0 pdst pcname+ dst <- peek pdst+ cname <- peek pcname+ name' <- peekCString cname+ assertEqual "seconds" expectedSeconds seconds+ assertEqual "dst" isDst dst+ assertEqual "name" expectedName name'
+ test/unix/Test/LocalTime/TimeZoneFallback.c view
@@ -0,0 +1,45 @@+#include <time.h>++static int test_time_zone_fallback_isdst = 0;+static long int test_time_zone_fallback_timezone = 0;+static long int test_time_zone_fallback_altzone = 0;+static char test_time_zone_fallback_std_name[] = "EST";+static char test_time_zone_fallback_dst_name[] = "EDT";+static char *test_time_zone_fallback_tzname[] = {+ test_time_zone_fallback_std_name,+ test_time_zone_fallback_dst_name+};++void test_time_zone_fallback_set_state(long int timezone_seconds, long int altzone_seconds, int isdst)+{+ test_time_zone_fallback_timezone = timezone_seconds;+ test_time_zone_fallback_altzone = altzone_seconds;+ test_time_zone_fallback_isdst = isdst;+}++void test_time_zone_fallback_tzset(void)+{+}++struct tm *test_time_zone_fallback_localtime_r(const time_t *timer, struct tm *result)+{+ (void) timer;+ result->tm_isdst = test_time_zone_fallback_isdst;+ return result;+}++#define tzset test_time_zone_fallback_tzset+#define localtime_r test_time_zone_fallback_localtime_r+#define timezone test_time_zone_fallback_timezone+#define altzone test_time_zone_fallback_altzone+#define tzname test_time_zone_fallback_tzname++#define get_current_timezone_seconds test_get_current_timezone_seconds_no_altzone+#include "../../../../lib/cbits/HsTime.c"+#undef get_current_timezone_seconds++#undef HAVE_DECL_ALTZONE+#define HAVE_DECL_ALTZONE 1+#define get_current_timezone_seconds test_get_current_timezone_seconds_altzone+#include "../../../../lib/cbits/HsTime.c"+#undef get_current_timezone_seconds
time.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: time-version: 1.16+version: 1.16.0.1 stability: stable license: BSD-2-Clause license-file: LICENSE@@ -25,8 +25,11 @@ configure lib/include/HsTime.h lib/include/HsTimeConfig.h.in+ test/main/Test/Calendar/EasterData/*.csv+ test/main/Test/Calendar/EasterData/*.txt test/unix/Test/Format/*.c test/unix/Test/Format/*.h+ test/unix/Test/LocalTime/*.h extra-tmp-files: config.log config.status@@ -201,7 +204,6 @@ Test.Calendar.ConvertBack Test.Calendar.Duration Test.Calendar.Easter- Test.Calendar.EasterRef Test.Calendar.DayPeriod Test.Calendar.LongWeekYears Test.Calendar.LongWeekYearsRef@@ -264,7 +266,10 @@ ghc-options: -Wall -fwarn-tabs if !arch (wasm32) ghc-options: -threaded -rtsopts -with-rtsopts=-N- c-sources: test/unix/Test/Format/FormatStuff.c+ include-dirs: test/unix/Test/LocalTime+ c-sources:+ test/unix/Test/Format/FormatStuff.c+ test/unix/Test/LocalTime/TimeZoneFallback.c build-depends: base, deepseq,