packages feed

hodatime-1.1.0.0: tests/HodaTime/WithCalendarTest.hs

{-# LANGUAGE FlexibleContexts #-}    -- for the polymorphic 'ymd' helper's Enum (MoY d) constraint
{-# LANGUAGE ScopedTypeVariables #-}
module HodaTime.WithCalendarTest
(
  withCalendarTests
)
where

import Test.Tasty
import Test.Tasty.HUnit
import Data.Maybe (fromJust)

import Data.HodaTime.CalendarDate (withCalendar, day, month, year, CalendarDate, HasDate, MoY)
import qualified Data.HodaTime.CalendarDateTime as CDT
import qualified Data.HodaTime.ZonedDateTime as Z
import qualified Data.HodaTime.Calendar.Gregorian as G
import qualified Data.HodaTime.Calendar.Julian as J
import qualified Data.HodaTime.Calendar.Coptic as C
import qualified Data.HodaTime.Calendar.Persian as P
import qualified Data.HodaTime.Calendar.Islamic as I
import Data.HodaTime.Instant (fromSecondsSinceUnixEpoch)
import Data.HodaTime.TimeZone (utc)

withCalendarTests :: TestTree
withCalendarTests = testGroup "withCalendar Tests" [dateTests, dateTimeTests, zonedTests]

-- | Decode any 'HasDate' value to (day, 1-based month, year).
ymd :: (HasDate d, Enum (MoY d)) => d -> (Int, Int, Int)
ymd x = (day x, succ . fromEnum $ month x, year x)

mkG :: Int -> G.Month G.Gregorian -> Int -> CalendarDate G.Gregorian
mkG d m y = fromJust $ G.calendarDate d m y

mkJ :: Int -> J.Month J.Julian -> Int -> CalendarDate J.Julian
mkJ d m y = fromJust $ J.calendarDate d m y

dateTests :: TestTree
dateTests = testGroup "CalendarDate"
  [
     testCase "Gregorian 17.Jun.2020 -> Julian 4.Jun.2020 (13 days behind)" $
       ymd (withCalendar (mkG 17 G.June 2020) :: CalendarDate J.Julian) @?= (4, 6, 2020)
    ,testCase "Gregorian 11.Sep.2021 -> Coptic 1 Thout 1738 (Nayrouz)" $
       ymd (withCalendar (mkG 11 G.September 2021) :: CalendarDate C.Coptic) @?= (1, 1, 1738)
    ,testCase "Julian 29.Aug.284 -> Coptic 1 Thout 1 (Coptic epoch)" $
       ymd (withCalendar (mkJ 29 J.August 284) :: CalendarDate C.Coptic) @?= (1, 1, 1)
    ,testCase "Gregorian 21.Mar.2021 -> Persian 1 Farvardin 1400 (Nowruz)" $
       ymd (withCalendar (mkG 21 G.March 2021) :: CalendarDate P.Persian) @?= (1, 1, 1400)
    ,testCase "Julian 15.Jul.622 -> Islamic 1 Muharram 1 (Hijri epoch)" $
       ymd (withCalendar (mkJ 15 J.July 622) :: CalendarDate I.IslamicBcl) @?= (1, 1, 1)
    ,testCase "Gregorian -> Islamic -> Gregorian round-trips" $
       let g = mkG 17 G.June 2020
       in (withCalendar (withCalendar g :: CalendarDate I.IslamicBcl) :: CalendarDate G.Gregorian) @?= g
    ,testCase "Gregorian -> Julian -> Gregorian round-trips" $
       let g = mkG 17 G.June 2020
       in (withCalendar (withCalendar g :: CalendarDate J.Julian) :: CalendarDate G.Gregorian) @?= g
  ]

dateTimeTests :: TestTree
dateTimeTests = testGroup "CalendarDateTime"
  [
     testCase "Gregorian 17.Jun.2020 (start of day) -> Julian 4.Jun.2020" $
       ymd (CDT.withCalendar (CDT.atStartOfDay (mkG 17 G.June 2020)) :: CDT.CalendarDateTime J.Julian) @?= (4, 6, 2020)
    ,testCase "Gregorian -> Julian -> Gregorian round-trips (keeps the LocalTime)" $
       let cdt = CDT.atStartOfDay (mkG 17 G.June 2020)
       in (CDT.withCalendar (CDT.withCalendar cdt :: CDT.CalendarDateTime J.Julian) :: CDT.CalendarDateTime G.Gregorian) @?= cdt
  ]

zonedTests :: TestTree
zonedTests = testGroup "ZonedDateTime"
  [
     testCase "same Instant (UTC), Gregorian view -> Julian view is 13 days behind" $ do
       tz <- utc
       let inst = fromSecondsSinceUnixEpoch 1592352000     -- 2020-06-17T00:00:00Z
           zdtG = Z.fromInstant inst tz :: Z.ZonedDateTime G.Gregorian
           zdtJ = Z.withCalendar zdtG :: Z.ZonedDateTime J.Julian
       (Z.year zdtJ, succ (fromEnum (Z.month zdtJ)), Z.day zdtJ) @?= (2020, 6, 4)
    ,testCase "round-trips back to the same Instant" $ do
       tz <- utc
       let inst = fromSecondsSinceUnixEpoch 1592352000
           zdtG = Z.fromInstant inst tz :: Z.ZonedDateTime G.Gregorian
           zdtJ = Z.withCalendar zdtG :: Z.ZonedDateTime J.Julian
       Z.toInstant zdtJ @?= inst
  ]