module HolidayCalendar
Constants
- BASE_URL
- CHECK_INTERVAL_SEC
- COUNTRY_ALIASES
- DEFAULT_COUNTRY
- DEFAULT_TIMEZONE
- STYLE_TITLE
- UPCOMING_LIMIT
- USER_AGENT
Public Instance Methods
Source
# File lib/holiday_calendar.rb, line 189 def announcement_key(holiday, when_label:) "#{effective_date(holiday)}|#{when_label.to_s.downcase}|#{holiday[:id]}" end
Source
# File lib/holiday_calendar.rb, line 155 def country_label(country) normalize_country(country) || DEFAULT_COUNTRY end
Source
# File lib/holiday_calendar.rb, line 193 def due_for_check?(state, now: Time.now, interval_sec: CHECK_INTERVAL_SEC) last = RabbotDb.parse_time(state["last_check_at"]) return true unless last (now - last) >= interval_sec end
Source
# File lib/holiday_calendar.rb, line 83 def effective_date(holiday) holiday[:observed_date].to_s.strip.empty? ? holiday[:date] : holiday[:observed_date] end
Source
# File lib/holiday_calendar.rb, line 87 def fetch_holidays(country:, year:, fetch: nil) url = holidays_url(year, country) body = if fetch fetch.call(url) else RabbotHttp.fetch(url, user_agent: USER_AGENT) end parse_holidays(JSON.parse(body)) end
Source
# File lib/holiday_calendar.rb, line 97 def fetch_holidays_safe(country:, year:, fetch: nil) fetch_holidays(country: country, year: year, fetch: fetch) rescue StandardError [] end
Source
# File lib/holiday_calendar.rb, line 176 def format_announcement(holiday, when_label:) date = effective_date(holiday) name = holiday_label(holiday) title = "#{when_label} · #{date}" body = name.empty? ? "Public holiday." : name [ IrcFormat.report_header(tag: "HOLIDAYS", title: title, style: STYLE_TITLE), body, IrcFormat.report_footer("public holiday reminder") ].join("\n") end
Source
# File lib/holiday_calendar.rb, line 260 def format_error(country:) code = country_label(country) [ IrcFormat.report_header(tag: "HOLIDAYS", title: "#{code} · error", style: STYLE_TITLE), "Could not fetch public holidays.", IrcFormat.report_footer("public holidays") ].join("\n") end
Source
# File lib/holiday_calendar.rb, line 149 def format_holiday_line(holiday) date = effective_date(holiday) name = holiday_label(holiday) name.empty? ? date.to_s : "#{date} — #{name}" end
Source
# File lib/holiday_calendar.rb, line 159 def format_list(holidays, country:, title: nil) code = country_label(country) heading = title.to_s.strip heading = "#{code} · upcoming" if heading.empty? body = if holidays.empty? "No upcoming public holidays found." else holidays.map { |holiday| format_holiday_line(holiday) }.join("\n") end [ IrcFormat.report_header(tag: "HOLIDAYS", title: heading, style: STYLE_TITLE), body, IrcFormat.report_footer("public holidays", code) ].join("\n") end
Source
# File lib/holiday_calendar.rb, line 141 def holiday_label(holiday) local = holiday[:local_name].to_s.strip english = holiday[:name].to_s.strip return local if english.empty? || local.casecmp?(english) "#{local} (#{english})" end
Source
# File lib/holiday_calendar.rb, line 103 def holidays_for_years(country:, years:, fetch:, safe: false) fetcher = safe ? method(:fetch_holidays_safe) : method(:fetch_holidays) years.flat_map { |year| fetcher.call(country: country, year: year, fetch: fetch) } .uniq { |holiday| [effective_date(holiday), holiday[:id]] } .sort_by { |holiday| effective_date(holiday) } end
Source
# File lib/holiday_calendar.rb, line 110 def holidays_on(holidays, date) target = date.is_a?(Date) ? date : Date.parse(date.to_s) holidays.select do |holiday| Date.parse(effective_date(holiday)) == target end rescue ArgumentError [] end
Source
# File lib/holiday_calendar.rb, line 59 def holidays_url(year, country) "#{BASE_URL}/PublicHolidays/#{year}/#{country}" end
Source
# File lib/holiday_calendar.rb, line 53 def local_date(now: Time.now, timezone: DEFAULT_TIMEZONE) TZInfo::Timezone.get(timezone).utc_to_local(now.utc).to_date rescue TZInfo::InvalidTimezoneIdentifier now.utc.to_date end
Source
# File lib/holiday_calendar.rb, line 236 def lookup_report(country:, fetch:, today: local_date, year: nil) country = normalize_country(country) || DEFAULT_COUNTRY years = if year [year.to_i] else [today.year, today.year + 1].uniq end holidays = holidays_for_years(country: country, years: years, fetch: fetch) selected = if year holidays.select { |holiday| Date.parse(effective_date(holiday)).year == year.to_i } else upcoming_holidays(holidays, from: today) end title = if year "#{country} · #{year}" else "#{country} · upcoming" end format_list(selected, country: country, title: title) rescue StandardError format_error(country: country) end
Source
# File lib/holiday_calendar.rb, line 39 def normalize_country(value) token = value.to_s.strip return DEFAULT_COUNTRY if token.empty? downcased = token.downcase if (mapped = COUNTRY_ALIASES[downcased]) return mapped end return token.upcase if token.match?(/\A[A-Za-z]{2}\z/) nil end
Source
# File lib/holiday_calendar.rb, line 63 def parse_holidays(json) Array(json).filter_map do |entry| next unless entry.is_a?(Hash) date = entry["date"].to_s next if date.empty? observed = entry["observedDate"].to_s.strip observed = date if observed.empty? { date: date, observed_date: observed, local_name: entry["localName"].to_s.strip, name: entry["name"].to_s.strip, country_code: entry["countryCode"].to_s.strip, id: entry["id"].to_s.strip } end end
Source
# File lib/holiday_calendar.rb, line 200 def poll_channel(state, country:, fetch:, now: Time.now, today: local_date(now: now)) country = normalize_country(country) || DEFAULT_COUNTRY years = [today.year, (today + 1).year].uniq holidays = holidays_for_years(country: country, years: years, fetch: fetch, safe: true) split = today_and_tomorrow_holidays(holidays, today: today) announcements = [] updated = state { today: "Today", tomorrow: "Tomorrow" }.each do |bucket, label| split[bucket].each do |holiday| key = announcement_key(holiday, when_label: label) next if HolidayStore.announced?(updated, key) announcements << { text: format_announcement(holiday, when_label: label), key: key } updated = HolidayStore.mark_announced(updated, key) end end updated = updated.merge( "country" => country, "last_check_at" => RabbotDb.iso_time(now) ) { state: updated, announcements: announcements } end
Source
# File lib/holiday_calendar.rb, line 129 def today_and_tomorrow_holidays(holidays, today: Date.today) today_date = today.is_a?(Date) ? today : Date.parse(today.to_s) tomorrow_date = today_date + 1 { today: holidays_on(holidays, today_date), tomorrow: holidays_on(holidays, tomorrow_date) } rescue ArgumentError { today: [], tomorrow: [] } end
Source
# File lib/holiday_calendar.rb, line 119 def upcoming_holidays(holidays, from: Date.today, limit: UPCOMING_LIMIT) from_date = from.is_a?(Date) ? from : Date.parse(from.to_s) holidays .select { |holiday| Date.parse(effective_date(holiday)) >= from_date } .sort_by { |holiday| effective_date(holiday) } .first(limit) rescue ArgumentError [] end