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