commercial 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +99 -0
- data/Rakefile +8 -0
- data/lib/commercial/us.rb +188 -0
- data/lib/commercial/version.rb +5 -0
- data/lib/commercial.rb +8 -0
- data/sig/commercial.rbs +31 -0
- metadata +50 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1625934e30d3bbff95fac940c7a12766c5c43a665ef88280f5d0eab1998cadea
|
|
4
|
+
data.tar.gz: 98e5ee5230a3b0ad15feb98ab38b4c3d4c2a4dffd9c273acdc07482610032340
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4975c5b58483ada85a2a7e07b0ce3462a8360b34ff39022c9c2c96071da89acbed29e1be028d33976492cd5a179615a3ce3b8c870b631f7677f4e0feae3e1424
|
|
7
|
+
data.tar.gz: 93105d5fee8884185aa7f2438ff43c956a4f46613fda199409f2864572228feeb6ce80733983bab6946f84910690de5d9ad802b339d17ab62097f9e1dcc37fd0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 William T. Nelson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Commercial
|
|
2
|
+
|
|
3
|
+
Ruby gem for checking business days based on the United States [federal holiday](https://www.opm.gov/policy-data-oversight/pay-leave/federal-holidays/) schedule.
|
|
4
|
+
|
|
5
|
+
Dates from 1971 onwards are supported.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
Check if a date is a commercial day (not a weekend or federal holiday):
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
require "commercial"
|
|
13
|
+
|
|
14
|
+
date = Date.new(2025, 7, 4) # Independence Day
|
|
15
|
+
Commercial::US.commercial?(date) # => false
|
|
16
|
+
|
|
17
|
+
date = Date.new(2025, 7, 5) # Saturday
|
|
18
|
+
Commercial::US.commercial?(date) # => false
|
|
19
|
+
|
|
20
|
+
date = Date.new(2025, 7, 7) # Monday
|
|
21
|
+
Commercial::US.commercial?(date) # => true
|
|
22
|
+
|
|
23
|
+
# Defaults to `Date.today`
|
|
24
|
+
Commercial::US.commercial? # => true/false
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Including the module
|
|
28
|
+
|
|
29
|
+
Include the module to call methods directly:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
include Commercial::US
|
|
33
|
+
commercial?(Date.today) # => true/false
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or in a more functional style:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
class HolidayChecker
|
|
40
|
+
extend Commercial::US
|
|
41
|
+
end
|
|
42
|
+
HolidayChecker.commercial?(Date.today) # => true/false
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Checking holidays
|
|
46
|
+
|
|
47
|
+
You can also check for specific holidays or use the general `.federal_holiday?` method:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
# Check if a date is a weekend
|
|
51
|
+
Commercial::US.weekend?(Date.new(2025, 7, 5)) # => true (Saturday)
|
|
52
|
+
|
|
53
|
+
# Check if a date is any federal holiday
|
|
54
|
+
Commercial::US.federal_holiday?(Date.new(2025, 7, 4)) # => true
|
|
55
|
+
|
|
56
|
+
# Check for specific holidays
|
|
57
|
+
Commercial::US.independence_day?(Date.new(2025, 7, 4)) # => true
|
|
58
|
+
Commercial::US.christmas_day?(Date.new(2025, 12, 25)) # => true
|
|
59
|
+
Commercial::US.thanksgiving_day?(Date.new(2025, 11, 27)) # => true
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Public methods
|
|
63
|
+
|
|
64
|
+
All methods accept an optional `Date` parameter, which defaults to `Date.today` if not specified:
|
|
65
|
+
|
|
66
|
+
- `.commercial?(date)` returns true if date is a business day (not weekend or holiday)
|
|
67
|
+
- `.weekend?(date)` returns true if date is Saturday or Sunday
|
|
68
|
+
- `.federal_holiday?(date)` returns true if date is any federal holiday
|
|
69
|
+
- `.new_years_day?(date)` January 1st (or observed)
|
|
70
|
+
- `.martin_luther_king_day?(date)` third Monday of January (observed since 1986)
|
|
71
|
+
- `.washingtons_birthday?(date)` third Monday of February
|
|
72
|
+
- `.memorial_day?(date)` last Monday of May
|
|
73
|
+
- `.juneteenth?(date)` June 19 (or observed, federal holiday since 2021)
|
|
74
|
+
- `.independence_day?(date)` July 4 (or observed)
|
|
75
|
+
- `.labor_day?(date)` first Monday of September
|
|
76
|
+
- `.columbus_day?(date)` second Monday of October
|
|
77
|
+
- `.thanksgiving_day?(date)` fourth Thursday of November
|
|
78
|
+
- `.christmas_day?(date)` December 25 (or observed)
|
|
79
|
+
|
|
80
|
+
## Type signatures
|
|
81
|
+
|
|
82
|
+
This gem includes [RBS](https://github.com/ruby/rbs) type signatures in the `sig/` directory. These provide static type information for type checkers like [Steep](https://github.com/soutaro/steep) and enable better IDE support.
|
|
83
|
+
|
|
84
|
+
To validate the signatures:
|
|
85
|
+
```bash
|
|
86
|
+
rbs -I sig -r date validate
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
92
|
+
|
|
93
|
+
## Contributing
|
|
94
|
+
|
|
95
|
+
Bug reports and pull requests are welcome on GitHub at [github.com/wtn/commercial](https://github.com/wtn/commercial).
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
|
|
5
|
+
module Commercial
|
|
6
|
+
module US
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Check if a given date is a commercial banking day (not a weekend or federal holiday)
|
|
10
|
+
# @param date [Date] the date to check
|
|
11
|
+
# @return [Boolean] true if the date is a commercial banking day
|
|
12
|
+
def commercial?(date = Date.today)
|
|
13
|
+
!weekend?(date) && !federal_holiday?(date)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Check if date is a US federal holiday
|
|
17
|
+
# @param date [Date] the date to check
|
|
18
|
+
# @return [Boolean] true if the date is a federal holiday
|
|
19
|
+
def federal_holiday?(date)
|
|
20
|
+
date.year < 1971 and raise ArgumentError, "year #{date.year} not supported"
|
|
21
|
+
new_years_day?(date) \
|
|
22
|
+
|| martin_luther_king_day?(date) \
|
|
23
|
+
|| washingtons_birthday?(date) \
|
|
24
|
+
|| memorial_day?(date) \
|
|
25
|
+
|| juneteenth?(date) \
|
|
26
|
+
|| independence_day?(date) \
|
|
27
|
+
|| labor_day?(date) \
|
|
28
|
+
|| columbus_day?(date) \
|
|
29
|
+
|| thanksgiving_day?(date) \
|
|
30
|
+
|| christmas_day?(date)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# New Year's Day (January 1st, or first Monday of January if on weekend)
|
|
34
|
+
# @param date [Date] the date to check
|
|
35
|
+
# @return [Boolean] true if the date is New Year's Day or its observance
|
|
36
|
+
def new_years_day?(date)
|
|
37
|
+
return true if date.month == 1 && date.day == 1 && !weekend?(date)
|
|
38
|
+
|
|
39
|
+
# If Jan 1 is on Saturday, observed on previous Friday (Dec 31)
|
|
40
|
+
# If Jan 1 is on Sunday, observed on next Monday (Jan 2)
|
|
41
|
+
if date.month == 1 && date.day == 2 && date.monday?
|
|
42
|
+
jan_1 = Date.new(date.year, 1, 1)
|
|
43
|
+
return jan_1.sunday?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Check if this is Dec 31 and Jan 1 is Saturday
|
|
47
|
+
if date.month == 12 && date.day == 31 && date.friday?
|
|
48
|
+
jan_1_next_year = Date.new(date.year + 1, 1, 1)
|
|
49
|
+
return jan_1_next_year.saturday?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Martin Luther King Day (third Monday of January)
|
|
56
|
+
# @param date [Date] the date to check
|
|
57
|
+
# @return [Boolean] true if the date is Martin Luther King Day
|
|
58
|
+
def martin_luther_king_day?(date)
|
|
59
|
+
date.year >= 1986 && date.month == 1 && date.monday? && nth_weekday_of_month?(date, 3)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Washington's Birthday (third Monday of February)
|
|
63
|
+
# @param date [Date] the date to check
|
|
64
|
+
# @return [Boolean] true if the date is Washington's Birthday
|
|
65
|
+
def washingtons_birthday?(date)
|
|
66
|
+
date.month == 2 && date.monday? && nth_weekday_of_month?(date, 3)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Memorial Day (last Monday of May)
|
|
70
|
+
# @param date [Date] the date to check
|
|
71
|
+
# @return [Boolean] true if the date is Memorial Day
|
|
72
|
+
def memorial_day?(date)
|
|
73
|
+
date.month == 5 && date.monday? && last_weekday_of_month?(date)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Juneteenth (June 19, or nearest Monday or Friday if on a weekend)
|
|
77
|
+
# @param date [Date] the date to check
|
|
78
|
+
# @return [Boolean] true if the date is Juneteenth or its observance
|
|
79
|
+
def juneteenth?(date)
|
|
80
|
+
return false if date.year < 2021
|
|
81
|
+
return true if date.month == 6 && date.day == 19 && !weekend?(date)
|
|
82
|
+
|
|
83
|
+
# If June 19 is on Saturday, observed on Friday (June 18)
|
|
84
|
+
if date.month == 6 && date.day == 18 && date.friday?
|
|
85
|
+
june_19 = Date.new(date.year, 6, 19)
|
|
86
|
+
return june_19.saturday?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# If June 19 is on Sunday, observed on Monday (June 20)
|
|
90
|
+
if date.month == 6 && date.day == 20 && date.monday?
|
|
91
|
+
june_19 = Date.new(date.year, 6, 19)
|
|
92
|
+
return june_19.sunday?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Independence Day (July 4, or July 3rd if on Saturday, July 5th if on Sunday)
|
|
99
|
+
# @param date [Date] the date to check
|
|
100
|
+
# @return [Boolean] true if the date is Independence Day or its observance
|
|
101
|
+
def independence_day?(date)
|
|
102
|
+
return true if date.month == 7 && date.day == 4 && !weekend?(date)
|
|
103
|
+
|
|
104
|
+
# If July 4 is on Saturday, observed on Friday (July 3)
|
|
105
|
+
if date.month == 7 && date.day == 3 && date.friday?
|
|
106
|
+
july_4 = Date.new(date.year, 7, 4)
|
|
107
|
+
return july_4.saturday?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# If July 4 is on Sunday, observed on Monday (July 5)
|
|
111
|
+
if date.month == 7 && date.day == 5 && date.monday?
|
|
112
|
+
july_4 = Date.new(date.year, 7, 4)
|
|
113
|
+
return july_4.sunday?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
false
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Labor Day (First Monday of September)
|
|
120
|
+
# @param date [Date] the date to check
|
|
121
|
+
# @return [Boolean] true if the date is Labor Day
|
|
122
|
+
def labor_day?(date)
|
|
123
|
+
date.month == 9 && date.monday? && nth_weekday_of_month?(date, 1)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Columbus Day (second Monday of October)
|
|
127
|
+
# @param date [Date] the date to check
|
|
128
|
+
# @return [Boolean] true if the date is Columbus Day
|
|
129
|
+
def columbus_day?(date)
|
|
130
|
+
date.month == 10 && date.monday? && nth_weekday_of_month?(date, 2)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Thanksgiving Day (Fourth Thursday of November)
|
|
134
|
+
# @param date [Date] the date to check
|
|
135
|
+
# @return [Boolean] true if the date is Thanksgiving Day
|
|
136
|
+
def thanksgiving_day?(date)
|
|
137
|
+
date.month == 11 && date.thursday? && nth_weekday_of_month?(date, 4)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Christmas Day (December 25th, or December 24th if on Saturday, December 26th if on Sunday)
|
|
141
|
+
# @param date [Date] the date to check
|
|
142
|
+
# @return [Boolean] true if the date is Christmas Day or its observance
|
|
143
|
+
def christmas_day?(date)
|
|
144
|
+
return true if date.month == 12 && date.day == 25 && !weekend?(date)
|
|
145
|
+
|
|
146
|
+
# If Dec 25 is on Saturday, observed on Friday (Dec 24)
|
|
147
|
+
if date.month == 12 && date.day == 24 && date.friday?
|
|
148
|
+
dec_25 = Date.new(date.year, 12, 25)
|
|
149
|
+
return dec_25.saturday?
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# If Dec 25 is on Sunday, observed on Monday (Dec 26)
|
|
153
|
+
if date.month == 12 && date.day == 26 && date.monday?
|
|
154
|
+
dec_25 = Date.new(date.year, 12, 25)
|
|
155
|
+
return dec_25.sunday?
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
false
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Check if date is a weekend (Saturday or Sunday)
|
|
162
|
+
# @param date [Date] the date to check
|
|
163
|
+
# @return [Boolean] true if the date is a weekend
|
|
164
|
+
def weekend?(date)
|
|
165
|
+
date.saturday? || date.sunday?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
class << self
|
|
169
|
+
private
|
|
170
|
+
|
|
171
|
+
# Check if date is the nth occurrence of its weekday in the month
|
|
172
|
+
# e.g., is this the 3rd Monday of the month?
|
|
173
|
+
def nth_weekday_of_month?(date, n)
|
|
174
|
+
# Calculate which occurrence this is
|
|
175
|
+
# Day 1-7 is 1st, 8-14 is 2nd, 15-21 is 3rd, 22-28 is 4th
|
|
176
|
+
occurrence = ((date.day - 1) / 7) + 1
|
|
177
|
+
occurrence == n
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Check if date is the last occurrence of its weekday in the month
|
|
181
|
+
def last_weekday_of_month?(date)
|
|
182
|
+
# Check if adding 7 days would put us in the next month
|
|
183
|
+
next_week = date + 7
|
|
184
|
+
next_week.month != date.month
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
data/lib/commercial.rb
ADDED
data/sig/commercial.rbs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Commercial
|
|
2
|
+
VERSION: String
|
|
3
|
+
|
|
4
|
+
module US
|
|
5
|
+
def self.commercial?: (?Date date) -> bool
|
|
6
|
+
|
|
7
|
+
def self.weekend?: (?Date date) -> bool
|
|
8
|
+
|
|
9
|
+
def self.federal_holiday?: (?Date date) -> bool
|
|
10
|
+
|
|
11
|
+
def self.new_years_day?: (Date date) -> bool
|
|
12
|
+
|
|
13
|
+
def self.martin_luther_king_day?: (Date date) -> bool
|
|
14
|
+
|
|
15
|
+
def self.washingtons_birthday?: (Date date) -> bool
|
|
16
|
+
|
|
17
|
+
def self.memorial_day?: (Date date) -> bool
|
|
18
|
+
|
|
19
|
+
def self.juneteenth?: (Date date) -> bool
|
|
20
|
+
|
|
21
|
+
def self.independence_day?: (Date date) -> bool
|
|
22
|
+
|
|
23
|
+
def self.labor_day?: (Date date) -> bool
|
|
24
|
+
|
|
25
|
+
def self.columbus_day?: (Date date) -> bool
|
|
26
|
+
|
|
27
|
+
def self.thanksgiving_day?: (Date date) -> bool
|
|
28
|
+
|
|
29
|
+
def self.christmas_day?: (Date date) -> bool
|
|
30
|
+
end
|
|
31
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: commercial
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- William T. Nelson
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
email:
|
|
13
|
+
- 35801+wtn@users.noreply.github.com
|
|
14
|
+
executables: []
|
|
15
|
+
extensions: []
|
|
16
|
+
extra_rdoc_files: []
|
|
17
|
+
files:
|
|
18
|
+
- LICENSE.txt
|
|
19
|
+
- README.md
|
|
20
|
+
- Rakefile
|
|
21
|
+
- lib/commercial.rb
|
|
22
|
+
- lib/commercial/us.rb
|
|
23
|
+
- lib/commercial/version.rb
|
|
24
|
+
- sig/commercial.rbs
|
|
25
|
+
homepage: https://github.com/wtn/commercial
|
|
26
|
+
licenses:
|
|
27
|
+
- MIT
|
|
28
|
+
metadata:
|
|
29
|
+
allowed_push_host: https://rubygems.org
|
|
30
|
+
homepage_uri: https://github.com/wtn/commercial
|
|
31
|
+
source_code_uri: https://github.com/wtn/commercial
|
|
32
|
+
rubygems_mfa_required: 'true'
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 3.0.0
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubygems_version: 3.7.2
|
|
48
|
+
specification_version: 4
|
|
49
|
+
summary: US commercial dates
|
|
50
|
+
test_files: []
|