Module: Codepulse::TimeHelpers
- Included in:
- CLI, MetricsCalculator
- Defined in:
- lib/codepulse/time_helpers.rb
Overview
Time calculation helpers for business days and holidays.
Constant Summary collapse
- SECONDS_PER_DAY =
86_400
Instance Method Summary collapse
- #build_us_holidays(year) ⇒ Object
-
#business_day?(time_value) ⇒ Boolean
Returns true if the time falls on a weekday and is not a US holiday.
-
#business_days_cutoff(business_days) ⇒ Object
Returns the cutoff time for N business days ago.
-
#business_seconds_between(start_time, end_time) ⇒ Object
Calculates seconds between two times, counting only business day hours.
- #date_key(time_value) ⇒ Object
- #end_of_day(time_value) ⇒ Object
-
#last_weekday(year, month, target_wday) ⇒ Object
Finds the last occurrence of a weekday in a month (e.g., last Monday of May).
-
#nth_weekday(year, month, target_wday, occurrence) ⇒ Object
Finds the nth occurrence of a weekday in a month (e.g., 3rd Monday).
-
#parse_time(value) ⇒ Object
Parses a time string, returns nil if invalid.
- #start_of_day(time_value) ⇒ Object
-
#us_holiday?(time_value) ⇒ Boolean
Returns true if the date is a US federal holiday.
- #us_holidays(year) ⇒ Object
-
#weekday?(time_value) ⇒ Boolean
Returns true for Monday through Friday.
Instance Method Details
#build_us_holidays(year) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/codepulse/time_helpers.rb', line 44 def build_us_holidays(year) holidays = [] # New Year's Day (Jan 1) holidays << [year, 1, 1] # MLK Day (3rd Monday in January) holidays << nth_weekday(year, 1, 1, 3) # Presidents Day (3rd Monday in February) holidays << nth_weekday(year, 2, 1, 3) # Memorial Day (last Monday in May) holidays << last_weekday(year, 5, 1) # Juneteenth (June 19) holidays << [year, 6, 19] # Independence Day (July 4) holidays << [year, 7, 4] # Labor Day (1st Monday in September) holidays << nth_weekday(year, 9, 1, 1) # Columbus Day (2nd Monday in October) holidays << nth_weekday(year, 10, 1, 2) # Veterans Day (Nov 11) holidays << [year, 11, 11] # Thanksgiving (4th Thursday in November) holidays << nth_weekday(year, 11, 4, 4) # Christmas Day (Dec 25) holidays << [year, 12, 25] holidays end |
#business_day?(time_value) ⇒ Boolean
Returns true if the time falls on a weekday and is not a US holiday.
18 19 20 21 22 23 |
# File 'lib/codepulse/time_helpers.rb', line 18 def business_day?(time_value) return false unless weekday?(time_value) return false if us_holiday?(time_value) true end |
#business_days_cutoff(business_days) ⇒ Object
Returns the cutoff time for N business days ago.
130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/codepulse/time_helpers.rb', line 130 def business_days_cutoff(business_days) return nil unless business_days current_time = Time.now remaining_days = business_days while remaining_days.positive? current_time -= SECONDS_PER_DAY remaining_days -= 1 if business_day?(current_time) end start_of_day(current_time) end |
#business_seconds_between(start_time, end_time) ⇒ Object
Calculates seconds between two times, counting only business day hours. Skips weekends and US holidays entirely.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/codepulse/time_helpers.rb', line 110 def business_seconds_between(start_time, end_time) return nil unless start_time && end_time return 0 if end_time <= start_time total = 0 current_start = start_time while current_start < end_time day_end = end_of_day(current_start) segment_end = [day_end, end_time].min total += (segment_end - current_start) if business_day?(current_start) current_start = start_of_day(current_start + SECONDS_PER_DAY) end total.to_i end |
#date_key(time_value) ⇒ Object
35 36 37 |
# File 'lib/codepulse/time_helpers.rb', line 35 def date_key(time_value) [time_value.year, time_value.month, time_value.day] end |
#end_of_day(time_value) ⇒ Object
104 105 106 |
# File 'lib/codepulse/time_helpers.rb', line 104 def end_of_day(time_value) Time.new(time_value.year, time_value.month, time_value.day, 23, 59, 59, time_value.utc_offset) end |
#last_weekday(year, month, target_wday) ⇒ Object
Finds the last occurrence of a weekday in a month (e.g., last Monday of May).
93 94 95 96 97 98 |
# File 'lib/codepulse/time_helpers.rb', line 93 def last_weekday(year, month, target_wday) next_month = month == 12 ? Time.new(year + 1, 1, 1) : Time.new(year, month + 1, 1) last_day = next_month - SECONDS_PER_DAY days_back = (last_day.wday - target_wday + 7) % 7 [year, month, last_day.day - days_back] end |
#nth_weekday(year, month, target_wday, occurrence) ⇒ Object
Finds the nth occurrence of a weekday in a month (e.g., 3rd Monday). target_wday: 0=Sun, 1=Mon, ..., 6=Sat
85 86 87 88 89 90 |
# File 'lib/codepulse/time_helpers.rb', line 85 def nth_weekday(year, month, target_wday, occurrence) first_day = Time.new(year, month, 1) days_until = (target_wday - first_day.wday + 7) % 7 day = 1 + days_until + (7 * (occurrence - 1)) [year, month, day] end |
#parse_time(value) ⇒ Object
Parses a time string, returns nil if invalid.
11 12 13 14 15 |
# File 'lib/codepulse/time_helpers.rb', line 11 def parse_time(value) Time.parse(value.to_s) rescue ArgumentError nil end |
#start_of_day(time_value) ⇒ Object
100 101 102 |
# File 'lib/codepulse/time_helpers.rb', line 100 def start_of_day(time_value) Time.new(time_value.year, time_value.month, time_value.day, 0, 0, 0, time_value.utc_offset) end |
#us_holiday?(time_value) ⇒ Boolean
Returns true if the date is a US federal holiday.
31 32 33 |
# File 'lib/codepulse/time_helpers.rb', line 31 def us_holiday?(time_value) us_holidays(time_value.year).include?(date_key(time_value)) end |
#us_holidays(year) ⇒ Object
39 40 41 42 |
# File 'lib/codepulse/time_helpers.rb', line 39 def us_holidays(year) @us_holidays_cache ||= {} @us_holidays_cache[year] ||= build_us_holidays(year) end |
#weekday?(time_value) ⇒ Boolean
Returns true for Monday through Friday.
26 27 28 |
# File 'lib/codepulse/time_helpers.rb', line 26 def weekday?(time_value) time_value.wday.between?(1, 5) end |