File: timezone.rb

package info (click to toggle)
ruby-icalendar 2.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 492 kB
  • sloc: ruby: 2,868; makefile: 5
file content (121 lines) | stat: -rw-r--r-- 3,238 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
require 'ice_cube'

module Icalendar

  class Timezone < Component
    module TzProperties
      def self.included(base)
        base.class_eval do
          required_property :dtstart, Icalendar::Values::DateTime
          required_property :tzoffsetfrom, Icalendar::Values::UtcOffset
          required_property :tzoffsetto, Icalendar::Values::UtcOffset

          optional_property :rrule, Icalendar::Values::Recur, true
          optional_property :comment
          optional_property :rdate, Icalendar::Values::DateTime
          optional_property :tzname

          transient_variable :@cached_occurrences
          transient_variable :@occurrences
        end
      end

      def occurrences
        @occurrences ||= IceCube::Schedule.new(dtstart.to_time) do |s|
          rrule.each do |rule|
            s.add_recurrence_rule IceCube::Rule.from_ical(rule.value_ical)
          end
          rdate.each do |date|
            s.add_recurrence_time date.to_time
          end
        end.all_occurrences_enumerator
      end

      def previous_occurrence(from)
        from = IceCube::TimeUtil.match_zone(from, dtstart.to_time)

        @cached_occurrences ||= []
        while @cached_occurrences.empty? || @cached_occurrences.last <= from
          begin
            @cached_occurrences << occurrences.next
          rescue StopIteration
            break
          end
        end

        @cached_occurrences.reverse_each.find { |occurrence| occurrence < from }
      end
    end
    class Daylight < Component
      include Marshable
      include TzProperties

      def initialize
        super 'daylight', 'DAYLIGHT'
      end
    end
    class Standard < Component
      include Marshable
      include TzProperties

      def initialize
        super 'standard', 'STANDARD'
      end
    end


    required_property :tzid

    optional_single_property :last_modified, Icalendar::Values::DateTime
    optional_single_property :tzurl, Icalendar::Values::Uri

    component :daylight, false, Icalendar::Timezone::Daylight
    component :standard, false, Icalendar::Timezone::Standard

    def initialize
      super 'timezone'
    end

    def valid?(strict = false)
      daylights.empty? && standards.empty? and return false
      daylights.all? { |d| d.valid? strict } or return false
      standards.all? { |s| s.valid? strict } or return false
      super
    end

    def offset_for_local(local)
      standard = standard_for local
      daylight = daylight_for local

      if standard.nil? && daylight.nil?
        "+00:00"
      elsif daylight.nil?
        standard.last.tzoffsetto
      elsif standard.nil?
        daylight.last.tzoffsetto
      else
        sdst = standard.first
        ddst = daylight.first
        if sdst > ddst
          standard.last.tzoffsetto
        else
          daylight.last.tzoffsetto
        end
      end
    end

    def standard_for(local)
      possible = standards.map do |std|
        [std.previous_occurrence(local.to_time), std]
      end
      possible.sort_by(&:first).last
    end

    def daylight_for(local)
      possible = daylights.map do |day|
        [day.previous_occurrence(local.to_time), day]
      end
      possible.sort_by(&:first).last
    end
  end
end