From e8a519b8288c6f02dc054dd4bf6ca39ce4867cf6 Mon Sep 17 00:00:00 2001 From: Kevin Fries Date: Wed, 14 Aug 2024 15:45:24 -0600 Subject: [PATCH] initial checkin --- CODEOWNERS | 12 + changelog.md | 12 + features/Holiday_Christmas.feature | 21 ++ features/Holiday_ColumbusDay.feature | 29 +++ features/Holiday_IndependanceDay.feature | 21 ++ features/Holiday_Juneteenth.feature | 21 ++ features/Holiday_LaborDay.feature | 29 +++ features/Holiday_MLK.feature | 29 +++ features/Holiday_MemorialDay.feature | 36 +++ features/Holiday_NewYearsDay.feature | 21 ++ features/Holiday_PresidentsDay.feature | 29 +++ features/Holiday_Thanksgiving.feature | 34 +++ features/Holiday_VeteransDay.feature | 21 ++ features/Holidays_USFederalHolidays.feature | 29 +++ go.mod | 17 ++ go.sum | 48 ++++ holiday.go | 177 ++++++++++++++ holidays.go | 55 +++++ holidays_test.go | 247 ++++++++++++++++++++ 19 files changed, 888 insertions(+) create mode 100644 CODEOWNERS create mode 100644 changelog.md create mode 100644 features/Holiday_Christmas.feature create mode 100644 features/Holiday_ColumbusDay.feature create mode 100644 features/Holiday_IndependanceDay.feature create mode 100644 features/Holiday_Juneteenth.feature create mode 100644 features/Holiday_LaborDay.feature create mode 100644 features/Holiday_MLK.feature create mode 100644 features/Holiday_MemorialDay.feature create mode 100644 features/Holiday_NewYearsDay.feature create mode 100644 features/Holiday_PresidentsDay.feature create mode 100644 features/Holiday_Thanksgiving.feature create mode 100644 features/Holiday_VeteransDay.feature create mode 100644 features/Holidays_USFederalHolidays.feature create mode 100644 go.mod create mode 100644 go.sum create mode 100644 holiday.go create mode 100644 holidays.go create mode 100644 holidays_test.go diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..26fe908 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,12 @@ +############################################################## +# +# List of approvers/reviewers for GO config package +# +############################################################## +# +# Learn about CODEOWNERS file format: +# https://help.github.com/en/articles/about-code-owners +# + +# These owners will be the default owners for everything in the repo. +* @kfries63 \ No newline at end of file diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..3c725d8 --- /dev/null +++ b/changelog.md @@ -0,0 +1,12 @@ +# Changelog + +## Pre-Release + +### 0.0.1 +*10 Oct 2022* + + * Setup Repo + * Setup Cucumber Testing + * Got Cucumber tests running from Go Test + * Built 1st test to return a version string + diff --git a/features/Holiday_Christmas.feature b/features/Holiday_Christmas.feature new file mode 100644 index 0000000..06383a5 --- /dev/null +++ b/features/Holiday_Christmas.feature @@ -0,0 +1,21 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a Christmas Holiday Object + + Scenario: verify the name + Then the name will be "Christmas" + + Scenario: verify the description + Then the description will be "December 25th" + + Scenario: verify a valid date + When I check "2022-12-25" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-01-01" + Then it will return false diff --git a/features/Holiday_ColumbusDay.feature b/features/Holiday_ColumbusDay.feature new file mode 100644 index 0000000..413a0eb --- /dev/null +++ b/features/Holiday_ColumbusDay.feature @@ -0,0 +1,29 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a ColumbusDay Holiday Object + + Scenario: verify the name + Then the name will be "Columbus Day / Indigenous People Day" + + Scenario: verify the description + Then the description will be "Second Monday in October" + + Scenario: verify a valid date in 2020 + When I check "2020-10-12" + Then it returns true + + Scenario: verify a valid date in 2021 + When I check "2021-10-11" + Then it returns true + + Scenario: verify a valid date in 2022 + When I check "2022-10-10" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-01-01" + Then it will return false diff --git a/features/Holiday_IndependanceDay.feature b/features/Holiday_IndependanceDay.feature new file mode 100644 index 0000000..191af24 --- /dev/null +++ b/features/Holiday_IndependanceDay.feature @@ -0,0 +1,21 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a IndependenceDay Holiday Object + + Scenario: verify the name + Then the name will be "Independence Day" + + Scenario: verify the description + Then the description will be "July 4th" + + Scenario: verify a valid date + When I check "2022-07-04" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-12-25" + Then it will return false diff --git a/features/Holiday_Juneteenth.feature b/features/Holiday_Juneteenth.feature new file mode 100644 index 0000000..9018fd6 --- /dev/null +++ b/features/Holiday_Juneteenth.feature @@ -0,0 +1,21 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a Juneteenth Holiday Object + + Scenario: verify the name + Then the name will be "Juneteenth" + + Scenario: verify the description + Then the description will be "June 19th" + + Scenario: verify a valid date + When I check "2022-06-19" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-12-25" + Then it will return false diff --git a/features/Holiday_LaborDay.feature b/features/Holiday_LaborDay.feature new file mode 100644 index 0000000..e63c717 --- /dev/null +++ b/features/Holiday_LaborDay.feature @@ -0,0 +1,29 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a LaborDay Holiday Object + + Scenario: verify the name + Then the name will be "Labor Day" + + Scenario: verify the description + Then the description will be "First Monday in September" + + Scenario: verify a valid date in 2020 + When I check "2020-09-07" + Then it returns true + + Scenario: verify a valid date in 2021 + When I check "2021-09-06" + Then it returns true + + Scenario: verify a valid date in 2022 + When I check "2022-09-05" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-01-01" + Then it will return false diff --git a/features/Holiday_MLK.feature b/features/Holiday_MLK.feature new file mode 100644 index 0000000..c930e16 --- /dev/null +++ b/features/Holiday_MLK.feature @@ -0,0 +1,29 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a MLKHoliday Holiday Object + + Scenario: verify the name + Then the name will be "Martin Luther King Holiday" + + Scenario: verify the description + Then the description will be "Second Monday in January" + + Scenario: verify a valid date in 2020 + When I check "2020-01-20" + Then it returns true + + Scenario: verify a valid date in 2021 + When I check "2021-01-18" + Then it returns true + + Scenario: verify a valid date in 2022 + When I check "2022-01-17" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-01-01" + Then it will return false diff --git a/features/Holiday_MemorialDay.feature b/features/Holiday_MemorialDay.feature new file mode 100644 index 0000000..0a9c042 --- /dev/null +++ b/features/Holiday_MemorialDay.feature @@ -0,0 +1,36 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + // var MemorialDay = FloatingHoliday{ + // "Memorial Day" + // -1, + // time.Monday, + // time.May, + // } + + Background: + Given I have a MemorialDay Holiday Object + + Scenario: verify the name + Then the name will be "Memorial Day" + + Scenario: verify the description + Then the description will be "Last Monday in May" + + Scenario: verify a valid date in 2020 + When I check "2020-05-25" + Then it returns true + + Scenario: verify a valid date in 2021 + When I check "2021-05-31" + Then it returns true + + Scenario: verify a valid date in 2022 + When I check "2022-05-30" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-01-01" + Then it will return false diff --git a/features/Holiday_NewYearsDay.feature b/features/Holiday_NewYearsDay.feature new file mode 100644 index 0000000..7036f6a --- /dev/null +++ b/features/Holiday_NewYearsDay.feature @@ -0,0 +1,21 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a NewYears Holiday Object + + Scenario: verify the name + Then the name will be "New Years Day" + + Scenario: verify the description + Then the description will be "January 1st" + + Scenario: verify a valid date + When I check "2022-01-01" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-12-25" + Then it will return false diff --git a/features/Holiday_PresidentsDay.feature b/features/Holiday_PresidentsDay.feature new file mode 100644 index 0000000..6d047d9 --- /dev/null +++ b/features/Holiday_PresidentsDay.feature @@ -0,0 +1,29 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a PresidentsDay Holiday Object + + Scenario: verify the name + Then the name will be "Presidents Day" + + Scenario: verify the description + Then the description will be "Second Monday in February" + + Scenario: verify a valid date in 2020 + When I check "2020-02-17" + Then it returns true + + Scenario: verify a valid date in 2021 + When I check "2021-02-15" + Then it returns true + + Scenario: verify a valid date in 2022 + When I check "2022-02-21" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-01-01" + Then it will return false diff --git a/features/Holiday_Thanksgiving.feature b/features/Holiday_Thanksgiving.feature new file mode 100644 index 0000000..a2f2128 --- /dev/null +++ b/features/Holiday_Thanksgiving.feature @@ -0,0 +1,34 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + // 4, + // time.Thursday, + // time.November, + // } + + Background: + Given I have a Thanksgiving Holiday Object + + Scenario: verify the name + Then the name will be "Thanksgiving" + + Scenario: verify the description + Then the description will be "4th thursday in November" + + Scenario: verify a valid date in 2020 + When I check "2020-11-26" + Then it returns true + + Scenario: verify a valid date in 2021 + When I check "2021-11-25" + Then it returns true + + Scenario: verify a valid date in 2022 + When I check "2022-11-24" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-01-01" + Then it will return false diff --git a/features/Holiday_VeteransDay.feature b/features/Holiday_VeteransDay.feature new file mode 100644 index 0000000..470a677 --- /dev/null +++ b/features/Holiday_VeteransDay.feature @@ -0,0 +1,21 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a VeteransDay Holiday Object + + Scenario: verify the name + Then the name will be "Veterans Day" + + Scenario: verify the description + Then the description will be "11th of November" + + Scenario: verify a valid date + When I check "2022-11-11" + Then it returns true + + Scenario: verify a non-valid date + When I check "2022-12-25" + Then it will return false diff --git a/features/Holidays_USFederalHolidays.feature b/features/Holidays_USFederalHolidays.feature new file mode 100644 index 0000000..018d95c --- /dev/null +++ b/features/Holidays_USFederalHolidays.feature @@ -0,0 +1,29 @@ +Feature: Define New Years Day Holiday + In order to properly identify when a day is the New Years Day Holiday + As a developer + I want to have a way to detect, and return this condition + + Background: + Given I have a USFederalHoliday HolidayList Object + + Scenario: verify the name + Then the title will be "US Federal Holidays" + + Scenario Outline: verify a the list of dates + When I execute check with "" + Then it returns "" + + Examples: + | dateString | result | name | + | 2022-01-01 | true | New Years | + | 2022-01-01 | true | MLK | + | 2022-01-01 | true | Presidents Day | + | 2022-01-01 | true | Memorial day | + | 2022-01-01 | true | Junteenth | + | 2022-01-01 | true | Independence Day | + | 2022-01-01 | true | Labor Day | + | 2022-01-01 | true | Columbus Day | + | 2022-01-01 | false | Holloween | + | 2022-01-01 | true | Veterens Day | + | 2022-01-01 | true | Thanksgiving | + | 2022-01-01 | true | Christmas | diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f9aadba --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module holidays + +go 1.22.4 + +require ( + github.com/cucumber/godog v0.14.1 + github.com/spf13/pflag v1.0.5 +) + +require ( + github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect + github.com/cucumber/messages/go/v21 v21.0.1 // indirect + github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-memdb v1.3.4 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..fc881b8 --- /dev/null +++ b/go.sum @@ -0,0 +1,48 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= +github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= +github.com/cucumber/godog v0.14.1 h1:HGZhcOyyfaKclHjJ+r/q93iaTJZLKYW6Tv3HkmUE6+M= +github.com/cucumber/godog v0.14.1/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= +github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= +github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= +github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/holiday.go b/holiday.go new file mode 100644 index 0000000..371537b --- /dev/null +++ b/holiday.go @@ -0,0 +1,177 @@ +package holidays + +import ( + "time" +) + +type Holiday interface { + Name() string + Describe() string + Check(target time.Time) bool +} + +type FixedHoliday struct { + NameStr string + Description string + Month time.Month + Day int +} + +func (fh FixedHoliday) Name() string { + return fh.NameStr +} + +func (fh FixedHoliday) Describe() string { + return fh.Description +} + +func (fh FixedHoliday) Check(target time.Time) bool { + if target.Month() != fh.Month { + return false + } + + if target.Day() != fh.Day { + return false + } + + return true +} + +type FloatingHoliday struct { + NameStr string + Description string + Ordinal int + DayOfWeek time.Weekday + Month time.Month +} + +func (fh FloatingHoliday) Name() string { + return fh.NameStr +} + +func (fh FloatingHoliday) Describe() string { + return fh.Description +} + +func (fh FloatingHoliday) Check(target time.Time) bool { + var minDay, maxDay int + + if target.Month() != fh.Month { + return false + } + + if target.Weekday() != fh.DayOfWeek { + return false + } + + if fh.Ordinal == -1 { + maxDay = time.Date(target.Year(), fh.Month+1, 0, 0, 0, 0, 0, time.UTC).Day() + 1 + minDay = maxDay - 8 + } else { + minDay = (fh.Ordinal - 1) * 7 + maxDay = minDay + 8 + } + + return target.Day() > minDay && target.Day() < maxDay +} + +// Predefined Holidays + +var NewYearsDay = FixedHoliday{ + "New Years Day", + "January 1st", + time.Month(1), + 1, +} + +var MLKHoliday = FloatingHoliday{ + "Martin Luther King Holiday", + "Second Monday in January", + 3, + time.Monday, + time.January, +} + +var PresidentsDay = FloatingHoliday{ + "Presidents Day", + "Second Monday in February", + 3, + time.Monday, + time.February, +} + +var MemorialDay = FloatingHoliday{ + "Memorial Day", + "Last Monday in May", + -1, + time.Monday, + time.May, +} + +var Juneteenth = FixedHoliday{ + "Juneteenth", + "June 19th", + time.June, + 19, +} + +var IndependenceDay = FixedHoliday{ + "Independence Day", + "July 4th", + time.July, + 4, +} + +var LaborDay = FloatingHoliday{ + "Labor Day", + "First Monday in September", + 1, + time.Monday, + time.September, +} + +var ColumbusDay = FloatingHoliday{ + "Columbus Day / Indigenous People Day", + "Second Monday in October", + 2, + time.Monday, + time.October, +} + +var VeteransDay = FixedHoliday{ + "Veterans Day", + "11th of November", + time.November, + 11, +} + +var Thanksgiving = FloatingHoliday{ + "Thanksgiving", + "4th thursday in November", + 4, + time.Thursday, + time.November, +} + +var Christmas = FixedHoliday{ + "Christmas", + "December 25th", + time.December, + 25, +} + +// Predefined HolidayList + +// var UsFederalHolidays = HolidayList{[]Holiday{ +// NewYearsDay, +// MLKHoliday, +// PresidentsDay, +// MemorialDay, +// Juneteenth, +// IndependenceDay, +// LaborDay, +// ColumbusDay, +// VeteransDay, +// Thanksgiving, +// Christmas, +// }} diff --git a/holidays.go b/holidays.go new file mode 100644 index 0000000..8cd8e33 --- /dev/null +++ b/holidays.go @@ -0,0 +1,55 @@ +package holidays + +import ( + "time" +) + +type Holidays interface { + Name() string + Describe() string + Check(target time.Time) bool +} + +type HolidayList struct { + NameStr string + Description string + Items []Holiday +} + +func (hl HolidayList) Name() string { + return hl.NameStr +} + +func (hl HolidayList) Check(dateVal time.Time) bool { + for _, holiday := range hl.Items { + if holiday.Check(dateVal) { + return true + } + } + + return false +} + +func (hl HolidayList) Describe() string { + return hl.Description +} + +// Predefined Holidays + +var USFederalHolidays = HolidayList{ + "US Federal Holidays", + "US Federal Holidays", + []Holiday{ + NewYearsDay, + MLKHoliday, + PresidentsDay, + MemorialDay, + Juneteenth, + IndependenceDay, + LaborDay, + ColumbusDay, + VeteransDay, + Thanksgiving, + Christmas, + }, +} diff --git a/holidays_test.go b/holidays_test.go new file mode 100644 index 0000000..4271a16 --- /dev/null +++ b/holidays_test.go @@ -0,0 +1,247 @@ +package holidays + +import ( + "errors" + "fmt" + "github.com/cucumber/godog" + "github.com/cucumber/godog/colors" + "github.com/spf13/pflag" + "os" + "strconv" + "testing" + "time" +) + +var opt = godog.Options{ + Concurrency: 5, + Format: "progress", + Output: colors.Colored(os.Stdout), + Randomize: 1, +} + +func init() { + godog.BindCommandLineFlags("cadence.", &opt) +} + +func TestCucumberFeatures(t *testing.T) { + pflag.Parse() + opt.Paths = pflag.Args() + + suite := godog.TestSuite{ + Name: "cadence", + ScenarioInitializer: InitializeScenario, + Options: &opt, + } + + if suite.Run() != 0 { + t.Fatal("non-zero status returned, failed to run cucumber tests") + } +} + +func InitializeScenario(sc *godog.ScenarioContext) { + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Holiday Object Tests // + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + htd := HolidayTestData{} + + // Given Mappings + sc.Step(`^I have a NewYears Holiday Object$`, htd.iHaveANewYearsHolidayObject) + sc.Step(`^I have a MLKHoliday Holiday Object$`, htd.iHaveAMLKHolidayHolidayObject) + sc.Step(`^I have a PresidentsDay Holiday Object$`, htd.iHaveAPresidentsDayHolidayObject) + sc.Step(`^I have a MemorialDay Holiday Object$`, htd.iHaveAMemorialDayHolidayObject) + sc.Step(`^I have a Juneteenth Holiday Object$`, htd.iHaveAJuneteenthHolidayObject) + sc.Step(`^I have a IndependenceDay Holiday Object$`, htd.iHaveAIndependenceDayHolidayObject) + sc.Step(`^I have a LaborDay Holiday Object$`, htd.iHaveALaborDayHolidayObject) + sc.Step(`^I have a ColumbusDay Holiday Object$`, htd.iHaveAColumbusDayHolidayObject) + sc.Step(`^I have a VeteransDay Holiday Object$`, htd.iHaveAVeteransDayHolidayObject) + sc.Step(`^I have a Thanksgiving Holiday Object$`, htd.iHaveAThanksgivingHolidayObject) + sc.Step(`^I have a Christmas Holiday Object$`, htd.iHaveAChristmasHolidayObject) + + // When Mappings + sc.Step(`^I check "([^"]*)"$`, htd.iCheck) + + // Then Mappings + sc.Step(`^the description will be "([^"]*)"$`, htd.theDescriptionWillBe) + sc.Step(`^the name will be "([^"]*)"$`, htd.theNameWillBe) + + sc.Step(`^it returns true$`, htd.itReturnsTrue) + sc.Step(`^it will return false$`, htd.itWillReturnFalse) + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Holiday List Object Tests // + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + hltd := HolidayListTestData{} + + // Given Mappings + sc.Step(`^I have a USFederalHoliday HolidayList Object$`, hltd.iHaveAUSFederalHolidayHolidayListObject) + + // When Mappings + sc.Step(`^I execute check with "([^"]*)"$`, hltd.iExecuteCheckWith) + + // Then Mappings + sc.Step(`^it returns "([^"]*)"$`, hltd.itReturns) + sc.Step(`^the title will be "([^"]*)"$`, hltd.theTitleWillBe) +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Holiday Object Tests // +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +type HolidayTestData struct { + holiday Holiday + CheckResult bool + StringResult string +} + +// Given Callbacks +func (td *HolidayTestData) iHaveANewYearsHolidayObject() (err error) { + td.holiday = NewYearsDay + + return +} + +func (td *HolidayTestData) iHaveAMLKHolidayHolidayObject() (err error) { + td.holiday = MLKHoliday + + return +} + +func (td *HolidayTestData) iHaveAPresidentsDayHolidayObject() (err error) { + td.holiday = PresidentsDay + + return +} + +func (td *HolidayTestData) iHaveAMemorialDayHolidayObject() (err error) { + td.holiday = MemorialDay + + return +} + +func (td *HolidayTestData) iHaveAJuneteenthHolidayObject() (err error) { + td.holiday = Juneteenth + + return +} + +func (td *HolidayTestData) iHaveAIndependenceDayHolidayObject() (err error) { + td.holiday = IndependenceDay + + return +} + +func (td *HolidayTestData) iHaveALaborDayHolidayObject() (err error) { + td.holiday = LaborDay + + return +} + +func (td *HolidayTestData) iHaveAColumbusDayHolidayObject() (err error) { + td.holiday = ColumbusDay + + return +} + +func (td *HolidayTestData) iHaveAVeteransDayHolidayObject() (err error) { + td.holiday = VeteransDay + + return +} + +func (td *HolidayTestData) iHaveAThanksgivingHolidayObject() (err error) { + td.holiday = Thanksgiving + + return +} + +func (td *HolidayTestData) iHaveAChristmasHolidayObject() (err error) { + td.holiday = Christmas + + return +} + +// When Callbacks +func (td *HolidayTestData) iCheck(dateString string) (err error) { + dateValue, err := time.Parse("2006-01-02", dateString) + td.CheckResult = td.holiday.Check(dateValue) + + return +} + +// Then Callbacks +func (td *HolidayTestData) itReturnsTrue() (err error) { + if !td.CheckResult { + err = errors.New("ERROR: HOLIDAY: Did not detect holiday correctly") + } + + return +} + +func (td *HolidayTestData) itWillReturnFalse() (err error) { + if td.CheckResult { + err = errors.New("ERROR: HOLIDAY: False positive, failed to error on incorrect date") + } + + return +} + +func (td *HolidayTestData) theDescriptionWillBe(expected string) (err error) { + if td.holiday.Describe() != expected { + err = errors.New(fmt.Sprintf("ERROR: HOLIDAY: Name was not as expected, received %s, expected: %s", td.holiday.Describe(), expected)) + } + + return +} + +func (td *HolidayTestData) theNameWillBe(expected string) (err error) { + if td.holiday.Name() != expected { + err = errors.New(fmt.Sprintf("ERROR: HOLIDAY: Description was not as expected, received %s, expected: %s", td.holiday.Name(), expected)) + } + + return +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Holiday List Object Tests // +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +type HolidayListTestData struct { + holidays Holidays + checkRes bool +} + +// Given Callbacks +func (hltd HolidayListTestData) iHaveAUSFederalHolidayHolidayListObject() (err error) { + hltd.holidays = USFederalHolidays + + return +} + +// When Callbacks +func (hltd HolidayListTestData) iExecuteCheckWith(dateString string) (err error) { + dateValue, err := time.Parse("2006-01-02", dateString) + hltd.checkRes = hltd.holidays.Check(dateValue) + + return +} + +// Then Callbacks +func (hltd HolidayListTestData) itReturns(boolStr string) (err error) { + boolVal, err := strconv.ParseBool(boolStr) + if hltd.checkRes != boolVal { + err = errors.New(fmt.Sprintf("ERROR: HOLIDAYLIST: Incorrect return from check")) + } + + return +} + +func (hltd HolidayListTestData) theTitleWillBe(expected string) (err error) { + if hltd.holidays.Name() != expected { + err = errors.New(fmt.Sprintf("ERROR: HOLIDAYLIST: Incorrect return from Name()")) + } + + return +}