Module: Holidays

Included in:
Date
Defined in:
lib/holidays.rb,
lib/holidays/au.rb,
lib/holidays/ca.rb,
lib/holidays/cz.rb,
lib/holidays/de.rb,
lib/holidays/dk.rb,
lib/holidays/es.rb,
lib/holidays/fr.rb,
lib/holidays/gb.rb,
lib/holidays/ie.rb,
lib/holidays/is.rb,
lib/holidays/it.rb,
lib/holidays/mx.rb,
lib/holidays/nl.rb,
lib/holidays/no.rb,
lib/holidays/nz.rb,
lib/holidays/pt.rb,
lib/holidays/se.rb,
lib/holidays/us.rb,
lib/holidays/za.rb,
lib/holidays/ups.rb,
lib/holidays/nyse.rb,
lib/holidays/europe.rb,
lib/holidays/scandinavia.rb,
lib/holidays/north_america.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.

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: AU, CA, CZ, DE, DK, ES, Europe, FR, GB, IE, IS, IT, MX, NL, NO, NYSE, NZ, North_America, PT, SCANDINAVIA, SE, UPS, US, ZA Classes: UnknownRegionError

Constant Summary collapse

VERSION =
'0.9.2'
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 }
@@regions =
[]
@@holidays_by_month =
{}
@@proc_cache =
{}

Class Method Summary collapse

Class Method Details

.between(start_date, end_date, *options) ⇒ Object

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], ...}]


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

def self.between(start_date, end_date, *options)
  start_date = start_date.to_date if start_date.respond_to?(:to_date)
  end_date = end_date.to_date if end_date.respond_to?(:to_date)
  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

.ca_victoria_day(year) ⇒ Object

Monday on or before May 24



55
56
57
58
59
60
61
62
63
# File 'lib/holidays/ca.rb', line 55

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

.closest_monday(date) ⇒ Object



45
46
47
48
49
50
51
52
53
54
# File 'lib/holidays/nz.rb', line 45

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

.de_buss_und_bettag(year) ⇒ Object

Germany: Wednesday before November 23



37
38
39
40
41
42
43
44
45
# File 'lib/holidays/de.rb', line 37

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

.easter(year) ⇒ Object

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.



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/holidays.rb', line 181

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

.is_sumardagurinn_fyrsti(year) ⇒ Object

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



48
49
50
51
52
53
54
55
56
# File 'lib/holidays/is.rb', line 48

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

.merge_defs(regions, holidays) ⇒ Object

Merge a new set of definitions into the Holidays module.

This method is automatically called when including holiday definition files.



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

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

.next_week(date) ⇒ Object



57
58
59
# File 'lib/holidays/nz.rb', line 57

def self.next_week(date)
  date + 7
end

.on(date, *options) ⇒ Object

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.



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

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

.previous_friday(date) ⇒ Object



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

def self.previous_friday(date)
  date - 3
end

.se_alla_helgons_dag(year) ⇒ Object

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



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

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

.se_midsommardagen(year) ⇒ Object

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



43
44
45
46
47
# File 'lib/holidays/se.rb', line 43

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

.to_monday_if_sunday(date) ⇒ Object

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



202
203
204
205
# File 'lib/holidays.rb', line 202

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

.to_monday_if_weekend(date) ⇒ Object

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



209
210
211
212
213
# File 'lib/holidays.rb', line 209

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

.to_weekday_if_boxing_weekend(date) ⇒ Object

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



217
218
219
220
# File 'lib/holidays.rb', line 217

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

.to_weekday_if_weekend(date) ⇒ Object

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



225
226
227
228
229
# File 'lib/holidays.rb', line 225

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

.us_inauguration_day(year) ⇒ Object

January 20, every fourth year, following Presidential election



41
42
43
# File 'lib/holidays/us.rb', line 41

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