Module: Holidays

Included in:
Date
Defined in:
lib/holidays.rb,
lib/holidays/fi.rb,
lib/holidays/fr.rb,
lib/holidays/be.rb,
lib/holidays/de.rb,
lib/holidays/jp.rb,
lib/holidays/pt.rb,
lib/holidays/hu.rb,
lib/holidays/hr.rb,
lib/holidays/is.rb,
lib/holidays/pl.rb,
lib/holidays/br.rb,
lib/holidays/ar.rb,
lib/holidays/gb.rb,
lib/holidays/ma.rb,
lib/holidays/it.rb,
lib/holidays/li.rb,
lib/holidays/at.rb,
lib/holidays/nz.rb,
lib/holidays/cz.rb,
lib/holidays/se.rb,
lib/holidays/si.rb,
lib/holidays/ca.rb,
lib/holidays/ie.rb,
lib/holidays/za.rb,
lib/holidays/ro.rb,
lib/holidays/ve.rb,
lib/holidays/sk.rb,
lib/holidays/nl.rb,
lib/holidays/el.rb,
lib/holidays/no.rb,
lib/holidays/es.rb,
lib/holidays/au.rb,
lib/holidays/mx.rb,
lib/holidays/dk.rb,
lib/holidays/ch.rb,
lib/holidays/us.rb,
lib/holidays/ups.rb,
lib/holidays/nerc.rb,
lib/holidays/nyse.rb,
lib/holidays/europe.rb,
lib/holidays/version.rb,
lib/holidays/ecb_target.rb,
lib/holidays/scandinavia.rb,
lib/holidays/north_america.rb,
lib/holidays/united_nations.rb,
lib/holidays/federal_reserve.rb

Overview

Region options

Holidays can be defined as belonging to one or more regions and sub regions. The Holidays#on, Holidays#between, Date#holidays and Date#holiday? methods each allow you to specify a specific region.

There are several different ways that you can specify a region:

:region

By region. For example, return holidays in the Canada with :ca.

:region_

By region and sub regions. For example, return holidays in Germany and all its sub regions with :de_.

:region_sub

By sub region. Return national holidays in Spain plus holidays in Spain's Valencia region with :es_v.

:any

Any region. Return holidays from any loaded region.

You can load all the available holiday definition sets by running

Holidays.load_all

Other options

:observed

Return holidays on the day they are observed (e.g. on a Monday if they fall on a Sunday).

:informal

Include informal holidays (e.g. Valentine's Day)

Examples

Return all holidays in the :ca and :us regions on the day that they are observed.

Holidays.between(from, to, :ca, :us, :observed)

Return all holidays in :ca and any :ca sub-region.

Holidays.between(from, to, :ca_)

Return all holidays in :ca_bc sub-region (which includes the :ca), including informal holidays.

Holidays.between(from, to, :ca_bc, :informal)

Defined Under Namespace

Modules: AR, AT, AU, BE, BR, CA, CH, CZ, DE, DK, ECB_TARGET, EL, ES, Europe, FI, FR, Federal_Reserve, GB, HR, HU, IE, IS, IT, JP, LI, MA, MX, NERC, NL, NO, NYSE, NZ, North_America, PL, PT, RO, SE, SI, SK, Scandinavia, UPS, US, United_Nations, VE, ZA Classes: UnknownRegionError

Constant Summary

WEEKS =
{:first => 1, :second => 2, :third => 3, :fourth => 4, :fifth => 5, :last => -1, :second_last => -2, :third_last => -3}
MONTH_LENGTHS =
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
DAY_SYMBOLS =
Date::DAYNAMES.collect { |n| n.downcase.intern }
DEFINITION_PATH =
File.expand_path(File.dirname(__FILE__) + '/holidays/')
VERSION =
"1.0.7.pre"
@@regions =
[]
@@holidays_by_month =
{}
@@proc_cache =
{}

Class Method Summary (collapse)

Class Method Details

+ (Object) available(full_path = false)

Returns an array of symbols all the available holiday definitions.

Optional `full_path` param is used internally for loading all the definitions.



299
300
301
302
# File 'lib/holidays.rb', line 299

def self.available(full_path = false)
  paths = Dir.glob(DEFINITION_PATH + '/*.rb')
  full_path ? paths : paths.collect { |path| path.match(/([a-z_-]+)\.rb/i)[1].to_sym }
end

+ (Object) between(start_date, end_date, *options)

Get all holidays occuring between two dates, inclusively.

Returns an array of hashes or nil.

Each holiday is returned as a hash with the following fields:

start_date

Ruby Date object.

end_date

Ruby Date object.

options

One or more region symbols, :informal and/or :observed.

Example

from = Date.civil(2008,7,1)
to   = Date.civil(2008,7,31)

Holidays.between(from, to, :ca, :us)
=> [{:name => 'Canada Day', :regions => [:ca]...}
    {:name => 'Independence Day'', :regions => [:us], ...}]


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
# File 'lib/holidays.rb', line 104

def self.between(start_date, end_date, *options)
  # remove the timezone
  start_date = start_date.new_offset(0) + start_date.offset if start_date.respond_to?(:new_offset)
  end_date = end_date.new_offset(0) + end_date.offset if end_date.respond_to?(:new_offset)

  # get simple dates
  if start_date.respond_to?(:to_date)
    start_date = start_date.to_date
  else
    start_date = Date.civil(start_date.year, start_date.mon, start_date.mday)
  end

  if end_date.respond_to?(:to_date)
    end_date = end_date.to_date
  else
    end_date = Date.civil(end_date.year, end_date.mon, end_date.mday)
  end

  regions, observed, informal = parse_options(options)
  holidays = []

  dates = {}
  (start_date..end_date).each do |date|
    # Always include month '0' for variable-month holidays
    dates[date.year] = [0] unless dates[date.year]
    # TODO: test this, maybe should push then flatten
    dates[date.year] << date.month unless dates[date.year].include?(date.month)
  end

  dates.each do |year, months|
    months.each do |month|
      next unless hbm = @@holidays_by_month[month]

      hbm.each do |h|
        next unless in_region?(regions, h[:regions])

        # Skip informal holidays unless they have been requested
        next if h[:type] == :informal and not informal

        if h[:function]
          # Holiday definition requires a calculation
          result = call_proc(h[:function], year)

          # Procs may return either Date or an integer representing mday
          if result.kind_of?(Date)
            month = result.month
            mday = result.mday
          else
            mday = result
          end
        else
          # Calculate the mday
          mday = h[:mday] || Date.calculate_mday(year, month, h[:week], h[:wday])
        end

        # Silently skip bad mdays
        begin
          date = Date.civil(year, month, mday)
        rescue; next; end

        # If the :observed option is set, calculate the date when the holiday
        # is observed.
        if observed and h[:observed]
          date = call_proc(h[:observed], date)
        end

        if date.between?(start_date, end_date)
          holidays << {:date => date, :name => h[:name], :regions => h[:regions]}
        end

      end
    end
  end

  holidays.sort{|a, b| a[:date] <=> b[:date] }
end

+ (Object) ca_victoria_day(year)

Monday on or before May 24



63
64
65
66
67
68
69
70
71
# File 'lib/holidays/ca.rb', line 63

def self.ca_victoria_day(year)
  date = Date.civil(year,5,24)
  if date.wday > 1
    date -= (date.wday - 1)
  elsif date.wday == 0
    date -= 6
  end
  date
end

+ (Object) ch_ge_jeune_genevois(year)

Thursday after the first Sunday of September



64
65
66
67
68
69
70
71
72
# File 'lib/holidays/ch.rb', line 64

def self.ch_ge_jeune_genevois(year)
  date = Date.civil(year,9,1)
  # Find the first Sunday of September
  until date.wday.eql? 0 do
    date += 1
  end
  # Thursday is four days after Sunday
  date + 4
end

+ (Object) ch_gl_naefelser_fahrt(year)

First Thursday of April. If the first Thursday of April is in the week before easter, then a week later.



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/holidays/ch.rb', line 76

def self.ch_gl_naefelser_fahrt(year)
  date = Date.civil(year,4,1)
  # Find the first Thursday of April
  until date.wday.eql? 4 do
    date += 1
  end
  if date.eql?(easter(year)-3)
    date += 7
  end
  date
end

+ (Object) ch_vd_lundi_du_jeune_federal(year)

Monday after the third Sunday of September



51
52
53
54
55
56
57
58
59
60
# File 'lib/holidays/ch.rb', line 51

def self.ch_vd_lundi_du_jeune_federal(year)
  date = Date.civil(year,9,1)
  # Find the first Sunday of September
  until date.wday.eql? 0 do
    date += 1
  end
  # There are 15 days between the first Sunday
  # and the Monday after the third Sunday
  date + 15
end

+ (Object) closest_monday(date)



49
50
51
52
53
54
55
56
57
58
# File 'lib/holidays/nz.rb', line 49

def self.closest_monday(date)
  if [1, 2, 3, 4].include?(date.wday)
    date -= (date.wday - 1)
  elsif 0 == date.wday
    date += 1
  else
    date += 8 - date.wday
  end
  date
end

+ (Object) de_buss_und_bettag(year)

Germany: Wednesday before November 23



41
42
43
44
45
46
47
48
49
# File 'lib/holidays/de.rb', line 41

def self.de_buss_und_bettag(year)
  date = Date.civil(year,11,23)
  if date.wday > 3
    date -= (date.wday - 3)
  else
    date -= (date.wday + 4)
  end
  date
end

+ (Object) easter(year)

Get the date of Easter Sunday in a given year. From Easter Sunday, it is possible to calculate many traditional holidays in Western countries. Returns a Date object.



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/holidays.rb', line 215

def self.easter(year)
  y = year
  a = y % 19
  b = y / 100
  c = y % 100
  d = b / 4
  e = b % 4
  f = (b + 8) / 25
  g = (b - f + 1) / 3
  h = (19 * a + b - d - g + 15) % 30
  i = c / 4
  k = c % 4
  l = (32 + 2 * e + 2 * i - h - k) % 7
  m = (a + 11 * h + 22 * l) / 451
  month = (h + l - 7 * m + 114) / 31
  day = ((h + l - 7 * m + 114) % 31) + 1
  Date.civil(year, month, day)
end

+ (Object) fi_juhannusaatto(year)

Finland: Mid-summer eve (Friday between June 19–25)



41
42
43
44
45
46
47
48
49
# File 'lib/holidays/fi.rb', line 41

def self.fi_juhannusaatto(year)
  date = Date.civil(year,6,19)
  if date.wday > 5 #if 19.6 is saturday
    date += 6
  else 
    date += (5 - date.wday)
  end
  date
end

+ (Object) fi_juhannuspaiva(year)

Finland: Mid-summer (Saturday between June 20–26)



53
54
55
56
57
# File 'lib/holidays/fi.rb', line 53

def self.fi_juhannuspaiva(year)
  date = Date.civil(year,6,20)
  date += (6 - date.wday)
  date
end

+ (Object) fi_pyhainpaiva(year)

Finland: All Saint's Day (Saturday between Oct 31 and Nov 6)



61
62
63
64
65
# File 'lib/holidays/fi.rb', line 61

def self.fi_pyhainpaiva(year)
  date = Date.civil(year,10,31)
  date += (6 - date.wday)
  date
end

+ (Boolean) full_week?(date, *options)

Does the given work-week have any holidays?

date

A Date object.

:options

One or more region symbols, and/or :informal. Automatically includes :observed. If you don't want this, pass :no_observed

The given Date can be any day of the week. Returns true if any holidays fall on Monday - Friday of the given week.

Returns:

  • (Boolean)


78
79
80
81
82
83
84
85
86
# File 'lib/holidays.rb', line 78

def self.full_week?(date, *options)
  days_to_monday = date.wday - 1
  days_to_friday = 5 - date.wday
  start_date = date - days_to_monday
  end_date = date + days_to_friday
  options += [:observed] unless options.include?(:no_observed)
  options.delete(:no_observed)
  self.between(start_date, end_date, options).empty?
end

+ (Object) ie_st_stephens_day(date)

Ireland - Stephens Day is always the day after christmas day



35
36
37
38
39
40
# File 'lib/holidays/ie.rb', line 35

def self.ie_st_stephens_day(date)
  date += 2 if date.wday == 6
  date += 2 if date.wday == 0
  date += 1 if date.wday == 1
  date
end

+ (Object) is_sumardagurinn_fyrsti(year)

Iceland: first day of summer (Thursday after 18 April)



52
53
54
55
56
57
58
59
60
# File 'lib/holidays/is.rb', line 52

def self.is_sumardagurinn_fyrsti(year)
  date = Date.civil(year,4,18)
  if date.wday < 4
    date += (4 - date.wday)
  else date
    date += (11 - date.wday)
  end
  date
end

+ (Object) jp_citizons_holiday(year)



93
94
95
96
97
98
99
100
101
# File 'lib/holidays/jp.rb', line 93

def self.jp_citizons_holiday(year)
  year < 2003 and return nil
  ncd = Holidays.jp_national_culture_day(year)
  if ncd.wday == 3
    ncd - 1
  else
    nil
  end
end

+ (Object) jp_national_culture_day(year)



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/holidays/jp.rb', line 73

def self.jp_national_culture_day(year)
  day =
    case year
    when 1851..1899
      22.2588
    when 1900..1979
      23.2588
    when 1980..2099
      23.2488
    when 2100..2150
      24.2488
    else
      raise IndexError.new("Out of range")
    end
  day += 0.242194 * (year - 1980) - ((year - 1980)/4).floor
  day = day.floor
  Date.civil(year, 9, day)
end

+ (Object) jp_substitute_holiday(*date)



104
105
106
107
# File 'lib/holidays/jp.rb', line 104

def self.jp_substitute_holiday(*date)
  date = date[0].kind_of?(Date) ? date.first : Date.civil(*date)
  date.wday == 0 ? date+1 : nil
end

+ (Object) jp_vernal_equinox_day(year)



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/holidays/jp.rb', line 53

def self.jp_vernal_equinox_day(year)
  day =
    case year
    when 1851..1899
      19.8277
    when 1900..1979
      20.8357
    when 1980..2099
      20.8431
    when 2100..2150
      21.8510
    else
      raise IndexError.new("Out of range")
    end
  day += 0.242194 * (year - 1980) - ((year - 1980)/4).floor
  day = day.floor
  Date.civil(year, 3, day)
end

+ (Object) load_all

Load all available holiday definitions



310
311
312
# File 'lib/holidays.rb', line 310

def self.load_all
  self.available(true).each { |path| require path }
end

+ (Object) merge_defs(regions, holidays)

Merge a new set of definitions into the Holidays module.

This method is automatically called when including holiday definition files.



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
# File 'lib/holidays.rb', line 185

def self.merge_defs(regions, holidays) # :nodoc:
  @@regions = @@regions | regions
  @@regions.uniq!

  holidays.each do |month, holiday_defs|
    @@holidays_by_month[month] = [] unless @@holidays_by_month[month]
    holiday_defs.each do |holiday_def|

        exists = false
        @@holidays_by_month[month].each do |ex|
          # TODO: gross.
          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]
            # append regions
            ex[:regions] << holiday_def[:regions]

            # Should do this once we're done
            ex[:regions].flatten!
            ex[:regions].uniq!
            exists = true
          end
        end

        @@holidays_by_month[month] << holiday_def  unless exists
    end
  end
end

+ (Object) next_week(date)



66
67
68
# File 'lib/holidays/nz.rb', line 66

def self.next_week(date)
  date + 7
end

+ (Object) on(date, *options)

Get all holidays on a given date.

date

A Date object.

:options

One or more region symbols, :informal and/or :observed.

Returns an array of hashes or nil. See Holidays#between for the output format.

Also available via Date#holidays.



67
68
69
# File 'lib/holidays.rb', line 67

def self.on(date, *options)
  self.between(date, date, options)
end

+ (Object) orthodox_easter(year)

A method to calculate the orthodox easter date, returns date in the Gregorian (western) calendar Safe until appr. 4100 AD, when one leap day will be removed. Returns a Date object.



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/holidays.rb', line 237

def self.orthodox_easter(year)
  y = year
  g = y % 19
  i = (19 * g + 15) % 30
  j = (year + year/4 + i) % 7
  j_month = 3 + (i - j + 40) / 44
  j_day = i - j + 28 - 31 * (j_month / 4)
  j_date = Date.civil(year, j_month, j_day)
  case
    # up until 1582, julian and gregorian easter dates were identical
    when year <= 1582
      offset = 0
    # between the years 1583 and 1699 10 days are added to the julian day count
    when (year >= 1583 and year <= 1699)
      offset = 10
    # after 1700, 1 day is added for each century, except if the century year is exactly divisible by 400 (in which case no days are added).
    # Safe until 4100 AD, when one leap day will be removed.
    when year >= 1700
      offset = (year - 1700).divmod(100)[0] + ((year - year.divmod(100)[1]).divmod(400)[1] == 0 ? 0 : 1) - (year - year.divmod(100)[1] - 1700).divmod(400)[0] + 10
  end
  # add offset to the julian day
  return Date.jd(j_date.jd + offset)
end

+ (Object) pl_trzech_kroli(year)

Poland: January 6 is holiday since 2011



66
67
68
# File 'lib/holidays/pl.rb', line 66

def self.pl_trzech_kroli(year)
  year >= 2011 ? 6 : nil
end

+ (Object) pl_trzech_kroli_informal(year)

Poland: January 6 wasn't holiday before 2011



72
73
74
# File 'lib/holidays/pl.rb', line 72

def self.pl_trzech_kroli_informal(year)
  year < 2011 ? 6 : nil
end

+ (Object) previous_friday(date)



61
62
63
# File 'lib/holidays/nz.rb', line 61

def self.previous_friday(date)
  date - 3
end

+ (Object) qld_labour_day_may(year)



56
57
58
# File 'lib/holidays/au.rb', line 56

def self.qld_labour_day_may(year)
  year <= 2012 ? Date.calculate_mday(year, 5, 1, 1) : nil
end

+ (Object) qld_labour_day_october(year)



63
64
65
# File 'lib/holidays/au.rb', line 63

def self.qld_labour_day_october(year)
  year <= 2012 ? nil : Date.calculate_mday(year, 10, 1, 1)
end

+ (Object) qld_queens_bday_october(year)



49
50
51
# File 'lib/holidays/au.rb', line 49

def self.qld_queens_bday_october(year)
  year == 2012 ? 1 : nil
end

+ (Object) regions

Returns an array of symbols of all the available holiday regions.



305
306
307
# File 'lib/holidays.rb', line 305

def self.regions
  @@regions
end

+ (Object) se_alla_helgons_dag(year)

Sweden: All Saint's Day (Saturday between Oct 31 and Nov 6)



47
48
49
50
51
# File 'lib/holidays/se.rb', line 47

def self.se_alla_helgons_dag(year)
  date = Date.civil(year,10,31)
  date += (6 - date.wday)
  date
end

+ (Object) se_midsommardagen(year)

Sweden: Mid-summer (Saturday between June 20–26)



39
40
41
42
43
# File 'lib/holidays/se.rb', line 39

def self.se_midsommardagen(year)
  date = Date.civil(year,6,20)
  date += (6 - date.wday)
  date
end

+ (Object) to_monday_if_sunday(date)

Move date to Monday if it occurs on a Sunday. Used as a callback function.



263
264
265
266
# File 'lib/holidays.rb', line 263

def self.to_monday_if_sunday(date)
  date += 1 if date.wday == 0
  date
end

+ (Object) to_monday_if_weekend(date)

Move date to Monday if it occurs on a Saturday on Sunday. Used as a callback function.



270
271
272
273
274
# File 'lib/holidays.rb', line 270

def self.to_monday_if_weekend(date)
  date += 1 if date.wday == 0
  date += 2 if date.wday == 6
  date
end

+ (Object) to_weekday_if_boxing_weekend(date)

Move Boxing Day if it falls on a weekend, leaving room for Christmas. Used as a callback function.



278
279
280
281
282
283
284
285
# File 'lib/holidays.rb', line 278

def self.to_weekday_if_boxing_weekend(date)
  if date.wday == 6 or date.wday == 0
    date += 2
  elsif date.wday == 1
    date += 1
  end
  date
end

+ (Object) to_weekday_if_weekend(date)

Move date to Monday if it occurs on a Sunday or to Friday if it occurs on a Saturday. Used as a callback function.



290
291
292
293
294
# File 'lib/holidays.rb', line 290

def self.to_weekday_if_weekend(date)
  date += 1 if date.wday == 0
  date -= 1 if date.wday == 6
  date
end

+ (Object) us_inauguration_day(year)

January 20, every fourth year, following Presidential election



48
49
50
# File 'lib/holidays/us.rb', line 48

def self.us_inauguration_day(year)
  year % 4 == 1 ? 20 : nil
end