Skip to content

Instantly share code, notes, and snippets.

@geoffgarside
Created September 22, 2008 23:33
Show Gist options
  • Save geoffgarside/12175 to your computer and use it in GitHub Desktop.
Save geoffgarside/12175 to your computer and use it in GitHub Desktop.
require 'rubygems'
require 'chronic'
require 'activesupport'
class BankHoliday
def self.include?(date)
@@holidays.include?(date.utc.beginning_of_day)
end
def self.next_from(date)
date = date.utc.beginning_of_day
@@holidays.each do |day|
return day if day >= date
end
end
def self.next_workday_from(date)
return date.utc unless include?(date)
next_workday_from(date.tomorrow)
end
private
def self.load_dates!
@@holidays = [
Chronic.parse('1st day in January'), # New Years Day (1st of January)
easter - 2.days, # Good Friday (Easter Sunday - 2)
easter + 1.days, # Easter Monday (Easter Sunday + 1)
Chronic.parse('1st Monday in May'), # Easter Bank Holiday (First Monday in May)
Chronic.parse('1st Monday in June') - 7.days, # Spring Bank Holiday (Last Monday in May)
Chronic.parse('1st Monday in September') - 7.days, # Summer Bank Holiday (Last Monday in August)
Chronic.parse('25th day in December'), # Christmas Day (25th of December)
Chronic.parse('26th day in December') # Boxing Day (26th of December)
].map { |day| day.utc.beginning_of_day }.sort
end
def self.easter(y = Time.now.year)
c = y / 100
n = y - 19 * ( y / 19 )
k = ( c - 17 ) / 25
i = c - c / 4 - ( c - k ) / 3 + 19 * n + 15
i = i - 30 * ( i / 30 )
i = i - ( i / 28 ) * ( 1 - ( i / 28 ) * ( 29 / ( i + 1 ) ) * ( ( 21 - n ) / 11 ) )
j = y + y / 4 + i + 2 - c + c / 4
j = j - 7 * ( j / 7 )
l = i - j
m = 3 + ( l + 40 ) / 44
d = l + 28 - 31 * ( m / 4 )
easter_sunday = Time.gm(y, m, d)
return easter_sunday < Time.now.utc ? easter(y + 1) : easter_sunday
end
# Here we load up the holiday dates
load_dates!
end
require 'test/unit'
require 'bank_holiday'
class BankHolidayTests < Test::Unit::TestCase
def setup
BankHoliday.cattr_reader(:holidays)
end
def test_includes_bank_holidays
BankHoliday.holidays.each do |holiday|
assert BankHoliday.include?(holiday)
end
end
def test_next_from_with_holiday_dates
BankHoliday.holidays.each do |holiday|
assert_equal BankHoliday.next_from(holiday), holiday
end
end
# Here we test all but the last one, the last one will technically
# generate a failure as only one years worth (from Time.now) of
# bank holidays are generated.
def test_next_from_with_holiday_dates_plus_one_day
BankHoliday.holidays[0...-1].each_with_index do |holiday, index|
assert_equal BankHoliday.next_from(holiday + 1.day), BankHoliday.holidays[index + 1]
end
end
def test_next_workday_from_with_holiday_dates
BankHoliday.holidays.each do |holiday|
assert BankHoliday.next_workday_from(holiday) > holiday
assert ! BankHoliday.include?(BankHoliday.next_workday_from(holiday))
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment