File: t_z_info_timezone.rb

package info (click to toggle)
mhc 1.2.1-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 2,452 kB
  • sloc: ruby: 12,700; lisp: 7,577; makefile: 70; sh: 68
file content (153 lines) | stat: -rw-r--r-- 4,778 bytes parent folder | download | duplicates (3)
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
#
# A wrapper class for a Timezone implemented by the TZInfo Gem
# (or by Rails)
#

class RiCal::Component::TZInfoTimezone < RiCal::Component::Timezone

  class Period #:nodoc: all

    def initialize(which, this_period, prev_period)
      @which = which
      if prev_period
        @onset = period_local_end(prev_period)
        @offset_from = format_rfc2445_offset(prev_period.utc_total_offset)
      else
        @onset = period_local_start(this_period)
        @offset_from = format_rfc2445_offset(this_period.utc_total_offset)
      end
      @offset_to = format_rfc2445_offset(this_period.utc_total_offset)
      @abbreviation = this_period.abbreviation
      @rdates = []
    end
    
    def daylight?
      @which == "DAYLIGHT"
    end
    
    def period_local_end(period)
      (period.local_end || DateTime.parse("99990101T000000")).strftime("%Y%m%dT%H%M%S")
    end
    
    # This assumes a 1 hour shift which is why we use the previous period local end when
    # possible
    def period_local_start(period)
      shift = daylight? ? Rational(-1, 24) : Rational(1, 24)
      ((period.local_start || DateTime.parse("16010101T000000")) + shift).strftime("%Y%m%dT%H%M%S")
    end

    def add_period(this_period)
      @rdates << period_local_start(this_period)
    end


    def format_rfc2445_offset(seconds) #:nodoc:
      abs_seconds = seconds.abs
      h = (abs_seconds/3600).floor
      m = (abs_seconds - (h * 3600))/60
      h *= -1 if seconds < 0
      sprintf("%+03d%02d", h, m)
    end

    def export_to(export_stream)
      export_stream.puts "BEGIN:#{@which}"
      export_stream.puts "DTSTART:#{@onset}"
      @rdates.each do |rdate|
        export_stream.puts "RDATE:#{rdate}"
      end
      export_stream.puts "TZOFFSETFROM:#{@offset_from}"
      export_stream.puts "TZOFFSETTO:#{@offset_to}"
      export_stream.puts "TZNAME:#{@abbreviation}"
      export_stream.puts "END:#{@which}"
    end
  end

  class Periods #:nodoc: all

    def initialize
      @dst_period = @std_period = @previous_period = nil
    end
    
    def empty?
      @periods.nil? || @periods.empty?
    end

    def daylight_period(this_period, previous_period)
      @daylight_period ||= Period.new("DAYLIGHT", this_period, previous_period)
    end

    def standard_period(this_period, previous_period)
      @standard_period ||= Period.new("STANDARD", this_period, previous_period)
    end

    def log_period(period)
      @periods ||= []
      @periods << period unless @periods.include?(period)
    end
    
    def add_period(this_period, force=false)
      if @previous_period || force
        if this_period.dst?
          period = daylight_period(this_period, @previous_period)
        else
          period = standard_period(this_period, @previous_period)
        end
        period.add_period(this_period)
        log_period(period)
      end
      @previous_period = this_period
    end

    def export_to(export_stream)
      @periods.each {|period| period.export_to(export_stream)}
    end
  end

  attr_reader :tzinfo_timezone #:nodoc:

  def initialize(tzinfo_timezone) #:nodoc:
    @tzinfo_timezone = tzinfo_timezone
  end
  
  # convert time from this time zone to utc time
  def local_to_utc(time)
    @tzinfo_timezone.local_to_utc(time.to_ri_cal_ruby_value)
  end

  # convert time from utc time to this time zone
  def utc_to_local(time)
    @tzinfo_timezone.utc_to_local(time.to_ri_cal_ruby_value)
  end

  # return the time zone identifier
  def identifier
    @tzinfo_timezone.identifier
  end

  def export_local_to(export_stream, local_start, local_end) #:nodoc:
    export_utc_to(export_stream, local_to_utc(local_start.to_ri_cal_ruby_value), local_to_utc(local_end.to_ri_cal_ruby_value))
  end

  def to_rfc2445_string(utc_start, utc_end) #:nodoc:
    export_stream = StringIO.new
    export_utc_to(export_stream, utc_start, utc_end)
    export_stream.string
  end

  def export_utc_to(export_stream, utc_start, utc_end) #:nodoc:
    export_stream.puts "BEGIN:VTIMEZONE","TZID;X-RICAL-TZSOURCE=TZINFO:#{identifier}"
    periods = Periods.new
    period = initial_period = tzinfo_timezone.period_for_utc(utc_start)
     #start with the period before the one containing utc_start
    prev_period = period.utc_start && tzinfo_timezone.period_for_utc(period.utc_start - 1)
    period = prev_period if prev_period
    while period && period.utc_start && period.utc_start < utc_end
      periods.add_period(period)
      period = period.utc_end && tzinfo_timezone.period_for_utc(period.utc_end + 1)
    end
    periods.add_period(initial_period, :force) if periods.empty?
    periods.export_to(export_stream)
    export_stream.puts "END:VTIMEZONE\n"
  end
end