wombleton-holidays 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG +22 -0
- data/LICENSE +21 -0
- data/README.rdoc +77 -0
- data/REFERENCES +16 -0
- data/VERSION +1 -0
- data/data/SYNTAX +111 -0
- data/data/au.yaml +111 -0
- data/data/build_defs.rb +152 -0
- data/data/ca.yaml +141 -0
- data/data/cz.yaml +68 -0
- data/data/de.yaml +111 -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 +26 -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/no.yaml +82 -0
- data/data/north_america_informal.yaml +49 -0
- data/data/nyse.yaml +63 -0
- data/data/nz.yaml +141 -0
- data/data/pt.yaml +85 -0
- data/data/se.yaml +91 -0
- data/data/united_nations.yaml +188 -0
- data/data/ups.yaml +56 -0
- data/data/us.yaml +81 -0
- data/data/za.yaml +78 -0
- data/holidays.gemspec +16 -0
- data/lib/holidays/MANIFEST +28 -0
- data/lib/holidays/au.rb +43 -0
- data/lib/holidays/ca.rb +69 -0
- data/lib/holidays/cz.rb +36 -0
- data/lib/holidays/de.rb +51 -0
- data/lib/holidays/dk.rb +48 -0
- data/lib/holidays/es.rb +53 -0
- data/lib/holidays/europe.rb +236 -0
- data/lib/holidays/fr.rb +37 -0
- data/lib/holidays/gb.rb +41 -0
- data/lib/holidays/ie.rb +33 -0
- data/lib/holidays/is.rb +62 -0
- data/lib/holidays/it.rb +36 -0
- data/lib/holidays/mx.rb +52 -0
- data/lib/holidays/nl.rb +37 -0
- data/lib/holidays/no.rb +40 -0
- data/lib/holidays/north_america.rb +108 -0
- data/lib/holidays/nyse.rb +33 -0
- data/lib/holidays/nz.rb +69 -0
- data/lib/holidays/pt.rb +38 -0
- data/lib/holidays/scandinavia.rb +115 -0
- data/lib/holidays/se.rb +53 -0
- data/lib/holidays/united_nations.rb +25 -0
- data/lib/holidays/ups.rb +32 -0
- data/lib/holidays/us.rb +49 -0
- data/lib/holidays/za.rb +36 -0
- data/lib/holidays.rb +408 -0
- data/rakefile.rb +103 -0
- data/test/defs/test_defs_au.rb +35 -0
- data/test/defs/test_defs_ca.rb +28 -0
- data/test/defs/test_defs_cz.rb +25 -0
- data/test/defs/test_defs_de.rb +48 -0
- data/test/defs/test_defs_dk.rb +29 -0
- data/test/defs/test_defs_es.rb +56 -0
- data/test/defs/test_defs_europe.rb +277 -0
- data/test/defs/test_defs_fr.rb +25 -0
- data/test/defs/test_defs_gb.rb +35 -0
- data/test/defs/test_defs_ie.rb +20 -0
- data/test/defs/test_defs_is.rb +32 -0
- data/test/defs/test_defs_it.rb +24 -0
- data/test/defs/test_defs_mx.rb +21 -0
- data/test/defs/test_defs_nl.rb +23 -0
- data/test/defs/test_defs_no.rb +28 -0
- data/test/defs/test_defs_north_america.rb +53 -0
- data/test/defs/test_defs_nyse.rb +21 -0
- data/test/defs/test_defs_nz.rb +21 -0
- data/test/defs/test_defs_pt.rb +31 -0
- data/test/defs/test_defs_scandinavia.rb +75 -0
- data/test/defs/test_defs_se.rb +32 -0
- data/test/defs/test_defs_ups.rb +20 -0
- data/test/defs/test_defs_us.rb +22 -0
- data/test/defs/test_defs_za.rb +24 -0
- data/test/test_date.rb +101 -0
- data/test/test_helper.rb +11 -0
- data/test/test_holidays.rb +117 -0
- data/test/test_multiple_regions.rb +20 -0
- metadata +172 -0
data/lib/holidays.rb
ADDED
@@ -0,0 +1,408 @@
|
|
1
|
+
# coding: 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 = '0.9.2'
|
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
|
+
start_date = start_date.to_date if start_date.respond_to?(:to_date)
|
87
|
+
end_date = end_date.to_date if end_date.respond_to?(:to_date)
|
88
|
+
regions, observed, informal = parse_options(options)
|
89
|
+
holidays = []
|
90
|
+
|
91
|
+
dates = {}
|
92
|
+
(start_date..end_date).each do |date|
|
93
|
+
# Always include month '0' for variable-month holidays
|
94
|
+
dates[date.year] = [0] unless dates[date.year]
|
95
|
+
# TODO: test this, maybe should push then flatten
|
96
|
+
dates[date.year] << date.month unless dates[date.year].include?(date.month)
|
97
|
+
end
|
98
|
+
|
99
|
+
dates.each do |year, months|
|
100
|
+
months.each do |month|
|
101
|
+
next unless hbm = @@holidays_by_month[month]
|
102
|
+
|
103
|
+
hbm.each do |h|
|
104
|
+
next unless in_region?(regions, h[:regions])
|
105
|
+
|
106
|
+
# Skip informal holidays unless they have been requested
|
107
|
+
next if h[:type] == :informal and not informal
|
108
|
+
|
109
|
+
if h[:function]
|
110
|
+
# Holiday definition requires a calculation
|
111
|
+
result = call_proc(h[:function], year)
|
112
|
+
|
113
|
+
# Procs may return either Date or an integer representing mday
|
114
|
+
if result.kind_of?(Date)
|
115
|
+
month = result.month
|
116
|
+
mday = result.mday
|
117
|
+
else
|
118
|
+
mday = result
|
119
|
+
end
|
120
|
+
else
|
121
|
+
# Calculate the mday
|
122
|
+
mday = h[:mday] || Date.calculate_mday(year, month, h[:week], h[:wday])
|
123
|
+
end
|
124
|
+
|
125
|
+
# Silently skip bad mdays
|
126
|
+
begin
|
127
|
+
date = Date.civil(year, month, mday)
|
128
|
+
rescue; next; end
|
129
|
+
|
130
|
+
# If the :observed option is set, calculate the date when the holiday
|
131
|
+
# is observed.
|
132
|
+
if observed and h[:observed]
|
133
|
+
date = call_proc(h[:observed], date)
|
134
|
+
end
|
135
|
+
|
136
|
+
if date.between?(start_date, end_date)
|
137
|
+
holidays << {:date => date, :name => h[:name], :regions => h[:regions]}
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
holidays
|
145
|
+
end
|
146
|
+
|
147
|
+
# Merge a new set of definitions into the Holidays module.
|
148
|
+
#
|
149
|
+
# This method is automatically called when including holiday definition
|
150
|
+
# files.
|
151
|
+
def self.merge_defs(regions, holidays) # :nodoc:
|
152
|
+
@@regions = @@regions | regions
|
153
|
+
@@regions.uniq!
|
154
|
+
|
155
|
+
holidays.each do |month, holiday_defs|
|
156
|
+
@@holidays_by_month[month] = [] unless @@holidays_by_month[month]
|
157
|
+
holiday_defs.each do |holiday_def|
|
158
|
+
|
159
|
+
exists = false
|
160
|
+
@@holidays_by_month[month].each do |ex|
|
161
|
+
# TODO: gross.
|
162
|
+
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]
|
163
|
+
# append regions
|
164
|
+
ex[:regions] << holiday_def[:regions]
|
165
|
+
|
166
|
+
# Should do this once we're done
|
167
|
+
ex[:regions].flatten!
|
168
|
+
ex[:regions].uniq!
|
169
|
+
exists = true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
@@holidays_by_month[month] << holiday_def unless exists
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Get the date of Easter Sunday in a given year. From Easter Sunday, it is
|
179
|
+
# possible to calculate many traditional holidays in Western countries.
|
180
|
+
# Returns a Date object.
|
181
|
+
def self.easter(year)
|
182
|
+
y = year
|
183
|
+
a = y % 19
|
184
|
+
b = y / 100
|
185
|
+
c = y % 100
|
186
|
+
d = b / 4
|
187
|
+
e = b % 4
|
188
|
+
f = (b + 8) / 25
|
189
|
+
g = (b - f + 1) / 3
|
190
|
+
h = (19 * a + b - d - g + 15) % 30
|
191
|
+
i = c / 4
|
192
|
+
k = c % 4
|
193
|
+
l = (32 + 2 * e + 2 * i - h - k) % 7
|
194
|
+
m = (a + 11 * h + 22 * l) / 451
|
195
|
+
month = (h + l - 7 * m + 114) / 31
|
196
|
+
day = ((h + l - 7 * m + 114) % 31) + 1
|
197
|
+
Date.civil(year, month, day)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Move date to Monday if it occurs on a Sunday.
|
201
|
+
# Used as a callback function.
|
202
|
+
def self.to_monday_if_sunday(date)
|
203
|
+
date += 1 if date.wday == 0
|
204
|
+
date
|
205
|
+
end
|
206
|
+
|
207
|
+
# Move date to Monday if it occurs on a Saturday on Sunday.
|
208
|
+
# Used as a callback function.
|
209
|
+
def self.to_monday_if_weekend(date)
|
210
|
+
date += 1 if date.wday == 0
|
211
|
+
date += 2 if date.wday == 6
|
212
|
+
date
|
213
|
+
end
|
214
|
+
|
215
|
+
# Move Boxing Day if it falls on a weekend, leaving room for Christmas.
|
216
|
+
# Used as a callback function.
|
217
|
+
def self.to_weekday_if_boxing_weekend(date)
|
218
|
+
date += 2 if date.wday == 6 or date.wday == 0
|
219
|
+
date
|
220
|
+
end
|
221
|
+
|
222
|
+
# Move date to Monday if it occurs on a Sunday or to Friday if it occurs on a
|
223
|
+
# Saturday.
|
224
|
+
# Used as a callback function.
|
225
|
+
def self.to_weekday_if_weekend(date)
|
226
|
+
date += 1 if date.wday == 0
|
227
|
+
date -= 1 if date.wday == 6
|
228
|
+
date
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
# Returns [(arr)regions, (bool)observed, (bool)informal]
|
233
|
+
def self.parse_options(*options) # :nodoc:
|
234
|
+
options.flatten!
|
235
|
+
observed = options.delete(:observed) ? true : false
|
236
|
+
informal = options.delete(:informal) ? true : false
|
237
|
+
regions = parse_regions(options)
|
238
|
+
return regions, observed, informal
|
239
|
+
end
|
240
|
+
|
241
|
+
# Check regions against list of supported regions and return an array of
|
242
|
+
# symbols.
|
243
|
+
#
|
244
|
+
# If a wildcard region is found (e.g. <tt>:ca_</tt>) it is expanded into all
|
245
|
+
# of its available sub regions.
|
246
|
+
def self.parse_regions(regions) # :nodoc:
|
247
|
+
regions = [regions] unless regions.kind_of?(Array)
|
248
|
+
return [:any] if regions.empty?
|
249
|
+
|
250
|
+
regions = regions.collect { |r| r.to_sym }
|
251
|
+
|
252
|
+
# Found sub region wild-card
|
253
|
+
regions.delete_if do |reg|
|
254
|
+
if reg.to_s =~ /_$/
|
255
|
+
regions << @@regions.select { |dr| dr.to_s =~ Regexp.new("^#{reg}") }
|
256
|
+
true
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
regions.flatten!
|
261
|
+
|
262
|
+
require "holidays/north_america" if regions.include?(:us) # special case for north_america/US cross-linking
|
263
|
+
|
264
|
+
raise UnknownRegionError unless regions.all? { |r| r == :any or @@regions.include?(r) or begin require "holidays/#{r.to_s}"; rescue LoadError; false; end }
|
265
|
+
regions
|
266
|
+
end
|
267
|
+
|
268
|
+
# Check sub regions.
|
269
|
+
#
|
270
|
+
# When request :any, all holidays should be returned.
|
271
|
+
# When requesting :ca_bc, holidays in :ca or :ca_bc should be returned.
|
272
|
+
# When requesting :ca, holidays in :ca but not its subregions should be returned.
|
273
|
+
def self.in_region?(requested, available) # :nodoc:
|
274
|
+
return true if requested.include?(:any)
|
275
|
+
|
276
|
+
# When an underscore is encountered, derive the parent regions
|
277
|
+
# symbol and include both in the requested array.
|
278
|
+
requested = requested.collect do |r|
|
279
|
+
r.to_s =~ /_/ ? [r, r.to_s.gsub(/_[\w]*$/, '').to_sym] : r
|
280
|
+
end
|
281
|
+
|
282
|
+
requested = requested.flatten.uniq
|
283
|
+
|
284
|
+
available.any? { |avail| requested.include?(avail) }
|
285
|
+
end
|
286
|
+
|
287
|
+
# Call a proc function defined in a holiday definition file.
|
288
|
+
#
|
289
|
+
# Procs are cached.
|
290
|
+
#
|
291
|
+
# ==== Benchmarks
|
292
|
+
#
|
293
|
+
# Lookup Easter Sunday, with caching, by number of iterations:
|
294
|
+
#
|
295
|
+
# user system total real
|
296
|
+
# 0001 0.000000 0.000000 0.000000 ( 0.000000)
|
297
|
+
# 0010 0.000000 0.000000 0.000000 ( 0.000000)
|
298
|
+
# 0100 0.078000 0.000000 0.078000 ( 0.078000)
|
299
|
+
# 1000 0.641000 0.000000 0.641000 ( 0.641000)
|
300
|
+
# 5000 3.172000 0.015000 3.187000 ( 3.219000)
|
301
|
+
#
|
302
|
+
# Lookup Easter Sunday, without caching, by number of iterations:
|
303
|
+
#
|
304
|
+
# user system total real
|
305
|
+
# 0001 0.000000 0.000000 0.000000 ( 0.000000)
|
306
|
+
# 0010 0.016000 0.000000 0.016000 ( 0.016000)
|
307
|
+
# 0100 0.125000 0.000000 0.125000 ( 0.125000)
|
308
|
+
# 1000 1.234000 0.000000 1.234000 ( 1.234000)
|
309
|
+
# 5000 6.094000 0.031000 6.125000 ( 6.141000)
|
310
|
+
def self.call_proc(function, year) # :nodoc:
|
311
|
+
proc_key = Digest::MD5.hexdigest("#{function.to_s}_#{year.to_s}")
|
312
|
+
@@proc_cache[proc_key] = function.call(year) unless @@proc_cache[proc_key]
|
313
|
+
@@proc_cache[proc_key]
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# === Extending Ruby's Date class with the Holidays gem
|
318
|
+
# The Holidays gem automatically extends Ruby's Date class and gives you access
|
319
|
+
# to three new methods: holiday?, #holidays and #calculate_mday.
|
320
|
+
#
|
321
|
+
# ==== Examples
|
322
|
+
# Lookup Canada Day in the <tt>:ca</tt> region
|
323
|
+
# Date.civil(2008,7,1).holiday?(:ca)
|
324
|
+
# => true
|
325
|
+
#
|
326
|
+
# Lookup Canada Day in the <tt>:fr</tt> region
|
327
|
+
# Date.civil(2008,7,1).holiday?(:fr)
|
328
|
+
# => false
|
329
|
+
#
|
330
|
+
# Lookup holidays on North America in January 1.
|
331
|
+
# Date.civil(2008,1,1).holidays(:ca, :mx, :us, :informal, :observed)
|
332
|
+
# => [{:name => 'New Year\'s Day'...}]
|
333
|
+
class Date
|
334
|
+
include Holidays
|
335
|
+
|
336
|
+
# Get holidays on the current date.
|
337
|
+
#
|
338
|
+
# Returns an array of hashes or nil. See Holidays#between for options
|
339
|
+
# and the output format.
|
340
|
+
#
|
341
|
+
# Date.civil('2008-01-01').holidays(:ca_)
|
342
|
+
# => [{:name => 'New Year\'s Day',...}]
|
343
|
+
#
|
344
|
+
# Also available via Holidays#on.
|
345
|
+
def holidays(*options)
|
346
|
+
Holidays.on(self, options)
|
347
|
+
end
|
348
|
+
|
349
|
+
# Check if the current date is a holiday.
|
350
|
+
#
|
351
|
+
# Returns true or false.
|
352
|
+
#
|
353
|
+
# Date.civil('2008-01-01').holiday?(:ca)
|
354
|
+
# => true
|
355
|
+
def holiday?(*options)
|
356
|
+
holidays = self.holidays(options)
|
357
|
+
holidays && !holidays.empty?
|
358
|
+
end
|
359
|
+
|
360
|
+
# Calculate day of the month based on the week number and the day of the
|
361
|
+
# week.
|
362
|
+
#
|
363
|
+
# ==== Parameters
|
364
|
+
# [<tt>year</tt>] Integer.
|
365
|
+
# [<tt>month</tt>] Integer from 1-12.
|
366
|
+
# [<tt>week</tt>] One of <tt>:first</tt>, <tt>:second</tt>, <tt>:third</tt>,
|
367
|
+
# <tt>:fourth</tt>, <tt>:fifth</tt> or <tt>:last</tt>.
|
368
|
+
# [<tt>wday</tt>] Day of the week as an integer from 0 (Sunday) to 6
|
369
|
+
# (Saturday) or as a symbol (e.g. <tt>:monday</tt>).
|
370
|
+
#
|
371
|
+
# Returns an integer.
|
372
|
+
#
|
373
|
+
# ===== Examples
|
374
|
+
# First Monday of January, 2008:
|
375
|
+
# Date.calculate_mday(2008, 1, :first, :monday)
|
376
|
+
# => 7
|
377
|
+
#
|
378
|
+
# Third Thursday of December, 2008:
|
379
|
+
# Date.calculate_mday(2008, 12, :third, :thursday)
|
380
|
+
# => 18
|
381
|
+
#
|
382
|
+
# Last Monday of January, 2008:
|
383
|
+
# Date.calculate_mday(2008, 1, :last, 1)
|
384
|
+
# => 28
|
385
|
+
#--
|
386
|
+
# see http://www.irt.org/articles/js050/index.htm
|
387
|
+
def self.calculate_mday(year, month, week, wday)
|
388
|
+
raise ArgumentError, "Week parameter must be one of Holidays::WEEKS (provided #{week})." unless WEEKS.include?(week) or WEEKS.has_value?(week)
|
389
|
+
|
390
|
+
unless wday.kind_of?(Numeric) and wday.between?(0,6) or DAY_SYMBOLS.index(wday)
|
391
|
+
raise ArgumentError, "Wday parameter must be an integer between 0 and 6 or one of Date::DAY_SYMBOLS."
|
392
|
+
end
|
393
|
+
|
394
|
+
week = WEEKS[week] if week.kind_of?(Symbol)
|
395
|
+
wday = DAY_SYMBOLS.index(wday) if wday.kind_of?(Symbol)
|
396
|
+
|
397
|
+
# :first, :second, :third, :fourth or :fifth
|
398
|
+
if week > 0
|
399
|
+
return ((week - 1) * 7) + 1 + ((7 + wday - Date.civil(year, month,(week-1)*7 + 1).wday) % 7)
|
400
|
+
end
|
401
|
+
|
402
|
+
days = MONTH_LENGTHS[month-1]
|
403
|
+
|
404
|
+
days = 29 if month == 2 and Date.leap?(year)
|
405
|
+
|
406
|
+
return days - ((Date.civil(year, month, days).wday - wday + 7) % 7) - (7 * (week.abs - 1))
|
407
|
+
end
|
408
|
+
end
|
data/rakefile.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'yaml'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'lib/holidays'
|
8
|
+
require 'data/build_defs'
|
9
|
+
|
10
|
+
desc 'Run all tests'
|
11
|
+
task :test => ["test:lib", "test:defs"]
|
12
|
+
|
13
|
+
namespace :test do
|
14
|
+
desc 'Run the unit tests.'
|
15
|
+
Rake::TestTask.new(:defs) do |t|
|
16
|
+
t.libs << 'lib'
|
17
|
+
t.test_files = FileList['test/defs/test*.rb'].exclude('test_helper.rb')
|
18
|
+
t.verbose = false
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Run the definition tests.'
|
22
|
+
Rake::TestTask.new(:lib) do |t|
|
23
|
+
t.libs << 'lib'
|
24
|
+
t.test_files = FileList['test/test*.rb'].exclude('test_helper.rb')
|
25
|
+
t.verbose = false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
task :doc => ["defs:manifest", :rdoc]
|
30
|
+
|
31
|
+
desc 'Generate documentation.'
|
32
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
33
|
+
rdoc.rdoc_dir = 'doc'
|
34
|
+
rdoc.title = 'Ruby Holidays Gem'
|
35
|
+
rdoc.options << '--all' << '--inline-source' << '--line-numbers'
|
36
|
+
rdoc.options << '--charset' << 'utf-8'
|
37
|
+
rdoc.rdoc_files.include('README.rdoc')
|
38
|
+
rdoc.rdoc_files.include('data/SYNTAX')
|
39
|
+
rdoc.rdoc_files.include('lib/holidays/MANIFEST')
|
40
|
+
rdoc.rdoc_files.include('REFERENCES')
|
41
|
+
rdoc.rdoc_files.include('CHANGELOG')
|
42
|
+
rdoc.rdoc_files.include('LICENSE')
|
43
|
+
rdoc.rdoc_files.include('lib/*.rb')
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Definition file tasks'
|
47
|
+
namespace :defs do
|
48
|
+
DATA_PATH = 'data'
|
49
|
+
|
50
|
+
desc 'Build holiday definition files'
|
51
|
+
task :build_all do
|
52
|
+
# load the index
|
53
|
+
def_index = YAML.load_file("#{DATA_PATH}/index.yaml")
|
54
|
+
|
55
|
+
# create a dir for the generated tests
|
56
|
+
FileUtils.mkdir_p('test/defs')
|
57
|
+
|
58
|
+
def_index['defs'].each do |region, files|
|
59
|
+
puts "Building #{region} definition module:"
|
60
|
+
files = files.collect { |f| "#{DATA_PATH}/#{f}" }
|
61
|
+
files.uniq!
|
62
|
+
|
63
|
+
module_src, test_src = parse_holiday_defs(region, files)
|
64
|
+
File.open("lib/holidays/#{region.downcase.to_s}.rb","w") do |file|
|
65
|
+
file.puts module_src
|
66
|
+
end
|
67
|
+
unless test_src.empty?
|
68
|
+
File.open("test/defs/test_defs_#{region.downcase.to_s}.rb","w") do |file|
|
69
|
+
file.puts test_src
|
70
|
+
end
|
71
|
+
end
|
72
|
+
puts "Done.\n\n"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
desc 'Build the definition manifest.'
|
77
|
+
task :manifest do
|
78
|
+
File.open("lib/holidays/MANIFEST","w") do |file|
|
79
|
+
file.puts <<-EOH
|
80
|
+
==== Regional definitions
|
81
|
+
The following definition files are included in this installation:
|
82
|
+
|
83
|
+
EOH
|
84
|
+
FileList.new('lib/holidays/*.rb').each do |str|
|
85
|
+
file.puts('* ' + str.gsub(/^lib\/|\.rb$/, ''))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
begin
|
92
|
+
require 'jeweler'
|
93
|
+
Jeweler::Tasks.new do |gemspec|
|
94
|
+
gemspec.name = "wombleton-holidays"
|
95
|
+
gemspec.summary = "A collection of Ruby methods to deal with statutory and other holidays. You deserve a holiday!"
|
96
|
+
gemspec.description = "A collection of Ruby methods to deal with statutory and other holidays. You deserve a holiday!"
|
97
|
+
gemspec.email = "wombleton@gmail.com"
|
98
|
+
gemspec.homepage = "http://github.com/wombleton/holidays"
|
99
|
+
gemspec.authors = ["Alex Dunae", "Rowan Crawford"]
|
100
|
+
end
|
101
|
+
rescue LoadError
|
102
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
103
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
# This file is generated by the Ruby Holiday gem.
|
4
|
+
#
|
5
|
+
# Definitions loaded: data/au.yaml
|
6
|
+
class AuDefinitionTests < Test::Unit::TestCase # :nodoc:
|
7
|
+
|
8
|
+
def test_au
|
9
|
+
{Date.civil(2007,1,1) => 'New Year\'s Day',
|
10
|
+
Date.civil(2007,1,26) => 'Australia Day',
|
11
|
+
Date.civil(2007,4,6) => 'Good Friday',
|
12
|
+
Date.civil(2007,4,9) => 'Easter Monday',
|
13
|
+
Date.civil(2007,4,25) => 'ANZAC Day',
|
14
|
+
Date.civil(2007,12,25) => 'Christmas Day',
|
15
|
+
Date.civil(2007,12,26) => 'Boxing Day'}.each do |date, name|
|
16
|
+
assert_equal name, (Holidays.on(date, :au, :informal)[0] || {})[:name]
|
17
|
+
end
|
18
|
+
|
19
|
+
[:au_sa, :au_act, :au_nsw, :au_].each do |r|
|
20
|
+
assert_equal 'Labour Day', Date.civil(2007,10,1).holidays(r)[0][:name]
|
21
|
+
end
|
22
|
+
|
23
|
+
[:au_sa, :au_act, :au_nsw, :au_vic, :au_tas, :au_qld, :au_nt, :au_].each do |r|
|
24
|
+
assert_equal 'Queen\'s Birthday', Date.civil(2007,6,11).holidays(r)[0][:name]
|
25
|
+
end
|
26
|
+
|
27
|
+
assert_equal 'Labour Day', Date.civil(2007,3,5).holidays(:au_wa)[0][:name]
|
28
|
+
assert_equal 'Labour Day', Date.civil(2007,3,12).holidays(:au_vic)[0][:name]
|
29
|
+
assert_equal 'Labour Day', Date.civil(2007,5,7).holidays(:au_qld)[0][:name]
|
30
|
+
|
31
|
+
assert_equal 'May Day', Date.civil(2007,5,7).holidays(:au_nt)[0][:name]
|
32
|
+
|
33
|
+
assert_equal 'Eight Hours Day', Date.civil(2007,3,12).holidays(:au_tas)[0][:name]
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
# This file is generated by the Ruby Holiday gem.
|
4
|
+
#
|
5
|
+
# Definitions loaded: data/ca.yaml, data/north_america_informal.yaml
|
6
|
+
class CaDefinitionTests < Test::Unit::TestCase # :nodoc:
|
7
|
+
|
8
|
+
def test_ca
|
9
|
+
{Date.civil(2008,1,1) => 'New Year\'s Day',
|
10
|
+
Date.civil(2008,3,21) => 'Good Friday',
|
11
|
+
Date.civil(2008,3,24) => 'Easter Monday',
|
12
|
+
Date.civil(2008,5,19) => 'Victoria Day',
|
13
|
+
Date.civil(2008,7,1) => 'Canada Day',
|
14
|
+
Date.civil(2008,9,1) => 'Labour Day',
|
15
|
+
Date.civil(2008,10,13) => 'Thanksgiving',
|
16
|
+
Date.civil(2008,11,11) => 'Rememberance Day',
|
17
|
+
Date.civil(2008,12,25) => 'Christmas Day',
|
18
|
+
Date.civil(2008,12,26) => 'Boxing Day'}.each do |date, name|
|
19
|
+
assert_equal name, (Holidays.on(date, :ca, :informal)[0] || {})[:name]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Victoria Day
|
23
|
+
[Date.civil(2004,5,24), Date.civil(2005,5,23), Date.civil(2006,5,22),
|
24
|
+
Date.civil(2007,5,21), Date.civil(2008,5,19)].each do |date|
|
25
|
+
assert_equal 'Victoria Day', Holidays.on(date, :ca)[0][:name]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
# This file is generated by the Ruby Holiday gem.
|
4
|
+
#
|
5
|
+
# Definitions loaded: data/cz.yaml
|
6
|
+
class CzDefinitionTests < Test::Unit::TestCase # :nodoc:
|
7
|
+
|
8
|
+
def test_cz
|
9
|
+
{Date.civil(2010,1,1) => 'Den obnovy samostatného českého státu',
|
10
|
+
Date.civil(2010,4,5) => 'Velikonoční pondělí',
|
11
|
+
Date.civil(2010,5,1) => 'Svátek práce',
|
12
|
+
Date.civil(2010,5,8) => 'Den vítězství',
|
13
|
+
Date.civil(2010,7,5) => 'Den slovanských věrozvěstů Cyrila a Metoděje',
|
14
|
+
Date.civil(2010,7,6) => 'Den upálení mistra Jana Husa',
|
15
|
+
Date.civil(2010,9,28) => 'Den české státnosti',
|
16
|
+
Date.civil(2010,10,28) => 'Den vzniku samostatného československého státu',
|
17
|
+
Date.civil(2010,11,17) => 'Den boje za svobodu a demokracii',
|
18
|
+
Date.civil(2010,12,24) => 'Štědrý den',
|
19
|
+
Date.civil(2010,12,25) => '1. svátek vánoční',
|
20
|
+
Date.civil(2010,12,26) => '2. svátek vánoční'}.each do |date, name|
|
21
|
+
assert_equal name, (Holidays.on(date, :cz, :informal)[0] || {})[:name]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
# This file is generated by the Ruby Holiday gem.
|
4
|
+
#
|
5
|
+
# Definitions loaded: data/de.yaml
|
6
|
+
class DeDefinitionTests < Test::Unit::TestCase # :nodoc:
|
7
|
+
|
8
|
+
def test_de
|
9
|
+
{Date.civil(2009,1,1) => 'Neujahrstag',
|
10
|
+
Date.civil(2009,4,10) => 'Karfreitag',
|
11
|
+
Date.civil(2009,4,13) => 'Ostermontag',
|
12
|
+
Date.civil(2009,5,1) => 'Tag der Arbeit',
|
13
|
+
Date.civil(2009,5,21) => 'Christi Himmelfahrt',
|
14
|
+
Date.civil(2009,6,1) => 'Pfingstmontag',
|
15
|
+
Date.civil(2009,10,3) => 'Tag der Deutschen Einheit',
|
16
|
+
Date.civil(2009,12,25) => '1. Weihnachtstag',
|
17
|
+
Date.civil(2009,12,26) => '2. Weihnachtstag'}.each do |date, name|
|
18
|
+
assert_equal name, (Holidays.on(date, :de, :informal)[0] || {})[:name]
|
19
|
+
end
|
20
|
+
|
21
|
+
[:de_bw, :de_by, :de_st, :de_].each do |r|
|
22
|
+
assert_equal 'Heilige Drei Könige', Date.civil(2009,1,6).holidays(r)[0][:name]
|
23
|
+
end
|
24
|
+
|
25
|
+
[:de_bw, :de_by, :de_he, :de_nw, :de_rp, :de_sl, :de_].each do |r|
|
26
|
+
assert_equal 'Fronleichnam', Date.civil(2009,6,11).holidays(r)[0][:name]
|
27
|
+
end
|
28
|
+
|
29
|
+
[:de_by, :de_sl, :de_].each do |r|
|
30
|
+
assert_equal 'Mariä Himmelfahrt', Date.civil(2009,8,15).holidays(r)[0][:name]
|
31
|
+
end
|
32
|
+
|
33
|
+
[:de_bb, :de_mv, :de_sn, :de_st, :de_th, :de_].each do |r|
|
34
|
+
assert_equal 'Reformationstag', Date.civil(2009,10,31).holidays(r)[0][:name]
|
35
|
+
end
|
36
|
+
|
37
|
+
[:de_bw, :de_by, :de_nw, :de_rp, :de_sl, :de_].each do |r|
|
38
|
+
assert_equal 'Allerheiligen', Date.civil(2009,11,1).holidays(r)[0][:name]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Repentance Day
|
42
|
+
assert_equal 'Buß- und Bettag', Date.civil(2004,11,17).holidays(:de_sn)[0][:name]
|
43
|
+
assert_equal 'Buß- und Bettag', Date.civil(2005,11,16).holidays(:de_sn)[0][:name]
|
44
|
+
assert_equal 'Buß- und Bettag', Date.civil(2006,11,22).holidays(:de_sn)[0][:name]
|
45
|
+
assert_equal 'Buß- und Bettag', Date.civil(2009,11,18).holidays(:de_sn)[0][:name]
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
# This file is generated by the Ruby Holiday gem.
|
4
|
+
#
|
5
|
+
# Definitions loaded: data/dk.yaml
|
6
|
+
class DkDefinitionTests < Test::Unit::TestCase # :nodoc:
|
7
|
+
|
8
|
+
def test_dk
|
9
|
+
{Date.civil(2007,1,1) => 'Nytårsdag',
|
10
|
+
Date.civil(2007,2,18) => 'Fastelavn',
|
11
|
+
Date.civil(2007,4,9) => 'Danmarks besættelse',
|
12
|
+
Date.civil(2007,4,16) => 'Dronningens fødselsdag',
|
13
|
+
Date.civil(2007,4,5) => 'Skærtorsdag',
|
14
|
+
Date.civil(2007,4,6) => 'Langfredag',
|
15
|
+
Date.civil(2007,4,8) => 'Påskedag',
|
16
|
+
Date.civil(2007,4,9) => '2. påskedag',
|
17
|
+
Date.civil(2007,5,1) => 'Arbejdernes kampdag',
|
18
|
+
Date.civil(2007,5,4) => 'Store Bededag',
|
19
|
+
Date.civil(2007,5,17) => 'Kristi Himmelfartsdag',
|
20
|
+
Date.civil(2007,5,27) => 'Pinsedag',
|
21
|
+
Date.civil(2007,5,28) => '2. Pinsedag',
|
22
|
+
Date.civil(2007,6,5) => 'Grundlovsdag',
|
23
|
+
Date.civil(2007,12,24) => 'Juleaftensdag',
|
24
|
+
Date.civil(2007,12,25) => '1. juledag',
|
25
|
+
Date.civil(2007,12,26) => '2. juledag'}.each do |date, name|
|
26
|
+
assert_equal name, (Holidays.on(date, :dk, :informal)[0] || {})[:name]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|