Module: CalInvite::IcalTimezone Private
- Defined in:
- lib/cal_invite/ical_timezone.rb
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
Builds RFC 5545 VTIMEZONE components and converts UTC times to local wall-clock time for a given IANA/Olson timezone identifier, using TZInfo (already pulled in transitively via activesupport).
Both entry points fail soft: if tzid isn't a TZInfo-recognized identifier (e.g.
a raw UTC offset string like "+01:00", or 'UTC' itself), they return nil so
callers can fall back to their previous behavior instead of raising.
Class Method Summary collapse
- .fixed_observance_lines(period) ⇒ Object private
- .format_offset(seconds) ⇒ Object private
-
.local_time(tzid, utc_time) ⇒ Time?
private
Converts a UTC time to local wall-clock time for the given timezone.
- .observance_lines(kind, from_period, to_period) ⇒ Object private
- .period_after(tz, period) ⇒ Object private
- .period_before(tz, period) ⇒ Object private
-
.rrule_line(time) ⇒ Object
private
Derives a YEARLY RRULE (e.g. "2nd Sunday in March") from a transition date.
-
.vtimezone_lines(tzid) ⇒ Array<String>?
private
Builds a complete VTIMEZONE component (STANDARD/DAYLIGHT observances with RRULEs derived from the timezone's actual transition rules) for the given timezone identifier.
Class Method Details
.fixed_observance_lines(period) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/cal_invite/ical_timezone.rb', line 102 def fixed_observance_lines(period) [ "BEGIN:STANDARD", "DTSTART:16010101T000000", "TZOFFSETFROM:#{format_offset(period.offset.observed_utc_offset)}", "TZOFFSETTO:#{format_offset(period.offset.observed_utc_offset)}", "TZNAME:#{period.offset.abbreviation}", "END:STANDARD" ] end |
.format_offset(seconds) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
126 127 128 129 130 |
# File 'lib/cal_invite/ical_timezone.rb', line 126 def format_offset(seconds) sign = seconds.negative? ? '-' : '+' abs = seconds.abs format('%<sign>s%<hours>02d%<minutes>02d', sign: sign, hours: abs / 3600, minutes: (abs % 3600) / 60) end |
.local_time(tzid, utc_time) ⇒ Time?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Converts a UTC time to local wall-clock time for the given timezone.
25 26 27 28 29 30 31 |
# File 'lib/cal_invite/ical_timezone.rb', line 25 def local_time(tzid, utc_time) return nil if tzid.nil? || tzid.to_s.strip.empty? || tzid.to_s.upcase == 'UTC' TZInfo::Timezone.get(tzid.to_s).to_local(utc_time.utc) rescue TZInfo::InvalidTimezoneIdentifier nil end |
.observance_lines(kind, from_period, to_period) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/cal_invite/ical_timezone.rb', line 87 def observance_lines(kind, from_period, to_period) transition_time = from_period.local_ends_at.to_time [ "BEGIN:#{kind}", "DTSTART:#{transition_time.strftime('%Y%m%dT%H%M%S')}", "TZOFFSETFROM:#{format_offset(from_period.offset.observed_utc_offset)}", "TZOFFSETTO:#{format_offset(to_period.offset.observed_utc_offset)}", "TZNAME:#{to_period.offset.abbreviation}", rrule_line(transition_time), "END:#{kind}" ] end |
.period_after(tz, period) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
80 81 82 83 84 |
# File 'lib/cal_invite/ical_timezone.rb', line 80 def period_after(tz, period) return nil unless period&.ends_at tz.period_for(period.ends_at.to_time) end |
.period_before(tz, period) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
73 74 75 76 77 |
# File 'lib/cal_invite/ical_timezone.rb', line 73 def period_before(tz, period) return nil unless period.starts_at tz.period_for(period.starts_at.to_time - 1) end |
.rrule_line(time) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Derives a YEARLY RRULE (e.g. "2nd Sunday in March") from a transition date.
116 117 118 119 120 121 122 123 |
# File 'lib/cal_invite/ical_timezone.rb', line 116 def rrule_line(time) wday_names = %w[SU MO TU WE TH FR SA] days_in_month = Date.new(time.year, time.month, -1).day nth = (time.day - 1) / 7 + 1 nth = -1 if time.day + 7 > days_in_month "RRULE:FREQ=YEARLY;BYMONTH=#{time.month};BYDAY=#{nth}#{wday_names[time.wday]}" end |
.vtimezone_lines(tzid) ⇒ Array<String>?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Builds a complete VTIMEZONE component (STANDARD/DAYLIGHT observances with RRULEs derived from the timezone's actual transition rules) for the given timezone identifier.
40 41 42 43 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 |
# File 'lib/cal_invite/ical_timezone.rb', line 40 def vtimezone_lines(tzid) return nil if tzid.nil? || tzid.to_s.strip.empty? || tzid.to_s.upcase == 'UTC' tz = TZInfo::Timezone.get(tzid.to_s) current = tz.period_for(Time.now.utc) if current.dst? dst_period = current std_period = period_before(tz, dst_period) else std_period = current dst_period = period_after(tz, std_period) dst_period = nil unless dst_period&.dst? end lines = ["BEGIN:VTIMEZONE", "TZID:#{tzid}"] if std_period && dst_period lines.concat(observance_lines('STANDARD', dst_period, std_period)) lines.concat(observance_lines('DAYLIGHT', std_period, dst_period)) else lines.concat(fixed_observance_lines(std_period || dst_period || current)) end lines << "END:VTIMEZONE" lines rescue StandardError # Never let an exotic/edge-case timezone break calendar generation — # worst case the VEVENT ends up without a VTIMEZONE definition. nil end |