Module: Philiprehberger::DateKit
- Defined in:
- lib/philiprehberger/date_kit.rb,
lib/philiprehberger/date_kit/version.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- VERSION =
'0.4.0'
Class Method Summary collapse
-
.add_business_days(date, days, holidays: []) ⇒ Date
Add business days to a date, skipping weekends and holidays.
-
.beginning_of_quarter(date) ⇒ Date
Return the first day of the quarter containing the given date.
-
.business_day?(date, holidays: []) ⇒ Boolean
Check if a date is a business day (not a weekend and not a holiday).
-
.business_days_between(start_date, finish_date) ⇒ Integer
Count business days (Mon-Fri) between two dates, exclusive of both endpoints.
-
.business_days_in_month(date, holidays: []) ⇒ Array<Date>
Return all business days in the month containing the given date.
-
.business_days_in_range(start_date, finish_date, holidays: []) ⇒ Array<Date>
Return an array of business days in a date range (inclusive).
-
.each_business_day(start_date, finish_date, holidays: []) {|Date| ... } ⇒ Enumerator
Iterate over business days in a date range.
-
.end_of_quarter(date) ⇒ Date
Return the last day of the quarter containing the given date.
-
.first_business_day_of_month(date, holidays: []) ⇒ Date
Return the first business day of the month containing the given date.
-
.last_business_day_of_month(date, holidays: []) ⇒ Date
Return the last business day of the month containing the given date.
-
.next_business_day(date, holidays: []) ⇒ Date
Return the next business day after the given date (skips weekends and holidays).
-
.nth_business_day_of_month(date, n, holidays: []) ⇒ Date
Return the nth business day of the month containing the given date.
-
.parse_relative(str, relative_to: Date.today) ⇒ Date
Parse a relative date expression into a Date.
-
.prev_business_day(date, holidays: []) ⇒ Date
Return the previous business day before the given date (skips weekends and holidays).
-
.quarter(date) ⇒ Integer
Return the quarter number (1-4) for the given date.
-
.weekend?(date) ⇒ Boolean
Check if a date falls on a weekend (Saturday or Sunday).
Class Method Details
.add_business_days(date, days, holidays: []) ⇒ Date
Add business days to a date, skipping weekends and holidays
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/philiprehberger/date_kit.rb', line 36 def self.add_business_days(date, days, holidays: []) date = coerce_date(date) holidays = holidays.map { |h| coerce_date(h) } raise Error, 'days must be an integer' unless days.is_a?(Integer) direction = days.positive? ? 1 : -1 remaining = days.abs current = date while remaining.positive? current += direction next if weekend?(current) || holidays.include?(current) remaining -= 1 end current end |
.beginning_of_quarter(date) ⇒ Date
Return the first day of the quarter containing the given date
60 61 62 63 64 |
# File 'lib/philiprehberger/date_kit.rb', line 60 def self.beginning_of_quarter(date) date = coerce_date(date) quarter_month = (((date.month - 1) / 3) * 3) + 1 Date.new(date.year, quarter_month, 1) end |
.business_day?(date, holidays: []) ⇒ Boolean
Check if a date is a business day (not a weekend and not a holiday)
207 208 209 210 211 |
# File 'lib/philiprehberger/date_kit.rb', line 207 def self.business_day?(date, holidays: []) date = coerce_date(date) holidays = holidays.map { |h| coerce_date(h) } !weekend?(date) && !holidays.include?(date) end |
.business_days_between(start_date, finish_date) ⇒ Integer
Count business days (Mon-Fri) between two dates, exclusive of both endpoints
15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/philiprehberger/date_kit.rb', line 15 def self.business_days_between(start_date, finish_date) start_date = coerce_date(start_date) finish_date = coerce_date(finish_date) return 0 if start_date >= finish_date count = 0 current = start_date + 1 while current < finish_date count += 1 unless weekend?(current) current += 1 end count end |
.business_days_in_month(date, holidays: []) ⇒ Array<Date>
Return all business days in the month containing the given date
260 261 262 263 264 265 |
# File 'lib/philiprehberger/date_kit.rb', line 260 def self.business_days_in_month(date, holidays: []) date = coerce_date(date) first = Date.new(date.year, date.month, 1) last = Date.new(date.year, date.month, -1) business_days_in_range(first, last, holidays: holidays) end |
.business_days_in_range(start_date, finish_date, holidays: []) ⇒ Array<Date>
Return an array of business days in a date range (inclusive)
169 170 171 172 173 174 175 176 177 |
# File 'lib/philiprehberger/date_kit.rb', line 169 def self.business_days_in_range(start_date, finish_date, holidays: []) start_date = coerce_date(start_date) finish_date = coerce_date(finish_date) holidays = holidays.map { |h| coerce_date(h) } return [] if start_date > finish_date (start_date..finish_date).reject { |d| weekend?(d) || holidays.include?(d) } end |
.each_business_day(start_date, finish_date, holidays: []) {|Date| ... } ⇒ Enumerator
Iterate over business days in a date range
186 187 188 189 190 191 |
# File 'lib/philiprehberger/date_kit.rb', line 186 def self.each_business_day(start_date, finish_date, holidays: [], &block) days = business_days_in_range(start_date, finish_date, holidays: holidays) return days.each unless block days.each(&block) end |
.end_of_quarter(date) ⇒ Date
Return the last day of the quarter containing the given date
70 71 72 73 74 |
# File 'lib/philiprehberger/date_kit.rb', line 70 def self.end_of_quarter(date) date = coerce_date(date) quarter_month = (((date.month - 1) / 3) * 3) + 3 Date.new(date.year, quarter_month, -1) end |
.first_business_day_of_month(date, holidays: []) ⇒ Date
Return the first business day of the month containing the given date
231 232 233 234 235 236 237 |
# File 'lib/philiprehberger/date_kit.rb', line 231 def self.first_business_day_of_month(date, holidays: []) date = coerce_date(date) holidays = holidays.map { |h| coerce_date(h) } current = Date.new(date.year, date.month, 1) current += 1 while weekend?(current) || holidays.include?(current) current end |
.last_business_day_of_month(date, holidays: []) ⇒ Date
Return the last business day of the month containing the given date
218 219 220 221 222 223 224 |
# File 'lib/philiprehberger/date_kit.rb', line 218 def self.last_business_day_of_month(date, holidays: []) date = coerce_date(date) holidays = holidays.map { |h| coerce_date(h) } current = Date.new(date.year, date.month, -1) current -= 1 while weekend?(current) || holidays.include?(current) current end |
.next_business_day(date, holidays: []) ⇒ Date
Return the next business day after the given date (skips weekends and holidays)
140 141 142 143 144 145 146 147 |
# File 'lib/philiprehberger/date_kit.rb', line 140 def self.next_business_day(date, holidays: []) date = coerce_date(date) holidays = holidays.map { |h| coerce_date(h) } current = date + 1 current += 1 while weekend?(current) || holidays.include?(current) current end |
.nth_business_day_of_month(date, n, holidays: []) ⇒ Date
Return the nth business day of the month containing the given date
246 247 248 249 250 251 252 253 |
# File 'lib/philiprehberger/date_kit.rb', line 246 def self.nth_business_day_of_month(date, n, holidays: []) raise Error, 'n must be a positive integer' unless n.is_a?(Integer) && n.positive? days = business_days_in_month(date, holidays: holidays) raise Error, "month has only #{days.size} business days" if n > days.size days[n - 1] end |
.parse_relative(str, relative_to: Date.today) ⇒ Date
Parse a relative date expression into a Date
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 |
# File 'lib/philiprehberger/date_kit.rb', line 91 def self.parse_relative(str, relative_to: Date.today) relative_to = coerce_date(relative_to) normalized = str.to_s.strip.downcase case normalized when 'today' relative_to when 'yesterday' relative_to - 1 when 'tomorrow' relative_to + 1 when /\A(\d+)\s+days?\s+ago\z/ relative_to - ::Regexp.last_match(1).to_i when /\Ain\s+(\d+)\s+days?\z/ relative_to + ::Regexp.last_match(1).to_i when /\A(\d+)\s+weeks?\s+ago\z/ relative_to - (::Regexp.last_match(1).to_i * 7) when /\Ain\s+(\d+)\s+weeks?\z/ relative_to + (::Regexp.last_match(1).to_i * 7) when /\A(\d+)\s+months?\s+ago\z/ months_ago(relative_to, ::Regexp.last_match(1).to_i) when /\Ain\s+(\d+)\s+months?\z/ months_ahead(relative_to, ::Regexp.last_match(1).to_i) when /\A(\d+)\s+years?\s+ago\z/ years_ago(relative_to, ::Regexp.last_match(1).to_i) when /\Ain\s+(\d+)\s+years?\z/ years_ahead(relative_to, ::Regexp.last_match(1).to_i) when 'last week' relative_to - 7 when 'next week' relative_to + 7 when 'last month' months_ago(relative_to, 1) when 'next month' months_ahead(relative_to, 1) when 'last year' years_ago(relative_to, 1) when 'next year' years_ahead(relative_to, 1) else raise Error, "cannot parse relative date: #{str}" end end |
.prev_business_day(date, holidays: []) ⇒ Date
Return the previous business day before the given date (skips weekends and holidays)
154 155 156 157 158 159 160 161 |
# File 'lib/philiprehberger/date_kit.rb', line 154 def self.prev_business_day(date, holidays: []) date = coerce_date(date) holidays = holidays.map { |h| coerce_date(h) } current = date - 1 current -= 1 while weekend?(current) || holidays.include?(current) current end |
.quarter(date) ⇒ Integer
Return the quarter number (1-4) for the given date
197 198 199 200 |
# File 'lib/philiprehberger/date_kit.rb', line 197 def self.quarter(date) date = coerce_date(date) ((date.month - 1) / 3) + 1 end |
.weekend?(date) ⇒ Boolean
Check if a date falls on a weekend (Saturday or Sunday)
80 81 82 83 |
# File 'lib/philiprehberger/date_kit.rb', line 80 def self.weekend?(date) date = coerce_date(date) date.saturday? || date.sunday? end |