packages feed

fuzzy-time-gen-0.3.0.0: test/Data/FuzzyTime/ResolveSpec.hs

module Data.FuzzyTime.ResolveSpec
  ( spec,
  )
where

import Data.FuzzyTime.Resolve
import Data.FuzzyTime.Types
import Data.FuzzyTime.Types.Gen ()
import Data.Time
import Test.Hspec
import Test.QuickCheck
import Test.Validity

spec :: Spec
spec = do
  describe "resolveLocalTimeForwards" $ do
    it "produces valid local times" $ producesValid2 resolveLocalTimeForwards

    it "works for unspecified noon, before noon" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (< midday)) $ \tod ->
          resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Noon)
            `shouldBe` Just (BothTimeAndDay (LocalTime ld midday))

    it "works for unspecified noon, after noon" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (>= midday)) $ \tod ->
          resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Noon)
            `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) midday))

    it "works for unspecified midnight" $
      forAllValid $ \ld ->
        forAllValid $ \tod ->
          resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Midnight)
            `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) midnight))

    it "works for unspecified morning, before morning" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (< morning)) $ \tod ->
          resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Morning)
            `shouldBe` Just (BothTimeAndDay (LocalTime ld morning))

    it "works for unspecified morning, after morning" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (>= morning)) $ \tod ->
          resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Morning)
            `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) morning))

    it "works for unspecified evening, before evening" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (< evening)) $ \tod ->
          resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Evening)
            `shouldBe` Just (BothTimeAndDay (LocalTime ld evening))

    it "works for unspecified evening, after evening" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (>= evening)) $ \tod ->
          resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Evening)
            `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) evening))

    describe "SameTime" $ do
      it "works like resolveDayForwards if the fuzzy time of day is SameTime" $
        forAllValid $ \lt@(LocalTime ld tod) ->
          forAllValid $ \fd ->
            resolveLocalTimeForwards lt (FuzzyLocalTimeBoth fd SameTime)
              `shouldBe` BothTimeAndDay <$> (LocalTime <$> resolveDayForwards ld fd <*> pure tod)

    describe "Yesterday" $ do
      it "works without diff" $
        forAllValid $ \lt@(LocalTime ld ltod) ->
          forAllValid $ \ftod ->
            resolveLocalTimeForwards lt (FuzzyLocalTimeBoth Yesterday ftod)
              `shouldBe` BothTimeAndDay <$> (LocalTime <$> resolveDayForwards ld Yesterday <*> resolveTimeOfDayForwards ltod ftod)

      it "works for noon yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Noon)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) midday))

      it "works for midnight yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Midnight)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) midnight))

      it "works for morning yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Morning)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) morning))

      it "works for evening yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Evening)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) evening))

    describe "Tomorrow" $ do
      it "works without diff" $
        forAllValid $ \lt@(LocalTime ld ltod) ->
          forAllValid $ \ftod ->
            resolveLocalTimeForwards lt (FuzzyLocalTimeBoth Tomorrow ftod)
              `shouldBe` BothTimeAndDay <$> (LocalTime <$> resolveDayForwards ld Tomorrow <*> resolveTimeOfDayForwards ltod ftod)

      it "works for noon tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Noon)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) midday))

      it "works for midnight tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Midnight)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) midnight))

      it "works for morning tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Morning)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) morning))

      it "works for evening tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeForwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Evening)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) evening))

  describe "resolveLocalTimeBackwards" $ do
    it "produces valid local times" $ producesValid2 resolveLocalTimeBackwards

    it "works for unspecified noon, before noon" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (< midday)) $ \tod ->
          resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Noon)
            `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) midday))

    it "works for unspecified noon, after noon" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (>= midday)) $ \tod ->
          resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Noon)
            `shouldBe` Just (BothTimeAndDay (LocalTime ld midday))

    it "works for unspecified midnight" $
      forAllValid $ \ld ->
        forAllValid $ \tod ->
          resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Midnight)
            `shouldBe` Just (BothTimeAndDay (LocalTime ld midnight))

    it "works for unspecified morning, before morning" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (< morning)) $ \tod ->
          resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Morning)
            `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) morning))

    it "works for unspecified morning, after morning" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (>= morning)) $ \tod ->
          resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Morning)
            `shouldBe` Just (BothTimeAndDay (LocalTime ld morning))

    it "works for unspecified evening, before evening" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (< evening)) $ \tod ->
          resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Evening)
            `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) evening))

    it "works for unspecified evening, after evening" $
      forAllValid $ \ld ->
        forAll (genValid `suchThat` (>= evening)) $ \tod ->
          resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeTimeOfDay Evening)
            `shouldBe` Just (BothTimeAndDay (LocalTime ld evening))

    describe "SameTime" $ do
      it "works like resolveDayBackwards if the fuzzy time of day is SameTime" $
        forAllValid $ \lt@(LocalTime ld tod) ->
          forAllValid $ \fd ->
            resolveLocalTimeBackwards lt (FuzzyLocalTimeBoth fd SameTime)
              `shouldBe` BothTimeAndDay <$> (LocalTime <$> resolveDayBackwards ld fd <*> pure tod)

    describe "Yesterday" $ do
      it "works without diff" $
        forAllValid $ \lt@(LocalTime ld ltod) ->
          forAllValid $ \ftod ->
            resolveLocalTimeBackwards lt (FuzzyLocalTimeBoth Yesterday ftod)
              `shouldBe` BothTimeAndDay <$> (LocalTime <$> resolveDayBackwards ld Yesterday <*> resolveTimeOfDayBackwards ltod ftod)

      it "works for noon yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Noon)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) midday))

      it "works for midnight yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Midnight)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) midnight))

      it "works for morning yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Morning)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) morning))

      it "works for evening yesterday" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Yesterday Evening)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays (-1) ld) evening))

    describe "Tomorrow" $ do
      it "works without diff" $
        forAllValid $ \lt@(LocalTime ld ltod) ->
          forAllValid $ \ftod ->
            resolveLocalTimeBackwards lt (FuzzyLocalTimeBoth Tomorrow ftod)
              `shouldBe` BothTimeAndDay <$> (LocalTime <$> resolveDayBackwards ld Tomorrow <*> resolveTimeOfDayBackwards ltod ftod)

      it "works for noon tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Noon)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) midday))

      it "works for midnight tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Midnight)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) midnight))

      it "works for morning tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Morning)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) morning))

      it "works for evening tomorrow" $
        forAllValid $ \ld ->
          forAllValid $ \tod ->
            resolveLocalTimeBackwards (LocalTime ld tod) (FuzzyLocalTimeBoth Tomorrow Evening)
              `shouldBe` Just (BothTimeAndDay (LocalTime (addDays 1 ld) evening))

  describe "normaliseTimeOfDay" $ do
    it "produces valid times of day" $ producesValid3 normaliseTimeOfDay

    it "works for this example of tomorrow" $
      normaliseTimeOfDay 25 0 0 `shouldBe` (1, TimeOfDay 1 0 0)

    it "works for this example of tomorrow" $
      normaliseTimeOfDay 23 120 0 `shouldBe` (1, TimeOfDay 1 0 0)

    it "works for this example of tomorrow" $
      normaliseTimeOfDay 23 0 7200 `shouldBe` (1, TimeOfDay 1 0 0)

    it "works for this example of tomorrow" $
      normaliseTimeOfDay 23 120 7200 `shouldBe` (1, TimeOfDay 3 0 0)

    it "works for this example of yesterday" $
      normaliseTimeOfDay (-1) 0 0 `shouldBe` (-1, TimeOfDay 23 0 0)

    it "works for this example of yesterday" $
      normaliseTimeOfDay 0 (-1) 0 `shouldBe` (-1, TimeOfDay 23 59 0)

    it "works for this example of yesterday" $
      normaliseTimeOfDay 0 0 (-1) `shouldBe` (-1, TimeOfDay 23 59 59)

    it "works for this example of yesterday" $
      normaliseTimeOfDay 0 0 (-0.01) `shouldBe` (-1, TimeOfDay 23 59 59.99)

    it "works for this example of yesterday" $
      normaliseTimeOfDay 0 0 (-0.00001) `shouldBe` (-1, TimeOfDay 23 59 59.99999)

    it "works for this example of yesterday" $
      normaliseTimeOfDay 0 0 (-0.00000001) `shouldBe` (-1, TimeOfDay 23 59 59.99999999)

    it "does something sensible with leap seconds" $
      normaliseTimeOfDay 23 59 60 `shouldBe` (1, TimeOfDay 0 0 0)

  describe "resolveTimeOfDayForwards" $ do
    it "produces valid times of day" $
      producesValid2 resolveTimeOfDayForwards

    it "works for sametime " $
      forAllValid $ \tod ->
        resolveTimeOfDayForwards tod SameTime `shouldBe` Just tod

    it "works for noon " $
      forAllValid $ \tod ->
        resolveTimeOfDayForwards tod Noon `shouldBe` Just (TimeOfDay 12 0 0)

    it "works for midnight " $
      forAllValid $ \tod ->
        resolveTimeOfDayForwards tod Midnight `shouldBe` Just (TimeOfDay 0 0 0)

    it "works for morning " $
      forAllValid $ \tod ->
        resolveTimeOfDayForwards tod Morning `shouldBe` Just (TimeOfDay 6 0 0)

    it "works for evening" $
      forAllValid $ \tod ->
        resolveTimeOfDayForwards tod Evening `shouldBe` Just (TimeOfDay 18 0 0)

    it "works for atHour" $
      forAllValid $ \tod ->
        forAllValid $ \h ->
          resolveTimeOfDayForwards tod (AtHour h) `shouldBe` Just (TimeOfDay h 0 0)

    it "works for atMinute" $
      forAllValid $ \tod ->
        forAllValid $ \h ->
          forAllValid $ \m ->
            resolveTimeOfDayForwards tod (AtMinute h m) `shouldBe` Just (TimeOfDay h m 0)

    it "works for atExact" $
      forAllValid $ \tod1 ->
        forAllValid $ \tod2 ->
          resolveTimeOfDayForwards tod1 (AtExact tod2) `shouldBe` Just tod2

    xdescribe "Don't actually hold" $ do
      it "has an inverse with (small) hoursDiff" $
        forAllValid $ \tod ->
          forAll (choose (-(24 - 1), 24 - 1)) $ \hd ->
            ( resolveTimeOfDayForwards tod (HoursDiff hd)
                >>= (\tod' -> resolveTimeOfDayForwards tod' (HoursDiff (-hd)))
            )
              `shouldBe` Just tod

      it "has an inverse with (small) minutesDiff" $
        forAllValid $ \tod ->
          forAll (choose (-(24 * 60 - 1), 24 * 60 - 1)) $ \md ->
            ( resolveTimeOfDayForwards tod (MinutesDiff md)
                >>= (\tod' -> resolveTimeOfDayForwards tod' (MinutesDiff (-md)))
            )
              `shouldBe` Just tod

      it "has an inverse with (small) secondsDiff" $
        forAllValid $ \tod ->
          forAll (max (-1000) . min 1000 <$> genValid) $ \sd ->
            ( resolveTimeOfDayForwards tod (SecondsDiff sd)
                >>= (\tod' -> resolveTimeOfDayForwards tod' (SecondsDiff (-sd)))
            )
              `shouldBe` Just tod

  describe "resolveTimeOfDayBackwards" $ do
    it "produces valid times of day" $
      producesValid2 resolveTimeOfDayBackwards

    it "works for sametime " $
      forAllValid $ \tod ->
        resolveTimeOfDayBackwards tod SameTime `shouldBe` Just tod

    it "works for noon " $
      forAllValid $ \tod ->
        resolveTimeOfDayBackwards tod Noon `shouldBe` Just (TimeOfDay 12 0 0)

    it "works for midnight " $
      forAllValid $ \tod ->
        resolveTimeOfDayBackwards tod Midnight `shouldBe` Just (TimeOfDay 0 0 0)

    it "works for morning " $
      forAllValid $ \tod ->
        resolveTimeOfDayBackwards tod Morning `shouldBe` Just (TimeOfDay 6 0 0)

    it "works for evening" $
      forAllValid $ \tod ->
        resolveTimeOfDayBackwards tod Evening `shouldBe` Just (TimeOfDay 18 0 0)

    it "works for atHour" $
      forAllValid $ \tod ->
        forAllValid $ \h ->
          resolveTimeOfDayBackwards tod (AtHour h) `shouldBe` Just (TimeOfDay h 0 0)

    it "works for atMinute" $
      forAllValid $ \tod ->
        forAllValid $ \h ->
          forAllValid $ \m ->
            resolveTimeOfDayBackwards tod (AtMinute h m) `shouldBe` Just (TimeOfDay h m 0)

    it "works for atExact" $
      forAllValid $ \tod1 ->
        forAllValid $ \tod2 ->
          resolveTimeOfDayBackwards tod1 (AtExact tod2) `shouldBe` Just tod2

    xdescribe "Don't actually hold" $ do
      it "has an inverse with (small) hoursDiff" $
        forAllValid $ \tod ->
          forAll (choose (-(24 - 1), 24 - 1)) $ \hd ->
            ( resolveTimeOfDayBackwards tod (HoursDiff hd)
                >>= (\tod' -> resolveTimeOfDayBackwards tod' (HoursDiff (-hd)))
            )
              `shouldBe` Just tod

      it "has an inverse with (small) minutesDiff" $
        forAllValid $ \tod ->
          forAll (choose (-(24 * 60 - 1), 24 * 60 - 1)) $ \md ->
            ( resolveTimeOfDayBackwards tod (MinutesDiff md)
                >>= (\tod' -> resolveTimeOfDayBackwards tod' (MinutesDiff (-md)))
            )
              `shouldBe` Just tod

      it "has an inverse with (small) secondsDiff" $
        forAllValid $ \tod ->
          forAll (max (-1000) . min 1000 <$> genValid) $ \sd ->
            ( resolveTimeOfDayBackwards tod (SecondsDiff sd)
                >>= (\tod' -> resolveTimeOfDayBackwards tod' (SecondsDiff (-sd)))
            )
              `shouldBe` Just tod

  describe "nextDayOfMonth" $ do
    it "produces valid days" $
      producesValid2 nextDayOfMonth

    it "finds next days" $
      forAllValid $ \d ->
        forAllValid $ \di ->
          case nextDayOfMonth d di of
            Nothing -> pure () -- Fine
            Just d' -> d' `shouldSatisfy` (> d)

    it "finds the next 17th of the month in january" $
      nextDayOfMonth (fromGregorian 2023 02 01) 17
        `shouldBe` Just (fromGregorian 2023 02 17)

    it "finds the next 17th of the month in january" $
      nextDayOfMonth (fromGregorian 2023 02 18) 17
        `shouldBe` Just (fromGregorian 2023 03 17)

  describe "previousDayOfMonth" $ do
    it "produces valid days" $
      producesValid2 previousDayOfMonth

    it "finds previous days" $
      forAllValid $ \d ->
        forAllValid $ \di ->
          case previousDayOfMonth d di of
            Nothing -> pure () -- Fine
            Just d' -> d' `shouldSatisfy` (< d)

    it "finds the previous 17th of the month in january" $
      previousDayOfMonth (fromGregorian 2023 02 01) 17
        `shouldBe` Just (fromGregorian 2023 01 17)

    it "finds the previous 17th of the month in january" $
      previousDayOfMonth (fromGregorian 2023 02 18) 17
        `shouldBe` Just (fromGregorian 2023 02 17)

  describe "nextDayOfMonthOfYear" $ do
    it "produces valid days" $
      producesValid3 nextDayOfMonthOfYear

    it "finds next days" $
      forAllValid $ \d ->
        forAllValid $ \w ->
          forAllValid $ \di ->
            case nextDayOfMonthOfYear d w di of
              Nothing -> pure () -- Fine
              Just d' -> d' `shouldSatisfy` (> d)

    it "finds the next 17th of the month in march" $
      nextDayOfMonthOfYear (fromGregorian 2023 02 01) 03 17
        `shouldBe` Just (fromGregorian 2023 03 17)

    it "finds the next 17th of the month in january" $
      nextDayOfMonthOfYear (fromGregorian 2023 02 18) 01 17
        `shouldBe` Just (fromGregorian 2024 01 17)

  describe "previousDayOfMonthOfYear" $ do
    it "produces valid days" $
      producesValid3 previousDayOfMonthOfYear

    it "finds previous days" $
      forAllValid $ \d ->
        forAllValid $ \w ->
          forAllValid $ \di ->
            case previousDayOfMonthOfYear d w di of
              Nothing -> pure () -- Fine
              Just d' -> d' `shouldSatisfy` (< d)

    it "finds the previous 17th of the month in march" $
      previousDayOfMonthOfYear (fromGregorian 2023 02 01) 03 17
        `shouldBe` Just (fromGregorian 2022 03 17)

    it "finds the previous 17th of the month in january" $
      previousDayOfMonthOfYear (fromGregorian 2023 02 18) 01 17
        `shouldBe` Just (fromGregorian 2023 01 17)

  describe "nextDayOfWeek" $ do
    it "produces valid values" $
      producesValid2 nextDayOfWeek

    it "finds next days" $
      forAllValid $ \d ->
        forAllValid $ \dow ->
          nextDayOfWeek d dow `shouldSatisfy` (> d)

    it "finds the day in this week" $
      nextDayOfWeek (fromGregorian 2024 01 18) Friday
        `shouldBe` fromGregorian 2024 01 19

    it "finds the day in next week" $
      nextDayOfWeek (fromGregorian 2024 01 18) Monday
        `shouldBe` fromGregorian 2024 01 22

  describe "previousDayOfWeek" $ do
    it "produces valid values" $
      producesValid2 previousDayOfWeek

    it "finds previous days" $
      forAllValid $ \d ->
        forAllValid $ \dow ->
          previousDayOfWeek d dow `shouldSatisfy` (< d)

    it "finds the day in this week" $
      previousDayOfWeek (fromGregorian 2024 01 18) Friday
        `shouldBe` fromGregorian 2024 01 12

    it "finds the day in next week" $
      previousDayOfWeek (fromGregorian 2024 01 18) Monday
        `shouldBe` fromGregorian 2024 01 15

  describe "resolveDayForwards" $ do
    it "produces valid days" $ producesValid2 resolveDayForwards

    it "works for this example for Yesterday" $
      resolveDayForwards (fromGregorian 2000 6 25) Yesterday
        `shouldBe` Just (fromGregorian 2000 6 24)

    it "is id for Now" $
      forAllValid $ \d ->
        resolveDayForwards d Now `shouldBe` Just d

    it "is id for Today" $
      forAllValid $ \d ->
        resolveDayForwards d Today `shouldBe` Just d

    it "works for this example for Tomorrow" $
      resolveDayForwards (fromGregorian 2001 6 23) Tomorrow
        `shouldBe` Just (fromGregorian 2001 6 24)

    it "produces valid values when given 'OnlyDay' values" $
      forAllValid $ \d ->
        forAllShrink ((OnlyDay <$> genValid) `suchThat` isValid) shrinkValid $ \fd ->
          shouldBeValid $ resolveDayForwards d fd

    it "works for OnlyDay for this example where the current date is before the given day" $
      resolveDayForwards (fromGregorian 2001 6 23) (OnlyDay 24)
        `shouldBe` Just (fromGregorian 2001 6 24)

    it "works for OnlyDay for this example where the current date is after the given day" $
      resolveDayForwards (fromGregorian 2001 6 23) (OnlyDay 5)
        `shouldBe` Just (fromGregorian 2001 7 5)

    it "works for OnlyDay for this example where the following given day is not in this month" $
      resolveDayForwards (fromGregorian 2001 2 23) (OnlyDay 29)
        `shouldBe` Just (fromGregorian 2001 3 29)

    it "does not work for OnlyDay for this example where the following given day is not in next month" $
      resolveDayForwards (fromGregorian 2001 1 30) (OnlyDay 29)
        `shouldBe` Nothing

    it "works for OnlyDay for this example where the following given day is not in next month" $
      resolveDayForwards (fromGregorian 2001 12 30) (OnlyDay 5)
        `shouldBe` Just (fromGregorian 2002 1 5)

    it "produces valid values when given 'DayInMonth' values" $
      forAllValid $ \d ->
        forAllShrink (((\(mi, di) -> DayInMonth (max 0 (min 12 mi)) (max 0 (min 31 di))) <$> genValid) `suchThat` isValid) shrinkValid $ \fd ->
          shouldBeValid $ resolveDayForwards d fd

    it "works for DayInMonth for this example where the current date is before the given day" $
      resolveDayForwards (fromGregorian 2001 6 23) (DayInMonth 6 24)
        `shouldBe` Just (fromGregorian 2001 6 24)

    it "works for DayInMonth for this example where the current date is after the given day" $
      resolveDayForwards (fromGregorian 2001 6 23) (DayInMonth 6 5)
        `shouldBe` Just (fromGregorian 2002 6 5)

    it "works for DayInMonth for this example accross years" $
      resolveDayForwards (fromGregorian 2001 1 30) (DayInMonth 1 5)
        `shouldBe` Just (fromGregorian 2002 1 5)

    it "does not work for DayInMonth for this example for february 29th" $
      resolveDayForwards (fromGregorian 2001 1 30) (DayInMonth 2 29)
        `shouldBe` Nothing

    it "produces valid values when given 'DayOfTheWeek' values" $
      forAllValid $ \d ->
        forAllShrink ((DayOfTheWeek <$> genValid <*> genValid) `suchThat` isValid) shrinkValid $ \fd ->
          shouldBeValid $ resolveDayForwards d fd

    it "works for DayOfTheWeek with a day of the week after today in the current week" $
      resolveDayForwards (fromGregorian 2018 10 9) (DayOfTheWeek Thursday 0)
        `shouldBe` Just (fromGregorian 2018 10 11)

    it "works for DayOfTheWeek with a day of the week after today in the next week" $
      resolveDayForwards (fromGregorian 2018 10 9) (DayOfTheWeek Monday 0)
        `shouldBe` Just (fromGregorian 2018 10 15)
    it
      "works for DayOfTheWeek with a day of the week after today in the current week at the end of the year"
      $ resolveDayForwards (fromGregorian 2020 12 30) (DayOfTheWeek Saturday 0)
        `shouldBe` Just (fromGregorian 2021 01 02)
    it
      "works for DayOfTheWeek with a day of the week after today in the next week at the end of the year"
      $ resolveDayForwards (fromGregorian 2020 12 30) (DayOfTheWeek Tuesday 0)
        `shouldBe` Just (fromGregorian 2021 01 05)

    it "works for DayOfTheWeek with an extra diff" $
      resolveDayForwards (fromGregorian 2018 10 9) (DayOfTheWeek Thursday 2)
        `shouldBe` Just (fromGregorian 2018 10 25)

  describe "resolveDayBackwards" $ do
    it "produces valid days" $ producesValid2 resolveDayBackwards

    it "works for this example for Yesterday" $
      resolveDayBackwards (fromGregorian 2000 6 25) Yesterday
        `shouldBe` Just (fromGregorian 2000 6 24)

    it "is id for Now" $
      forAllValid $ \d ->
        resolveDayBackwards d Now `shouldBe` Just d

    it "is id for Today" $
      forAllValid $ \d ->
        resolveDayBackwards d Today `shouldBe` Just d

    it "works for this example for Tomorrow" $
      resolveDayBackwards (fromGregorian 2001 6 23) Tomorrow
        `shouldBe` Just (fromGregorian 2001 6 24)

    it "produces valid values when given 'OnlyDay' values" $
      forAllValid $ \d ->
        forAllShrink ((OnlyDay <$> genValid) `suchThat` isValid) shrinkValid $ \fd ->
          shouldBeValid $ resolveDayBackwards d fd

    it "works for OnlyDay for this example where the current date is before the given day" $
      resolveDayBackwards (fromGregorian 2001 6 23) (OnlyDay 24)
        `shouldBe` Just (fromGregorian 2001 5 24)

    it "works for OnlyDay for this example where the current date is after the given day" $
      resolveDayBackwards (fromGregorian 2001 6 23) (OnlyDay 5)
        `shouldBe` Just (fromGregorian 2001 6 5)

    it "works for OnlyDay for this example where the following given day is not in this month" $
      resolveDayBackwards (fromGregorian 2001 2 23) (OnlyDay 29)
        `shouldBe` Just (fromGregorian 2001 1 29)

    it "does not work for OnlyDay for this example where the following given day is not in previous month" $
      resolveDayBackwards (fromGregorian 2001 3 25) (OnlyDay 29)
        `shouldBe` Nothing

    it "works for OnlyDay for this example where the following given day is not in previous month" $
      resolveDayBackwards (fromGregorian 2002 01 02) (OnlyDay 5)
        `shouldBe` Just (fromGregorian 2001 12 5)

    it "produces valid values when given 'DayInMonth' values" $
      forAllValid $ \d ->
        forAllShrink (((\(mi, di) -> DayInMonth (max 0 (min 12 mi)) (max 0 (min 31 di))) <$> genValid) `suchThat` isValid) shrinkValid $ \fd ->
          shouldBeValid $ resolveDayBackwards d fd

    it "works for DayInMonth for this example where the current date is before the given day" $
      resolveDayBackwards (fromGregorian 2001 6 23) (DayInMonth 6 24)
        `shouldBe` Just (fromGregorian 2000 6 24)

    it "works for DayInMonth for this example where the current date is after the given day" $
      resolveDayBackwards (fromGregorian 2001 6 23) (DayInMonth 6 5)
        `shouldBe` Just (fromGregorian 2001 6 5)

    it "works for DayInMonth for this example accross years" $
      resolveDayBackwards (fromGregorian 2002 1 03) (DayInMonth 1 5)
        `shouldBe` Just (fromGregorian 2001 1 5)

    it "does not work for DayInMonth for this example for february 29th" $
      resolveDayBackwards (fromGregorian 2002 3 20) (DayInMonth 2 29)
        `shouldBe` Nothing

    it "produces valid values when given 'DayInMonth' values" $
      forAllValid $ \d ->
        forAllShrink ((DayOfTheWeek <$> genValid <*> genValid) `suchThat` isValid) shrinkValid $ \fd ->
          shouldBeValid $ resolveDayBackwards d fd

    it "works for DayOfTheWeek with a day of the week after today in the current week" $
      resolveDayBackwards (fromGregorian 2018 10 9) (DayOfTheWeek Thursday 0)
        `shouldBe` Just (fromGregorian 2018 10 04)

    it "works for DayOfTheWeek with a day of the week after today in the previous week" $
      resolveDayBackwards (fromGregorian 2018 10 9) (DayOfTheWeek Monday 0)
        `shouldBe` Just (fromGregorian 2018 10 08)
    it
      "works for DayOfTheWeek with a day of the week after today in the current week at the end of the year"
      $ resolveDayBackwards (fromGregorian 2021 01 02) (DayOfTheWeek Saturday 0)
        `shouldBe` Just (fromGregorian 2020 12 26)
    it
      "works for DayOfTheWeek with a day of the week after today in the previous week at the end of the year"
      $ resolveDayBackwards (fromGregorian 2021 01 01) (DayOfTheWeek Tuesday 0)
        `shouldBe` Just (fromGregorian 2020 12 29)

    it "works for DayOfTheWeek with a day of the week after today in the current week" $
      resolveDayBackwards (fromGregorian 2018 10 9) (DayOfTheWeek Thursday (-2))
        `shouldBe` Just (fromGregorian 2018 09 20)