holidays 0.9.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG +7 -0
- data/{README → README.rdoc} +36 -39
- data/data/SYNTAX +1 -1
- data/data/au.yaml +110 -105
- data/data/build_defs.rb +4 -3
- data/data/ca.yaml +140 -140
- data/data/cz.yaml +68 -0
- data/data/de.yaml +12 -14
- data/data/dk.yaml +1 -1
- data/data/es.yaml +1 -1
- data/data/fr.yaml +1 -1
- data/data/gb.yaml +1 -1
- data/data/ie.yaml +1 -1
- data/data/index.yaml +7 -2
- data/data/is.yaml +1 -1
- data/data/it.yaml +1 -1
- data/data/mx.yaml +1 -1
- data/data/nl.yaml +1 -1
- data/data/no.yaml +82 -0
- data/data/nyse.yaml +1 -1
- data/data/nz.yaml +141 -0
- data/data/pt.yaml +1 -1
- data/data/se.yaml +1 -1
- data/data/united_nations.yaml +188 -188
- data/data/ups.yaml +55 -55
- data/data/us.yaml +80 -80
- data/data/za.yaml +1 -1
- data/holidays.gemspec +155 -0
- data/lib/holidays.rb +424 -403
- data/lib/holidays/MANIFEST +28 -25
- data/lib/holidays/au.rb +43 -41
- data/lib/holidays/ca.rb +69 -68
- data/lib/holidays/cz.rb +36 -0
- data/lib/holidays/de.rb +51 -52
- data/lib/holidays/dk.rb +48 -47
- data/lib/holidays/es.rb +53 -52
- data/lib/holidays/europe.rb +236 -215
- data/lib/holidays/fr.rb +37 -36
- data/lib/holidays/gb.rb +41 -40
- data/lib/holidays/ie.rb +33 -32
- data/lib/holidays/is.rb +62 -61
- data/lib/holidays/it.rb +36 -35
- data/lib/holidays/mx.rb +52 -51
- data/lib/holidays/nl.rb +37 -36
- data/lib/holidays/no.rb +40 -0
- data/lib/holidays/north_america.rb +108 -107
- data/lib/holidays/nyse.rb +33 -32
- data/lib/holidays/nz.rb +69 -0
- data/lib/holidays/pt.rb +38 -52
- data/lib/holidays/scandinavia.rb +124 -114
- data/lib/holidays/se.rb +53 -52
- data/lib/holidays/united_nations.rb +25 -24
- data/lib/holidays/ups.rb +32 -31
- data/lib/holidays/us.rb +49 -48
- data/lib/holidays/za.rb +36 -35
- data/rakefile.rb +105 -113
- data/test/defs/test_defs_au.rb +36 -36
- data/test/defs/test_defs_ca.rb +29 -29
- data/test/defs/test_defs_cz.rb +26 -0
- data/test/defs/test_defs_de.rb +51 -46
- data/test/defs/test_defs_dk.rb +30 -30
- data/test/defs/test_defs_es.rb +57 -57
- data/test/defs/test_defs_europe.rb +280 -240
- data/test/defs/test_defs_fr.rb +26 -26
- data/test/defs/test_defs_gb.rb +36 -36
- data/test/defs/test_defs_ie.rb +21 -21
- data/test/defs/test_defs_is.rb +33 -33
- data/test/defs/test_defs_it.rb +25 -25
- data/test/defs/test_defs_mx.rb +22 -22
- data/test/defs/test_defs_nl.rb +24 -24
- data/test/defs/test_defs_no.rb +29 -0
- data/test/defs/test_defs_north_america.rb +54 -54
- data/test/defs/test_defs_nyse.rb +22 -22
- data/test/defs/test_defs_nz.rb +22 -0
- data/test/defs/test_defs_pt.rb +32 -32
- data/test/defs/test_defs_scandinavia.rb +94 -75
- data/test/defs/test_defs_se.rb +32 -32
- data/test/defs/test_defs_ups.rb +21 -21
- data/test/defs/test_defs_us.rb +23 -23
- data/test/defs/test_defs_za.rb +25 -25
- data/test/test_date.rb +123 -0
- data/test/test_helper.rb +22 -0
- data/test/test_holidays.rb +128 -0
- data/test/test_multiple_regions.rb +24 -0
- metadata +126 -73
data/data/us.yaml
CHANGED
@@ -1,81 +1,81 @@
|
|
1
|
-
# United States holiday definitions for the Ruby Holiday gem.
|
2
|
-
#
|
3
|
-
# Updated: 2008-11-24.
|
4
|
-
# Source: http://en.wikipedia.org/wiki/Public_holidays_of_the_United_States
|
5
|
-
---
|
6
|
-
months:
|
7
|
-
0:
|
8
|
-
- name: Good Friday
|
9
|
-
regions: [us]
|
10
|
-
function: easter(year)-2
|
11
|
-
type: informal
|
12
|
-
1:
|
13
|
-
- name: New Year's Day
|
14
|
-
regions: [us]
|
15
|
-
mday: 1
|
16
|
-
observed: to_weekday_if_weekend
|
17
|
-
- name: Martin Luther King, Jr. Day
|
18
|
-
week: 3
|
19
|
-
regions: [us]
|
20
|
-
wday: 1
|
21
|
-
- name: Inauguration Day
|
22
|
-
function: us_inauguration_day(year)
|
23
|
-
regions: [us_dc]
|
24
|
-
2:
|
25
|
-
- name: Presidents' Day
|
26
|
-
week: 3
|
27
|
-
regions: [us]
|
28
|
-
wday: 1
|
29
|
-
5:
|
30
|
-
- name: Memorial Day
|
31
|
-
week: -1
|
32
|
-
regions: [us]
|
33
|
-
wday: 1
|
34
|
-
7:
|
35
|
-
- name: Independence Day
|
36
|
-
regions: [us]
|
37
|
-
mday: 4
|
38
|
-
observed: to_weekday_if_weekend
|
39
|
-
9:
|
40
|
-
- name: Labor Day
|
41
|
-
week: 1
|
42
|
-
regions: [us]
|
43
|
-
wday: 1
|
44
|
-
10:
|
45
|
-
- name: Columbus Day
|
46
|
-
week: 2
|
47
|
-
regions: [us]
|
48
|
-
wday: 1
|
49
|
-
11:
|
50
|
-
- name: Veterans Day
|
51
|
-
regions: [us]
|
52
|
-
mday: 11
|
53
|
-
observed: to_weekday_if_weekend
|
54
|
-
- name: Thanksgiving
|
55
|
-
week: 4
|
56
|
-
regions: [us]
|
57
|
-
wday: 4
|
58
|
-
12:
|
59
|
-
- name: Christmas Day
|
60
|
-
regions: [us]
|
61
|
-
mday: 25
|
62
|
-
observed: to_weekday_if_weekend
|
63
|
-
methods:
|
64
|
-
us_inauguration_day: |
|
65
|
-
# January 20, every fourth year, following Presidential election
|
66
|
-
def self.us_inauguration_day(year)
|
67
|
-
year % 4 == 1 ? 20 : nil
|
68
|
-
end
|
69
|
-
tests: |
|
70
|
-
{Date.civil(2008,1,1) => 'New Year\'s Day',
|
71
|
-
Date.civil(2008,1,21) => 'Martin Luther King, Jr. Day',
|
72
|
-
Date.civil(2008,2,18) => 'Presidents\' Day',
|
73
|
-
Date.civil(2008,5,26) => 'Memorial Day',
|
74
|
-
Date.civil(2008,7,4) => 'Independence Day',
|
75
|
-
Date.civil(2008,9,1) => 'Labor Day',
|
76
|
-
Date.civil(2008,10,13) => 'Columbus Day',
|
77
|
-
Date.civil(2008,11,11) => 'Veterans Day',
|
78
|
-
Date.civil(2008,11,27) => 'Thanksgiving',
|
79
|
-
Date.civil(2008,12,25) => 'Christmas Day'}.each do |date, name|
|
80
|
-
assert_equal name, Holidays.on(date, :us)[0][:name]
|
1
|
+
# United States holiday definitions for the Ruby Holiday gem.
|
2
|
+
#
|
3
|
+
# Updated: 2008-11-24.
|
4
|
+
# Source: http://en.wikipedia.org/wiki/Public_holidays_of_the_United_States
|
5
|
+
---
|
6
|
+
months:
|
7
|
+
0:
|
8
|
+
- name: Good Friday
|
9
|
+
regions: [us]
|
10
|
+
function: easter(year)-2
|
11
|
+
type: informal
|
12
|
+
1:
|
13
|
+
- name: New Year's Day
|
14
|
+
regions: [us]
|
15
|
+
mday: 1
|
16
|
+
observed: to_weekday_if_weekend
|
17
|
+
- name: Martin Luther King, Jr. Day
|
18
|
+
week: 3
|
19
|
+
regions: [us]
|
20
|
+
wday: 1
|
21
|
+
- name: Inauguration Day
|
22
|
+
function: us_inauguration_day(year)
|
23
|
+
regions: [us_dc]
|
24
|
+
2:
|
25
|
+
- name: Presidents' Day
|
26
|
+
week: 3
|
27
|
+
regions: [us]
|
28
|
+
wday: 1
|
29
|
+
5:
|
30
|
+
- name: Memorial Day
|
31
|
+
week: -1
|
32
|
+
regions: [us]
|
33
|
+
wday: 1
|
34
|
+
7:
|
35
|
+
- name: Independence Day
|
36
|
+
regions: [us]
|
37
|
+
mday: 4
|
38
|
+
observed: to_weekday_if_weekend
|
39
|
+
9:
|
40
|
+
- name: Labor Day
|
41
|
+
week: 1
|
42
|
+
regions: [us]
|
43
|
+
wday: 1
|
44
|
+
10:
|
45
|
+
- name: Columbus Day
|
46
|
+
week: 2
|
47
|
+
regions: [us]
|
48
|
+
wday: 1
|
49
|
+
11:
|
50
|
+
- name: Veterans Day
|
51
|
+
regions: [us]
|
52
|
+
mday: 11
|
53
|
+
observed: to_weekday_if_weekend
|
54
|
+
- name: Thanksgiving
|
55
|
+
week: 4
|
56
|
+
regions: [us]
|
57
|
+
wday: 4
|
58
|
+
12:
|
59
|
+
- name: Christmas Day
|
60
|
+
regions: [us]
|
61
|
+
mday: 25
|
62
|
+
observed: to_weekday_if_weekend
|
63
|
+
methods:
|
64
|
+
us_inauguration_day: |
|
65
|
+
# January 20, every fourth year, following Presidential election
|
66
|
+
def self.us_inauguration_day(year)
|
67
|
+
year % 4 == 1 ? 20 : nil
|
68
|
+
end
|
69
|
+
tests: |
|
70
|
+
{Date.civil(2008,1,1) => 'New Year\'s Day',
|
71
|
+
Date.civil(2008,1,21) => 'Martin Luther King, Jr. Day',
|
72
|
+
Date.civil(2008,2,18) => 'Presidents\' Day',
|
73
|
+
Date.civil(2008,5,26) => 'Memorial Day',
|
74
|
+
Date.civil(2008,7,4) => 'Independence Day',
|
75
|
+
Date.civil(2008,9,1) => 'Labor Day',
|
76
|
+
Date.civil(2008,10,13) => 'Columbus Day',
|
77
|
+
Date.civil(2008,11,11) => 'Veterans Day',
|
78
|
+
Date.civil(2008,11,27) => 'Thanksgiving',
|
79
|
+
Date.civil(2008,12,25) => 'Christmas Day'}.each do |date, name|
|
80
|
+
assert_equal name, (Holidays.on(date, :us)[0] || {})[:name]
|
81
81
|
end
|
data/data/za.yaml
CHANGED
@@ -74,5 +74,5 @@ tests: |
|
|
74
74
|
Date.civil(2007,12,16) => 'Day of Reconciliation',
|
75
75
|
Date.civil(2007,12,25) => 'Christmas Day',
|
76
76
|
Date.civil(2007,12,26) => 'Day of Goodwill'}.each do |date, name|
|
77
|
-
assert_equal name, Holidays.on(date, :za, :informal)[0][:name]
|
77
|
+
assert_equal name, (Holidays.on(date, :za, :informal)[0] || {})[:name]
|
78
78
|
end
|
data/holidays.gemspec
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile.rb, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{holidays}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Alex Dunae", "Rowan Crawford"]
|
12
|
+
s.date = %q{2010-11-12}
|
13
|
+
s.description = %q{A collection of Ruby methods to deal with statutory and other holidays. You deserve a holiday!}
|
14
|
+
s.email = %q{code@dunae.ca}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"CHANGELOG",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"REFERENCES",
|
25
|
+
"data/SYNTAX",
|
26
|
+
"data/au.yaml",
|
27
|
+
"data/build_defs.rb",
|
28
|
+
"data/ca.yaml",
|
29
|
+
"data/cz.yaml",
|
30
|
+
"data/de.yaml",
|
31
|
+
"data/dk.yaml",
|
32
|
+
"data/es.yaml",
|
33
|
+
"data/fr.yaml",
|
34
|
+
"data/gb.yaml",
|
35
|
+
"data/ie.yaml",
|
36
|
+
"data/index.yaml",
|
37
|
+
"data/is.yaml",
|
38
|
+
"data/it.yaml",
|
39
|
+
"data/mx.yaml",
|
40
|
+
"data/nl.yaml",
|
41
|
+
"data/no.yaml",
|
42
|
+
"data/north_america_informal.yaml",
|
43
|
+
"data/nyse.yaml",
|
44
|
+
"data/nz.yaml",
|
45
|
+
"data/pt.yaml",
|
46
|
+
"data/se.yaml",
|
47
|
+
"data/united_nations.yaml",
|
48
|
+
"data/ups.yaml",
|
49
|
+
"data/us.yaml",
|
50
|
+
"data/za.yaml",
|
51
|
+
"holidays.gemspec",
|
52
|
+
"lib/holidays.rb",
|
53
|
+
"lib/holidays/MANIFEST",
|
54
|
+
"lib/holidays/au.rb",
|
55
|
+
"lib/holidays/ca.rb",
|
56
|
+
"lib/holidays/cz.rb",
|
57
|
+
"lib/holidays/de.rb",
|
58
|
+
"lib/holidays/dk.rb",
|
59
|
+
"lib/holidays/es.rb",
|
60
|
+
"lib/holidays/europe.rb",
|
61
|
+
"lib/holidays/fr.rb",
|
62
|
+
"lib/holidays/gb.rb",
|
63
|
+
"lib/holidays/ie.rb",
|
64
|
+
"lib/holidays/is.rb",
|
65
|
+
"lib/holidays/it.rb",
|
66
|
+
"lib/holidays/mx.rb",
|
67
|
+
"lib/holidays/nl.rb",
|
68
|
+
"lib/holidays/no.rb",
|
69
|
+
"lib/holidays/north_america.rb",
|
70
|
+
"lib/holidays/nyse.rb",
|
71
|
+
"lib/holidays/nz.rb",
|
72
|
+
"lib/holidays/pt.rb",
|
73
|
+
"lib/holidays/scandinavia.rb",
|
74
|
+
"lib/holidays/se.rb",
|
75
|
+
"lib/holidays/united_nations.rb",
|
76
|
+
"lib/holidays/ups.rb",
|
77
|
+
"lib/holidays/us.rb",
|
78
|
+
"lib/holidays/za.rb",
|
79
|
+
"rakefile.rb",
|
80
|
+
"test/defs/test_defs_au.rb",
|
81
|
+
"test/defs/test_defs_ca.rb",
|
82
|
+
"test/defs/test_defs_cz.rb",
|
83
|
+
"test/defs/test_defs_de.rb",
|
84
|
+
"test/defs/test_defs_dk.rb",
|
85
|
+
"test/defs/test_defs_es.rb",
|
86
|
+
"test/defs/test_defs_europe.rb",
|
87
|
+
"test/defs/test_defs_fr.rb",
|
88
|
+
"test/defs/test_defs_gb.rb",
|
89
|
+
"test/defs/test_defs_ie.rb",
|
90
|
+
"test/defs/test_defs_is.rb",
|
91
|
+
"test/defs/test_defs_it.rb",
|
92
|
+
"test/defs/test_defs_mx.rb",
|
93
|
+
"test/defs/test_defs_nl.rb",
|
94
|
+
"test/defs/test_defs_no.rb",
|
95
|
+
"test/defs/test_defs_north_america.rb",
|
96
|
+
"test/defs/test_defs_nyse.rb",
|
97
|
+
"test/defs/test_defs_nz.rb",
|
98
|
+
"test/defs/test_defs_pt.rb",
|
99
|
+
"test/defs/test_defs_scandinavia.rb",
|
100
|
+
"test/defs/test_defs_se.rb",
|
101
|
+
"test/defs/test_defs_ups.rb",
|
102
|
+
"test/defs/test_defs_us.rb",
|
103
|
+
"test/defs/test_defs_za.rb",
|
104
|
+
"test/test_date.rb",
|
105
|
+
"test/test_helper.rb",
|
106
|
+
"test/test_holidays.rb",
|
107
|
+
"test/test_multiple_regions.rb"
|
108
|
+
]
|
109
|
+
s.homepage = %q{https://github.com/alexdunae/holidays}
|
110
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
111
|
+
s.require_paths = ["lib"]
|
112
|
+
s.rubygems_version = %q{1.3.7}
|
113
|
+
s.summary = %q{A collection of Ruby methods to deal with statutory and other holidays. You deserve a holiday!}
|
114
|
+
s.test_files = [
|
115
|
+
"test/defs/test_defs_au.rb",
|
116
|
+
"test/defs/test_defs_ca.rb",
|
117
|
+
"test/defs/test_defs_cz.rb",
|
118
|
+
"test/defs/test_defs_de.rb",
|
119
|
+
"test/defs/test_defs_dk.rb",
|
120
|
+
"test/defs/test_defs_es.rb",
|
121
|
+
"test/defs/test_defs_europe.rb",
|
122
|
+
"test/defs/test_defs_fr.rb",
|
123
|
+
"test/defs/test_defs_gb.rb",
|
124
|
+
"test/defs/test_defs_ie.rb",
|
125
|
+
"test/defs/test_defs_is.rb",
|
126
|
+
"test/defs/test_defs_it.rb",
|
127
|
+
"test/defs/test_defs_mx.rb",
|
128
|
+
"test/defs/test_defs_nl.rb",
|
129
|
+
"test/defs/test_defs_no.rb",
|
130
|
+
"test/defs/test_defs_north_america.rb",
|
131
|
+
"test/defs/test_defs_nyse.rb",
|
132
|
+
"test/defs/test_defs_nz.rb",
|
133
|
+
"test/defs/test_defs_pt.rb",
|
134
|
+
"test/defs/test_defs_scandinavia.rb",
|
135
|
+
"test/defs/test_defs_se.rb",
|
136
|
+
"test/defs/test_defs_ups.rb",
|
137
|
+
"test/defs/test_defs_us.rb",
|
138
|
+
"test/defs/test_defs_za.rb",
|
139
|
+
"test/test_date.rb",
|
140
|
+
"test/test_helper.rb",
|
141
|
+
"test/test_holidays.rb",
|
142
|
+
"test/test_multiple_regions.rb"
|
143
|
+
]
|
144
|
+
|
145
|
+
if s.respond_to? :specification_version then
|
146
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
147
|
+
s.specification_version = 3
|
148
|
+
|
149
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
150
|
+
else
|
151
|
+
end
|
152
|
+
else
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
data/lib/holidays.rb
CHANGED
@@ -1,404 +1,425 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require '
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# [<tt>:
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
@@
|
49
|
-
@@
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# [<tt
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# [<tt>
|
75
|
-
# [<tt>
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
date
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
#
|
239
|
-
#
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
#
|
286
|
-
#
|
287
|
-
#
|
288
|
-
#
|
289
|
-
#
|
290
|
-
#
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
#
|
305
|
-
#
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
#
|
322
|
-
#
|
323
|
-
#
|
324
|
-
#
|
325
|
-
#
|
326
|
-
#
|
327
|
-
|
328
|
-
#
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
#
|
357
|
-
#
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
370
|
-
#
|
371
|
-
#
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
#
|
378
|
-
#
|
379
|
-
#
|
380
|
-
#
|
381
|
-
|
382
|
-
#
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
# == Region options
|
8
|
+
# Holidays can be defined as belonging to one or more regions and sub regions.
|
9
|
+
# The Holidays#on, Holidays#between, Date#holidays and Date#holiday? methods
|
10
|
+
# each allow you to specify a specific region.
|
11
|
+
#
|
12
|
+
# There are several different ways that you can specify a region:
|
13
|
+
#
|
14
|
+
# [<tt>:region</tt>]
|
15
|
+
# By region. For example, return holidays in the Canada with <tt>:ca</tt>.
|
16
|
+
# [<tt>:region_</tt>]
|
17
|
+
# By region and sub regions. For example, return holidays in Germany
|
18
|
+
# and all its sub regions with <tt>:de_</tt>.
|
19
|
+
# [<tt>:region_sub</tt>]
|
20
|
+
# By sub region. Return national holidays in Spain plus holidays in Spain's
|
21
|
+
# Valencia region with <tt>:es_v</tt>.
|
22
|
+
# [<tt>:any</tt>]
|
23
|
+
# Any region. Return holidays from any loaded region.
|
24
|
+
#
|
25
|
+
# == Other options
|
26
|
+
# [<tt>:observed</tt>] Return holidays on the day they are observed (e.g. on a Monday if they fall on a Sunday).
|
27
|
+
# [<tt>:informal</tt>] Include informal holidays (e.g. Valentine's Day)
|
28
|
+
#
|
29
|
+
# == Examples
|
30
|
+
# Return all holidays in the <tt>:ca</tt> and <tt>:us</tt> regions on the day that they are
|
31
|
+
# observed.
|
32
|
+
#
|
33
|
+
# Holidays.between(from, to, :ca, :us, :observed)
|
34
|
+
#
|
35
|
+
# Return all holidays in <tt>:ca</tt> and any <tt>:ca</tt> sub-region.
|
36
|
+
#
|
37
|
+
# Holidays.between(from, to, :ca_)
|
38
|
+
#
|
39
|
+
# Return all holidays in <tt>:ca_bc</tt> sub-region (which includes the <tt>:ca</tt>), including informal holidays.
|
40
|
+
#
|
41
|
+
# Holidays.between(from, to, :ca_bc, :informal)
|
42
|
+
module Holidays
|
43
|
+
# Exception thrown when an unknown region is requested.
|
44
|
+
class UnknownRegionError < ArgumentError; end
|
45
|
+
|
46
|
+
VERSION = '1.0.0'
|
47
|
+
|
48
|
+
@@regions = []
|
49
|
+
@@holidays_by_month = {}
|
50
|
+
@@proc_cache = {}
|
51
|
+
|
52
|
+
WEEKS = {:first => 1, :second => 2, :third => 3, :fourth => 4, :fifth => 5, :last => -1, :second_last => -2, :third_last => -3}
|
53
|
+
MONTH_LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
54
|
+
DAY_SYMBOLS = Date::DAYNAMES.collect { |n| n.downcase.intern }
|
55
|
+
|
56
|
+
# Get all holidays on a given date.
|
57
|
+
#
|
58
|
+
# [<tt>date</tt>] A Date object.
|
59
|
+
# [<tt>:options</tt>] One or more region symbols, <tt>:informal</tt> and/or <tt>:observed</tt>.
|
60
|
+
#
|
61
|
+
# Returns an array of hashes or nil. See Holidays#between for the output
|
62
|
+
# format.
|
63
|
+
#
|
64
|
+
# Also available via Date#holidays.
|
65
|
+
def self.on(date, *options)
|
66
|
+
self.between(date, date, options)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Get all holidays occuring between two dates, inclusively.
|
70
|
+
#
|
71
|
+
# Returns an array of hashes or nil.
|
72
|
+
#
|
73
|
+
# Each holiday is returned as a hash with the following fields:
|
74
|
+
# [<tt>start_date</tt>] Ruby Date object.
|
75
|
+
# [<tt>end_date</tt>] Ruby Date object.
|
76
|
+
# [<tt>options</tt>] One or more region symbols, <tt>:informal</tt> and/or <tt>:observed</tt>.
|
77
|
+
#
|
78
|
+
# ==== Example
|
79
|
+
# from = Date.civil(2008,7,1)
|
80
|
+
# to = Date.civil(2008,7,31)
|
81
|
+
#
|
82
|
+
# Holidays.between(from, to, :ca, :us)
|
83
|
+
# => [{:name => 'Canada Day', :regions => [:ca]...}
|
84
|
+
# {:name => 'Independence Day'', :regions => [:us], ...}]
|
85
|
+
def self.between(start_date, end_date, *options)
|
86
|
+
# remove the timezone
|
87
|
+
start_date = start_date.new_offset(0) if start_date.respond_to?(:new_offset)
|
88
|
+
end_date = end_date.new_offset(0) if end_date.respond_to?(:new_offset)
|
89
|
+
|
90
|
+
# get simple dates
|
91
|
+
if start_date.respond_to?(:to_date)
|
92
|
+
start_date = start_date.to_date
|
93
|
+
else
|
94
|
+
start_date = Date.civil(start_date.year, start_date.mon, start_date.mday)
|
95
|
+
end
|
96
|
+
|
97
|
+
if end_date.respond_to?(:to_date)
|
98
|
+
end_date = end_date.to_date
|
99
|
+
else
|
100
|
+
end_date = Date.civil(end_date.year, end_date.mon, end_date.mday)
|
101
|
+
end
|
102
|
+
|
103
|
+
regions, observed, informal = parse_options(options)
|
104
|
+
holidays = []
|
105
|
+
|
106
|
+
dates = {}
|
107
|
+
(start_date..end_date).each do |date|
|
108
|
+
# Always include month '0' for variable-month holidays
|
109
|
+
dates[date.year] = [0] unless dates[date.year]
|
110
|
+
# TODO: test this, maybe should push then flatten
|
111
|
+
dates[date.year] << date.month unless dates[date.year].include?(date.month)
|
112
|
+
end
|
113
|
+
|
114
|
+
dates.each do |year, months|
|
115
|
+
months.each do |month|
|
116
|
+
next unless hbm = @@holidays_by_month[month]
|
117
|
+
|
118
|
+
hbm.each do |h|
|
119
|
+
next unless in_region?(regions, h[:regions])
|
120
|
+
|
121
|
+
# Skip informal holidays unless they have been requested
|
122
|
+
next if h[:type] == :informal and not informal
|
123
|
+
|
124
|
+
if h[:function]
|
125
|
+
# Holiday definition requires a calculation
|
126
|
+
result = call_proc(h[:function], year)
|
127
|
+
|
128
|
+
# Procs may return either Date or an integer representing mday
|
129
|
+
if result.kind_of?(Date)
|
130
|
+
month = result.month
|
131
|
+
mday = result.mday
|
132
|
+
else
|
133
|
+
mday = result
|
134
|
+
end
|
135
|
+
else
|
136
|
+
# Calculate the mday
|
137
|
+
mday = h[:mday] || Date.calculate_mday(year, month, h[:week], h[:wday])
|
138
|
+
end
|
139
|
+
|
140
|
+
# Silently skip bad mdays
|
141
|
+
begin
|
142
|
+
date = Date.civil(year, month, mday)
|
143
|
+
rescue; next; end
|
144
|
+
|
145
|
+
# If the :observed option is set, calculate the date when the holiday
|
146
|
+
# is observed.
|
147
|
+
if observed and h[:observed]
|
148
|
+
date = call_proc(h[:observed], date)
|
149
|
+
end
|
150
|
+
|
151
|
+
if date.between?(start_date, end_date)
|
152
|
+
holidays << {:date => date, :name => h[:name], :regions => h[:regions]}
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
holidays.sort{|a, b| a[:date] <=> b[:date] }
|
160
|
+
end
|
161
|
+
|
162
|
+
# Merge a new set of definitions into the Holidays module.
|
163
|
+
#
|
164
|
+
# This method is automatically called when including holiday definition
|
165
|
+
# files.
|
166
|
+
def self.merge_defs(regions, holidays) # :nodoc:
|
167
|
+
@@regions = @@regions | regions
|
168
|
+
@@regions.uniq!
|
169
|
+
|
170
|
+
holidays.each do |month, holiday_defs|
|
171
|
+
@@holidays_by_month[month] = [] unless @@holidays_by_month[month]
|
172
|
+
holiday_defs.each do |holiday_def|
|
173
|
+
|
174
|
+
exists = false
|
175
|
+
@@holidays_by_month[month].each do |ex|
|
176
|
+
# TODO: gross.
|
177
|
+
if ex[:name] == holiday_def[:name] and ex[:wday] == holiday_def[:wday] and ex[:mday] == holiday_def[:mday] and ex[:week] == holiday_def[:week] and ex[:function_id] == holiday_def[:function_id] and ex[:type] == holiday_def[:type] and ex[:observed_id] == holiday_def[:observed_id]
|
178
|
+
# append regions
|
179
|
+
ex[:regions] << holiday_def[:regions]
|
180
|
+
|
181
|
+
# Should do this once we're done
|
182
|
+
ex[:regions].flatten!
|
183
|
+
ex[:regions].uniq!
|
184
|
+
exists = true
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
@@holidays_by_month[month] << holiday_def unless exists
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Get the date of Easter Sunday in a given year. From Easter Sunday, it is
|
194
|
+
# possible to calculate many traditional holidays in Western countries.
|
195
|
+
# Returns a Date object.
|
196
|
+
def self.easter(year)
|
197
|
+
y = year
|
198
|
+
a = y % 19
|
199
|
+
b = y / 100
|
200
|
+
c = y % 100
|
201
|
+
d = b / 4
|
202
|
+
e = b % 4
|
203
|
+
f = (b + 8) / 25
|
204
|
+
g = (b - f + 1) / 3
|
205
|
+
h = (19 * a + b - d - g + 15) % 30
|
206
|
+
i = c / 4
|
207
|
+
k = c % 4
|
208
|
+
l = (32 + 2 * e + 2 * i - h - k) % 7
|
209
|
+
m = (a + 11 * h + 22 * l) / 451
|
210
|
+
month = (h + l - 7 * m + 114) / 31
|
211
|
+
day = ((h + l - 7 * m + 114) % 31) + 1
|
212
|
+
Date.civil(year, month, day)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Move date to Monday if it occurs on a Sunday.
|
216
|
+
# Used as a callback function.
|
217
|
+
def self.to_monday_if_sunday(date)
|
218
|
+
date += 1 if date.wday == 0
|
219
|
+
date
|
220
|
+
end
|
221
|
+
|
222
|
+
# Move date to Monday if it occurs on a Saturday on Sunday.
|
223
|
+
# Used as a callback function.
|
224
|
+
def self.to_monday_if_weekend(date)
|
225
|
+
date += 1 if date.wday == 0
|
226
|
+
date += 2 if date.wday == 6
|
227
|
+
date
|
228
|
+
end
|
229
|
+
|
230
|
+
# Move Boxing Day if it falls on a weekend, leaving room for Christmas.
|
231
|
+
# Used as a callback function.
|
232
|
+
def self.to_weekday_if_boxing_weekend(date)
|
233
|
+
date += 2 if date.wday == 6 or date.wday == 0
|
234
|
+
date
|
235
|
+
end
|
236
|
+
|
237
|
+
# Move date to Monday if it occurs on a Sunday or to Friday if it occurs on a
|
238
|
+
# Saturday.
|
239
|
+
# Used as a callback function.
|
240
|
+
def self.to_weekday_if_weekend(date)
|
241
|
+
date += 1 if date.wday == 0
|
242
|
+
date -= 1 if date.wday == 6
|
243
|
+
date
|
244
|
+
end
|
245
|
+
|
246
|
+
private
|
247
|
+
# Returns [(arr)regions, (bool)observed, (bool)informal]
|
248
|
+
def self.parse_options(*options) # :nodoc:
|
249
|
+
options.flatten!
|
250
|
+
observed = options.delete(:observed) ? true : false
|
251
|
+
informal = options.delete(:informal) ? true : false
|
252
|
+
regions = parse_regions(options)
|
253
|
+
return regions, observed, informal
|
254
|
+
end
|
255
|
+
|
256
|
+
# Check regions against list of supported regions and return an array of
|
257
|
+
# symbols.
|
258
|
+
#
|
259
|
+
# If a wildcard region is found (e.g. <tt>:ca_</tt>) it is expanded into all
|
260
|
+
# of its available sub regions.
|
261
|
+
def self.parse_regions(regions) # :nodoc:
|
262
|
+
regions = [regions] unless regions.kind_of?(Array)
|
263
|
+
return [:any] if regions.empty?
|
264
|
+
|
265
|
+
regions = regions.collect { |r| r.to_sym }
|
266
|
+
|
267
|
+
# Found sub region wild-card
|
268
|
+
regions.delete_if do |reg|
|
269
|
+
if reg.to_s =~ /_$/
|
270
|
+
prefix = reg.to_s.split('_').first
|
271
|
+
raise UnknownRegionError unless @@regions.include?(prefix.to_sym) or begin require "holidays/#{prefix}"; rescue LoadError; false; end
|
272
|
+
regions << @@regions.select { |dr| dr.to_s =~ Regexp.new("^#{reg}") }
|
273
|
+
true
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
regions.flatten!
|
278
|
+
|
279
|
+
require "holidays/north_america" if regions.include?(:us) # special case for north_america/US cross-linking
|
280
|
+
|
281
|
+
raise UnknownRegionError unless regions.all? { |r| r == :any or @@regions.include?(r) or begin require "holidays/#{r.to_s}"; rescue LoadError; false; end }
|
282
|
+
regions
|
283
|
+
end
|
284
|
+
|
285
|
+
# Check sub regions.
|
286
|
+
#
|
287
|
+
# When request :any, all holidays should be returned.
|
288
|
+
# When requesting :ca_bc, holidays in :ca or :ca_bc should be returned.
|
289
|
+
# When requesting :ca, holidays in :ca but not its subregions should be returned.
|
290
|
+
def self.in_region?(requested, available) # :nodoc:
|
291
|
+
return true if requested.include?(:any)
|
292
|
+
|
293
|
+
# When an underscore is encountered, derive the parent regions
|
294
|
+
# symbol and include both in the requested array.
|
295
|
+
requested = requested.collect do |r|
|
296
|
+
r.to_s =~ /_/ ? [r, r.to_s.gsub(/_[\w]*$/, '').to_sym] : r
|
297
|
+
end
|
298
|
+
|
299
|
+
requested = requested.flatten.uniq
|
300
|
+
|
301
|
+
available.any? { |avail| requested.include?(avail) }
|
302
|
+
end
|
303
|
+
|
304
|
+
# Call a proc function defined in a holiday definition file.
|
305
|
+
#
|
306
|
+
# Procs are cached.
|
307
|
+
#
|
308
|
+
# ==== Benchmarks
|
309
|
+
#
|
310
|
+
# Lookup Easter Sunday, with caching, by number of iterations:
|
311
|
+
#
|
312
|
+
# user system total real
|
313
|
+
# 0001 0.000000 0.000000 0.000000 ( 0.000000)
|
314
|
+
# 0010 0.000000 0.000000 0.000000 ( 0.000000)
|
315
|
+
# 0100 0.078000 0.000000 0.078000 ( 0.078000)
|
316
|
+
# 1000 0.641000 0.000000 0.641000 ( 0.641000)
|
317
|
+
# 5000 3.172000 0.015000 3.187000 ( 3.219000)
|
318
|
+
#
|
319
|
+
# Lookup Easter Sunday, without caching, by number of iterations:
|
320
|
+
#
|
321
|
+
# user system total real
|
322
|
+
# 0001 0.000000 0.000000 0.000000 ( 0.000000)
|
323
|
+
# 0010 0.016000 0.000000 0.016000 ( 0.016000)
|
324
|
+
# 0100 0.125000 0.000000 0.125000 ( 0.125000)
|
325
|
+
# 1000 1.234000 0.000000 1.234000 ( 1.234000)
|
326
|
+
# 5000 6.094000 0.031000 6.125000 ( 6.141000)
|
327
|
+
def self.call_proc(function, year) # :nodoc:
|
328
|
+
proc_key = Digest::MD5.hexdigest("#{function.to_s}_#{year.to_s}")
|
329
|
+
@@proc_cache[proc_key] = function.call(year) unless @@proc_cache[proc_key]
|
330
|
+
@@proc_cache[proc_key]
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# === Extending Ruby's Date class with the Holidays gem
|
335
|
+
# The Holidays gem automatically extends Ruby's Date class and gives you access
|
336
|
+
# to three new methods: holiday?, #holidays and #calculate_mday.
|
337
|
+
#
|
338
|
+
# ==== Examples
|
339
|
+
# Lookup Canada Day in the <tt>:ca</tt> region
|
340
|
+
# Date.civil(2008,7,1).holiday?(:ca)
|
341
|
+
# => true
|
342
|
+
#
|
343
|
+
# Lookup Canada Day in the <tt>:fr</tt> region
|
344
|
+
# Date.civil(2008,7,1).holiday?(:fr)
|
345
|
+
# => false
|
346
|
+
#
|
347
|
+
# Lookup holidays on North America in January 1.
|
348
|
+
# Date.civil(2008,1,1).holidays(:ca, :mx, :us, :informal, :observed)
|
349
|
+
# => [{:name => 'New Year\'s Day'...}]
|
350
|
+
class Date
|
351
|
+
include Holidays
|
352
|
+
|
353
|
+
# Get holidays on the current date.
|
354
|
+
#
|
355
|
+
# Returns an array of hashes or nil. See Holidays#between for options
|
356
|
+
# and the output format.
|
357
|
+
#
|
358
|
+
# Date.civil('2008-01-01').holidays(:ca_)
|
359
|
+
# => [{:name => 'New Year\'s Day',...}]
|
360
|
+
#
|
361
|
+
# Also available via Holidays#on.
|
362
|
+
def holidays(*options)
|
363
|
+
Holidays.on(self, options)
|
364
|
+
end
|
365
|
+
|
366
|
+
# Check if the current date is a holiday.
|
367
|
+
#
|
368
|
+
# Returns true or false.
|
369
|
+
#
|
370
|
+
# Date.civil('2008-01-01').holiday?(:ca)
|
371
|
+
# => true
|
372
|
+
def holiday?(*options)
|
373
|
+
holidays = self.holidays(options)
|
374
|
+
holidays && !holidays.empty?
|
375
|
+
end
|
376
|
+
|
377
|
+
# Calculate day of the month based on the week number and the day of the
|
378
|
+
# week.
|
379
|
+
#
|
380
|
+
# ==== Parameters
|
381
|
+
# [<tt>year</tt>] Integer.
|
382
|
+
# [<tt>month</tt>] Integer from 1-12.
|
383
|
+
# [<tt>week</tt>] One of <tt>:first</tt>, <tt>:second</tt>, <tt>:third</tt>,
|
384
|
+
# <tt>:fourth</tt>, <tt>:fifth</tt> or <tt>:last</tt>.
|
385
|
+
# [<tt>wday</tt>] Day of the week as an integer from 0 (Sunday) to 6
|
386
|
+
# (Saturday) or as a symbol (e.g. <tt>:monday</tt>).
|
387
|
+
#
|
388
|
+
# Returns an integer.
|
389
|
+
#
|
390
|
+
# ===== Examples
|
391
|
+
# First Monday of January, 2008:
|
392
|
+
# Date.calculate_mday(2008, 1, :first, :monday)
|
393
|
+
# => 7
|
394
|
+
#
|
395
|
+
# Third Thursday of December, 2008:
|
396
|
+
# Date.calculate_mday(2008, 12, :third, :thursday)
|
397
|
+
# => 18
|
398
|
+
#
|
399
|
+
# Last Monday of January, 2008:
|
400
|
+
# Date.calculate_mday(2008, 1, :last, 1)
|
401
|
+
# => 28
|
402
|
+
#--
|
403
|
+
# see http://www.irt.org/articles/js050/index.htm
|
404
|
+
def self.calculate_mday(year, month, week, wday)
|
405
|
+
raise ArgumentError, "Week parameter must be one of Holidays::WEEKS (provided #{week})." unless WEEKS.include?(week) or WEEKS.has_value?(week)
|
406
|
+
|
407
|
+
unless wday.kind_of?(Numeric) and wday.between?(0,6) or DAY_SYMBOLS.index(wday)
|
408
|
+
raise ArgumentError, "Wday parameter must be an integer between 0 and 6 or one of Date::DAY_SYMBOLS."
|
409
|
+
end
|
410
|
+
|
411
|
+
week = WEEKS[week] if week.kind_of?(Symbol)
|
412
|
+
wday = DAY_SYMBOLS.index(wday) if wday.kind_of?(Symbol)
|
413
|
+
|
414
|
+
# :first, :second, :third, :fourth or :fifth
|
415
|
+
if week > 0
|
416
|
+
return ((week - 1) * 7) + 1 + ((wday - Date.civil(year, month,(week-1)*7 + 1).wday) % 7)
|
417
|
+
end
|
418
|
+
|
419
|
+
days = MONTH_LENGTHS[month-1]
|
420
|
+
|
421
|
+
days = 29 if month == 2 and Date.leap?(year)
|
422
|
+
|
423
|
+
return days - ((Date.civil(year, month, days).wday - wday + 7) % 7) - (7 * (week.abs - 1))
|
424
|
+
end
|
404
425
|
end
|