packages feed

holidays (empty) → 0.1.0.0

raw patch · 19 files changed

+890/−0 lines, 19 filesdep +basedep +containersdep +holidays

Dependencies added: base, containers, holidays, tasty, tasty-hunit, tasty-quickcheck, text, time

Files

+ CHANGELOG.md view
@@ -0,0 +1,7 @@+# Revision history for holidays++## **0.1.0.0** - 2025-02-04+- small EDSL for describing holidays+- ZAF, NAM, USA, GBR, DEU+- support for country regions+- unit + property based tests
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2024 Daniel Cabral++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.
+ holidays.cabal view
@@ -0,0 +1,63 @@+cabal-version: 3.8++name: holidays+version: 0.1.0.0+synopsis: Library for country public holidays+description:+  Library for country public holidays. Provides a small EDSL to describe holidays for a country.+category: Date+homepage: https://github.com/danielc777888/holidays+license: MIT+license-file: LICENSE+author: Daniel Cabral+maintainer: danielc777888@gmail.com+extra-doc-files: CHANGELOG.md++tested-with: GHC == 9.4.8++source-repository head+  type: git+  location: git://github.com/danielc777888/holidays.git++common common+  ghc-options: -Wall+  build-depends:+    base ^>=4.17.2.1,+    containers ^>=0.7,+    text ^>=2.1,+    time >=1.4 && <2+  default-language: GHC2021++library+  import: common+  exposed-modules:+    Holidays+  other-modules:+    Holidays.Base+    Holidays.DateFinder+    Holidays.DateTransform+    Holidays.Germany+    Holidays.Namibia+    Holidays.SouthAfrica+    Holidays.UnitedKingdom+    Holidays.UnitedStates+  hs-source-dirs: src++test-suite tests+  import: common+  type: exitcode-stdio-1.0+  build-depends:+    holidays,+    tasty >=1.5,+    tasty-hunit >=0.10,+    tasty-quickcheck >=0.10,+    text ^>=2.1+  hs-source-dirs: tst+  main-is: Main.hs+  other-modules:+    Test.Holidays+    Test.Holidays.Germany+    Test.Holidays.Namibia+    Test.Holidays.SouthAfrica+    Test.Holidays.UnitedKingdom+    Test.Holidays.UnitedStates
+ src/Holidays.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE OverloadedStrings #-}++{- |+This module determines public holidays based on country code and year.+-}+module Holidays (+  ISO_3166_1_Alpha_3,+  Region,+  holidays,+)+where++import Data.Set qualified as S+import Data.Time+import Holidays.Base+import Holidays.DateFinder+import Holidays.DateTransform+import Holidays.Germany qualified as DEU+import Holidays.Namibia qualified as NAM+import Holidays.SouthAfrica qualified as ZAF+import Holidays.UnitedKingdom qualified as GBR+import Holidays.UnitedStates qualified as USA++{- |+Returns a set of public holidays based on the country code (ISO_3166_1_Alpha_3) and a specific year.+If a country is not supported an empty set is returned.+Allowed to specify regions in a country to further determine holidays.++Examples:++@+holidays "DEU" ["BW","BY","BE"] 2025 -- Germany and various states+holidays "USA" [] 2025+@+-}+holidays :: ISO_3166_1_Alpha_3 -> [Region] -> Year -> S.Set Day+holidays countryCode regions year =+  case countryCode of+    "DEU" -> DEU.holidays regions `apply` year+    "GBR" -> GBR.holidays `apply` year+    "NAM" -> NAM.holidays `apply` year+    "USA" -> USA.holidays `apply` year+    "ZAF" -> ZAF.holidays `apply` year+    _ -> S.empty++-- Applies year and transformations to holidays+apply :: (DateFinders, DateTransforms) -> Year -> S.Set Day+apply (finders, transforms) year =+  let validDays = filter valid $ map (\d -> d year) finders -- apply year and filter out invalid days+  in  foldr (\d ds -> S.insert (foldr (\t d' -> t ds d') d transforms) ds) S.empty validDays -- apply transforms to valid days
+ src/Holidays/Base.hs view
@@ -0,0 +1,17 @@+module Holidays.Base (+  ISO_3166_1_Alpha_3,+  Region,+  day,+) where++import Data.Text qualified as T+import Data.Time++-- | 3-letter country codes+type ISO_3166_1_Alpha_3 = T.Text++-- | Custom regions for a country+type Region = T.Text++day :: Year -> MonthOfYear -> DayOfMonth -> Day+day = fromGregorian
+ src/Holidays/DateFinder.hs view
@@ -0,0 +1,174 @@+module Holidays.DateFinder (+  after,+  before,+  valid,+  days,+  years,+  sun,+  mon,+  tues,+  wed,+  thurs,+  fri,+  sat,+  jan,+  feb,+  mar,+  apr,+  may,+  jun,+  jul,+  aug,+  sep,+  oct,+  nov,+  dec,+  easterSunday,+  easterMonday,+  goodFriday,+  christmasDay,+  boxingDay,+  newYearsDay,+  ascensionDay,+  workersDay,+  nextOpenDay,+  DateFinders,+) where++import Data.Maybe+import Data.Set qualified as S+import Data.Time++import Data.Time.Calendar.Easter+import Holidays.Base++data Direction = Past | Future++type DateFinders = [Year -> Day]++-- common dates+newYearsDay :: Year -> Day+newYearsDay = jan 1++easterSunday :: Year -> Day+easterSunday = gregorianEaster++easterMonday :: Year -> Day+easterMonday = addDays 1 . easterSunday++goodFriday :: Year -> Day+goodFriday = (1 `fri`) . before . easterSunday++christmasDay :: Year -> Day+christmasDay = dec 25++boxingDay :: Year -> Day+boxingDay = dec 26++ascensionDay :: Year -> Day+ascensionDay = (39 `days`) . after . easterSunday++workersDay :: Year -> Day+workersDay = may 1++-- utils+nullDate :: Day+nullDate = day 0 0 0++valid :: Day -> Bool+valid = not . (nullDate ==)++days :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+days n f = f n Nothing++years :: (Year -> Bool) -> Day -> Day+years f d = if f (fromIntegral y) then d else nullDate+  where+    (y, _, _) = toGregorian d++-- months+jan :: DayOfMonth -> Year -> Day+jan d y = day y January d++feb :: DayOfMonth -> Year -> Day+feb d y = day y February d++mar :: DayOfMonth -> Year -> Day+mar d y = day y March d++apr :: DayOfMonth -> Year -> Day+apr d y = day y April d++may :: DayOfMonth -> Year -> Day+may d y = day y May d++jun :: DayOfMonth -> Year -> Day+jun d y = day y June d++jul :: DayOfMonth -> Year -> Day+jul d y = day y July d++aug :: DayOfMonth -> Year -> Day+aug d y = day y August d++sep :: DayOfMonth -> Year -> Day+sep d y = day y September d++oct :: DayOfMonth -> Year -> Day+oct d y = day y October d++nov :: DayOfMonth -> Year -> Day+nov d y = day y November d++dec :: DayOfMonth -> Year -> Day+dec d y = day y December d++-- time travel++-- exclusive of before day+before :: Day -> Integer -> Maybe DayOfWeek -> Day+before = timeTravel Past++-- inclusive of after day+after :: Day -> Integer -> Maybe DayOfWeek -> Day+after = timeTravel Future++timeTravel :: Direction -> Day -> Integer -> Maybe DayOfWeek -> Day+timeTravel Past d n w+  | isNothing w = addDays (negate n) d+  | otherwise =+      let diff = fromIntegral $ if dayOfWeek d == fromJust w then 7 else dayOfWeekDiff (dayOfWeek d) (fromJust w)+      in  addDays (negate diff - ((n - 1) * 7)) d+timeTravel Future d n w+  | isNothing w = addDays n d+  | otherwise =+      let diff = fromIntegral $ dayOfWeekDiff (fromJust w) (dayOfWeek d)+      in  addDays (diff + ((n - 1) * 7)) d++-- week days+sun :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+sun n f = f n (Just Sunday)++mon :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+mon n f = f n (Just Monday)++tues :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+tues n f = f n (Just Tuesday)++wed :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+wed n f = f n (Just Wednesday)++thurs :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+thurs n f = f n (Just Thursday)++fri :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+fri n f = f n (Just Friday)++sat :: Integer -> (Integer -> Maybe DayOfWeek -> Day) -> Day+sat n f = f n (Just Saturday)++nextOpenDay :: [DayOfWeek] -> S.Set Day -> Day -> Day+nextOpenDay ds s d+  | dayOfWeek d `elem` ds = nextOpenDay ds s (addDays 1 d)+  | S.member d s = nextOpenDay ds s (addDays 1 d)+  | otherwise = d
+ src/Holidays/DateTransform.hs view
@@ -0,0 +1,19 @@+module Holidays.DateTransform (+  substituteRule,+  sundayRule,+  DateTransforms+) where++import Data.Set qualified as S+import Data.Time+import Holidays.DateFinder+++type DateTransforms = [S.Set Day -> Day -> Day]++-- general transformations+sundayRule :: S.Set Day -> Day -> Day+sundayRule _ d = if dayOfWeek d == Sunday then addDays 1 d else d -- mon after sunday++substituteRule :: S.Set Day -> Day -> Day+substituteRule = nextOpenDay [Saturday, Sunday]
+ src/Holidays/Germany.hs view
@@ -0,0 +1,153 @@+{--+  references:+  https://en.wikipedia.org/wiki/Public_holidays_in_Germany+  https://publicholidays.de/+--}+{-# LANGUAGE OverloadedStrings #-}++module Holidays.Germany (+  holidays,+) where++import Holidays.Base+import Holidays.DateFinder+import Holidays.DateTransform++holidays :: [Region] -> (DateFinders, DateTransforms)+holidays regions =+  ( concat+      [ federalHolidays,+        regionalHolidays regions+      ],+    []+  )++federalHolidays :: DateFinders+federalHolidays =+  [ newYearsDay,+    goodFriday,+    easterMonday,+    workersDay, -- labour day+    ascensionDay,+    (50 `days`) . after . easterSunday, -- whit monday+    oct 3, -- germany unity day+    christmasDay,+    boxingDay -- second day of christmas+  ]++regionalHolidays :: [Region] -> DateFinders+regionalHolidays =+  concatMap+    ( \r -> case r of+        "BW" -> badenWurttembergHolidays+        "BY" -> bavariaHolidays+        "BE" -> berlinHolidays+        "BB" -> brandenburgHolidays+        "HB" -> bremenHolidays+        "HH" -> hamburgHolidays+        "HE" -> hesseHolidays+        "MV" -> mecklenburgVorpommernHolidays+        "NI" -> lowerSaxonyHolidays+        "NW" -> northRhineWestphaliaHolidays+        "RP" -> rhinelandPalatinateHolidays+        "SL" -> saarlandHolidays+        "SN" -> saxonyHolidays+        "ST" -> saxonyAnhaltHolidays+        "SH" -> schleswigHolsteinHolidays+        "TH" -> thuringiaHolidays+        _ -> []+    )++badenWurttembergHolidays :: DateFinders+badenWurttembergHolidays =+  [ jan 6, -- epiphany+    (60 `days`) . after . easterSunday, -- corpus christi+    nov 1 -- all saints day+  ]++bavariaHolidays :: DateFinders+bavariaHolidays =+  [ jan 6, -- epiphany+    (60 `days`) . after . easterSunday, -- corpus christi+    aug 15, -- assumption day+    nov 1 -- all saints day+  ]++berlinHolidays :: DateFinders+berlinHolidays =+  [ mar 8, -- international womens day+    nov 1, -- all saints day+    years (== 2025) . may 8 -- 80th anniversary of the end of world war 2+  ]++brandenburgHolidays :: DateFinders+brandenburgHolidays =+  [ easterSunday,+    (49 `days`) . after . easterSunday, -- whit sunday+    oct 31 -- reformation day+  ]++bremenHolidays :: DateFinders+bremenHolidays =+  [ oct 31 -- reformation day+  ]++hamburgHolidays :: DateFinders+hamburgHolidays =+  [ oct 31 -- reformation day+  ]++hesseHolidays :: DateFinders+hesseHolidays =+  [ (60 `days`) . after . easterSunday -- corpus christi+  ]++mecklenburgVorpommernHolidays :: DateFinders+mecklenburgVorpommernHolidays =+  [ mar 8, -- international womens day+    oct 31 -- reformation day+  ]++lowerSaxonyHolidays :: DateFinders+lowerSaxonyHolidays =+  [ oct 31 -- reformation day+  ]++northRhineWestphaliaHolidays :: DateFinders+northRhineWestphaliaHolidays =+  [ (60 `days`) . after . easterSunday, -- corpus christi+    nov 1 -- all saints day+  ]++rhinelandPalatinateHolidays :: DateFinders+rhinelandPalatinateHolidays =+  [ (60 `days`) . after . easterSunday, -- corpus christi+    nov 1 -- all saints day+  ]++saarlandHolidays :: DateFinders+saarlandHolidays =+  [ (60 `days`) . after . easterSunday, -- corpus christi+    aug 15, -- assumption day+    nov 1 -- all saints day+  ]++saxonyHolidays :: DateFinders+saxonyHolidays =+  [ (2 `wed`) . before . (4 `sun`) . before . christmasDay -- repentance and prayer day+  ]++saxonyAnhaltHolidays :: DateFinders+saxonyAnhaltHolidays =+  [ jan 6, -- epiphany+    oct 31 -- reformation day+  ]++schleswigHolsteinHolidays :: DateFinders+schleswigHolsteinHolidays =+  []++thuringiaHolidays :: DateFinders+thuringiaHolidays =+  [ sep 20 -- world childrens day+  ]
+ src/Holidays/Namibia.hs view
@@ -0,0 +1,29 @@+-- references:+-- https://en.wikipedia.org/wiki/Public_holidays_in_Namibia++module Holidays.Namibia (+  holidays,+) where++import Holidays.DateFinder+import Holidays.DateTransform++-- Namibia public holidays+holidays :: (DateFinders, DateTransforms)+holidays =+  ( [ newYearsDay,+      mar 21, -- independence day+      goodFriday,+      easterSunday,+      ascensionDay,+      workersDay,+      may 4, -- cassinga day+      may 25, -- africa day+      aug 26, -- heroes day+      dec 10, -- human rights day+      christmasDay,+      boxingDay, -- family day+      years (>= 2025) . may 28 -- genocide remembrance day+    ],+    [sundayRule]+  )
+ src/Holidays/SouthAfrica.hs view
@@ -0,0 +1,28 @@+-- references:+-- https://en.wikipedia.org/wiki/Public_holidays_in_South_Africa++module Holidays.SouthAfrica (+  holidays,+) where++import Holidays.DateFinder+import Holidays.DateTransform++holidays :: (DateFinders, DateTransforms)+holidays =+  ( [ newYearsDay,+      goodFriday,+      easterSunday, -- family day+      mar 21, -- human right day+      apr 27, -- freedom day+      workersDay,+      jun 16, -- youth day+      aug 9, -- womens day+      sep 24, -- heritage day+      dec 16, -- day of reconciliation+      christmasDay,+      boxingDay, -- day of goodwill+      years (== 2024) . may 29 -- general elections 2024+    ],+    [sundayRule]+  )
+ src/Holidays/UnitedKingdom.hs view
@@ -0,0 +1,23 @@+-- references:+-- https://en.wikipedia.org/wiki/Public_holidays_in_the_United_Kingdom++module Holidays.UnitedKingdom (+  holidays,+) where++import Holidays.DateFinder+import Holidays.DateTransform++holidays :: (DateFinders, DateTransforms)+holidays =+  ( [ newYearsDay,+      goodFriday,+      easterSunday,+      (1 `mon`) . after . may 1, -- early may bank holiday+      (1 `mon`) . before . jun 1, -- spring bank holiday+      (1 `mon`) . before . sep 1, -- summer bank holiday+      christmasDay,+      boxingDay+    ],+    [substituteRule]+  )
+ src/Holidays/UnitedStates.hs view
@@ -0,0 +1,26 @@+-- references:+-- https://en.wikipedia.org/wiki/Federal_holidays_in_the_United_States++module Holidays.UnitedStates (+  holidays,+) where++import Holidays.DateFinder+import Holidays.DateTransform++holidays :: (DateFinders, DateTransforms)+holidays =+  ( [ newYearsDay,+      (3 `mon`) . after . jan 1, -- Martin Luther King's birthday+      (3 `mon`) . after . feb 1, -- Georges Washington's birthday+      (1 `mon`) . before . jun 1, -- memorial day, last monday of may+      jun 19, -- juneteenth independence day+      jul 4, -- independence day+      (1 `mon`) . after . sep 1, -- labor day+      (2 `mon`) . after . oct 1, -- columbus day+      nov 11, -- veterans day,+      (4 `thurs`) . after . nov 1, -- thanksgiving day+      christmasDay+    ],+    []+  )
+ tst/Main.hs view
@@ -0,0 +1,20 @@+module Main where++import Test.Holidays.Germany qualified as DEU+import Test.Holidays.Namibia qualified as NAM+import Test.Holidays.SouthAfrica qualified as ZAF+import Test.Holidays.UnitedKingdom qualified as GBR+import Test.Holidays.UnitedStates qualified as USA+import Test.Tasty++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Holidays tests" [unitTests, propTests]++unitTests :: TestTree+unitTests = testGroup "Unit tests" [DEU.unitTests, GBR.unitTests, NAM.unitTests, USA.unitTests, ZAF.unitTests]++propTests :: TestTree+propTests = testGroup "Holidays property based tests" [DEU.propTests, GBR.propTests, NAM.propTests, USA.propTests, ZAF.propTests]
+ tst/Test/Holidays.hs view
@@ -0,0 +1,30 @@+module Test.Holidays (+  countryPropTests,+  day,+) where++import Data.Set qualified as S+import Data.Time+import Holidays qualified as H+import Test.Tasty+import Test.Tasty.QuickCheck as QC++day :: Year -> MonthOfYear -> DayOfMonth -> Day+day = fromGregorian++countryPropTests :: H.ISO_3166_1_Alpha_3 -> [H.Region] -> TestTree+countryPropTests countryCode regions =+  let uniqueYears c y =+        S.map+          ( \h ->+              let+                (year', _, _) = toGregorian h+              in+                year'+          )+          (H.holidays c regions y)+  in  testGroup+        ("Holidays property based tests for country " <> show countryCode)+        [ QC.testProperty "Day always in the same year" $+            \year -> S.singleton year == uniqueYears countryCode year+        ]
+ tst/Test/Holidays/Germany.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Holidays.Germany (+  unitTests,+  propTests,+) where++import Data.Set qualified as S+import Holidays+import Test.Holidays+import Test.Tasty+import Test.Tasty.HUnit++-- german states+regions :: [Region] +regions = ["BW","BY","BE","BB","HB","HH","HE","MV","NI","NW","RP","SL","SN","ST","SH","TH"]++unitTests :: TestTree+unitTests =+  testGroup+    "DEU unit tests"+    [ testCase "2025" $+        S.toAscList (holidays "DEU" regions 2025)+          @?= [ day 2025 1 1,+                day 2025 1 6,+                day 2025 3 8,+                day 2025 4 18,+                day 2025 4 20,+                day 2025 4 21,+                day 2025 5 1,+                day 2025 5 8,+                day 2025 5 29,+                day 2025 6 8,+                day 2025 6 9,+                day 2025 6 19,+                day 2025 8 15,+                day 2025 9 20,+                day 2025 10 3,+                day 2025 10 31,+                day 2025 11 1,+                day 2025 11 19,+                day 2025 12 25,+                day 2025 12 26+              ]+    ]++propTests :: TestTree+propTests = countryPropTests "DEU" regions
+ tst/Test/Holidays/Namibia.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Holidays.Namibia (+  unitTests,+  propTests,+) where++import Data.Set qualified as S+import Holidays+import Test.Holidays+import Test.Tasty+import Test.Tasty.HUnit++unitTests :: TestTree+unitTests =+  testGroup+    "NAM unit tests"+    [ testCase "2024" $+        S.toAscList (holidays "NAM" [] 2024)+          @?= [ day 2024 1 1,+                day 2024 3 21,+                day 2024 3 29,+                day 2024 4 1,+                day 2024 5 1,+                day 2024 5 4,+                day 2024 5 9,+                day 2024 5 25,+                day 2024 8 26,+                day 2024 12 10,+                day 2024 12 25,+                day 2024 12 26+              ],+      testCase "2025" $+        S.toAscList (holidays "NAM" [] 2025)+          @?= [ day 2025 1 1,+                day 2025 3 21,+                day 2025 4 18,+                day 2025 4 21,+                day 2025 5 1,+                day 2025 5 5,+                day 2025 5 26,+                day 2025 5 28,+                day 2025 5 29,+                day 2025 8 26,+                day 2025 12 10,+                day 2025 12 25,+                day 2025 12 26+              ]+    ]++propTests :: TestTree+propTests = countryPropTests "NAM" []
+ tst/Test/Holidays/SouthAfrica.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Holidays.SouthAfrica (+  unitTests,+  propTests,+) where++import Data.Set qualified as S+import Holidays+import Test.Holidays+import Test.Tasty+import Test.Tasty.HUnit++unitTests :: TestTree+unitTests =+  testGroup+    "ZAF unit tests"+    [ testCase "2024" $+        S.toAscList (holidays "ZAF" [] 2024)+          @?= [ day 2024 1 1,+                day 2024 3 21,+                day 2024 3 29,+                day 2024 4 1,+                day 2024 4 27,+                day 2024 5 1,+                day 2024 5 29,+                day 2024 6 17,+                day 2024 8 9,+                day 2024 9 24,+                day 2024 12 16,+                day 2024 12 25,+                day 2024 12 26+              ],+      testCase "2025" $+        S.toAscList (holidays "ZAF" [] 2025)+          @?= [ day 2025 1 1,+                day 2025 3 21,+                day 2025 4 18,+                day 2025 4 21,+                day 2025 4 28,+                day 2025 5 1,+                day 2025 6 16,+                day 2025 8 9,+                day 2025 9 24,+                day 2025 12 16,+                day 2025 12 25,+                day 2025 12 26+              ]+    ]++propTests :: TestTree+propTests = countryPropTests "ZAF" []
+ tst/Test/Holidays/UnitedKingdom.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Holidays.UnitedKingdom (+  unitTests,+  propTests,+) where++import Data.Set qualified as S+import Holidays+import Test.Holidays+import Test.Tasty+import Test.Tasty.HUnit++unitTests :: TestTree+unitTests =+  testGroup+    "GBR unit tests"+    [ testCase "2025" $+        S.toAscList (holidays "GBR" [] 2025)+          @?= [ day 2025 1 1,+                day 2025 4 18,+                day 2025 4 21,+                day 2025 5 5,+                day 2025 5 26,+                day 2025 8 25,+                day 2025 12 25,+                day 2025 12 26+              ],+      testCase "2026" $+        S.toAscList (holidays "GBR" [] 2026)+          @?= [ day 2026 1 1,+                day 2026 4 3,+                day 2026 4 6,+                day 2026 5 4,+                day 2026 5 25,+                day 2026 8 31,+                day 2026 12 25,+                day 2026 12 28+              ]+    ]++propTests :: TestTree+propTests = countryPropTests "GBR" []
+ tst/Test/Holidays/UnitedStates.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Holidays.UnitedStates (+  unitTests,+  propTests,+) where++import Data.Set qualified as S+import Holidays+import Test.Holidays+import Test.Tasty+import Test.Tasty.HUnit++unitTests :: TestTree+unitTests =+  testGroup+    "USA unit tests"+    [ testCase "2025" $+        S.toAscList (holidays "USA" [] 2025)+          @?= [ day 2025 1 1,+                day 2025 1 20,+                day 2025 2 17,+                day 2025 5 26,+                day 2025 6 19,+                day 2025 7 4,+                day 2025 9 1,+                day 2025 10 13,+                day 2025 11 11,+                day 2025 11 27,+                day 2025 12 25+              ]+    ]++propTests :: TestTree+propTests = countryPropTests "USA" []