ri_cal 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +45 -0
- data/Manifest.txt +129 -0
- data/README.txt +394 -0
- data/Rakefile +31 -0
- data/bin/ri_cal +8 -0
- data/component_attributes/alarm.yml +10 -0
- data/component_attributes/calendar.yml +4 -0
- data/component_attributes/component_property_defs.yml +180 -0
- data/component_attributes/event.yml +45 -0
- data/component_attributes/freebusy.yml +16 -0
- data/component_attributes/journal.yml +35 -0
- data/component_attributes/timezone.yml +3 -0
- data/component_attributes/timezone_period.yml +11 -0
- data/component_attributes/todo.yml +46 -0
- data/copyrights.txt +1 -0
- data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
- data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
- data/docs/incrementers.txt +7 -0
- data/docs/rfc2445.pdf +0 -0
- data/lib/ri_cal.rb +144 -0
- data/lib/ri_cal/component.rb +247 -0
- data/lib/ri_cal/component/alarm.rb +21 -0
- data/lib/ri_cal/component/calendar.rb +219 -0
- data/lib/ri_cal/component/event.rb +60 -0
- data/lib/ri_cal/component/freebusy.rb +18 -0
- data/lib/ri_cal/component/journal.rb +30 -0
- data/lib/ri_cal/component/t_z_info_timezone.rb +123 -0
- data/lib/ri_cal/component/timezone.rb +196 -0
- data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
- data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
- data/lib/ri_cal/component/timezone/timezone_period.rb +53 -0
- data/lib/ri_cal/component/todo.rb +43 -0
- data/lib/ri_cal/core_extensions.rb +6 -0
- data/lib/ri_cal/core_extensions/array.rb +7 -0
- data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
- data/lib/ri_cal/core_extensions/date.rb +13 -0
- data/lib/ri_cal/core_extensions/date/conversions.rb +61 -0
- data/lib/ri_cal/core_extensions/date_time.rb +15 -0
- data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
- data/lib/ri_cal/core_extensions/object.rb +8 -0
- data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
- data/lib/ri_cal/core_extensions/string.rb +8 -0
- data/lib/ri_cal/core_extensions/string/conversions.rb +63 -0
- data/lib/ri_cal/core_extensions/time.rb +13 -0
- data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
- data/lib/ri_cal/core_extensions/time/conversions.rb +61 -0
- data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
- data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +88 -0
- data/lib/ri_cal/floating_timezone.rb +32 -0
- data/lib/ri_cal/invalid_property_value.rb +8 -0
- data/lib/ri_cal/invalid_timezone_identifer.rb +20 -0
- data/lib/ri_cal/occurrence_enumerator.rb +206 -0
- data/lib/ri_cal/occurrence_period.rb +17 -0
- data/lib/ri_cal/parser.rb +138 -0
- data/lib/ri_cal/properties/alarm.rb +390 -0
- data/lib/ri_cal/properties/calendar.rb +164 -0
- data/lib/ri_cal/properties/event.rb +1526 -0
- data/lib/ri_cal/properties/freebusy.rb +594 -0
- data/lib/ri_cal/properties/journal.rb +1240 -0
- data/lib/ri_cal/properties/timezone.rb +151 -0
- data/lib/ri_cal/properties/timezone_period.rb +416 -0
- data/lib/ri_cal/properties/todo.rb +1562 -0
- data/lib/ri_cal/property_value.rb +149 -0
- data/lib/ri_cal/property_value/array.rb +27 -0
- data/lib/ri_cal/property_value/cal_address.rb +11 -0
- data/lib/ri_cal/property_value/date.rb +175 -0
- data/lib/ri_cal/property_value/date_time.rb +335 -0
- data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
- data/lib/ri_cal/property_value/date_time/time_machine.rb +181 -0
- data/lib/ri_cal/property_value/date_time/timezone_support.rb +96 -0
- data/lib/ri_cal/property_value/duration.rb +110 -0
- data/lib/ri_cal/property_value/geo.rb +11 -0
- data/lib/ri_cal/property_value/integer.rb +12 -0
- data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
- data/lib/ri_cal/property_value/period.rb +82 -0
- data/lib/ri_cal/property_value/recurrence_rule.rb +145 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +97 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
- data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
- data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurence_incrementer.rb +793 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +60 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +49 -0
- data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
- data/lib/ri_cal/property_value/text.rb +40 -0
- data/lib/ri_cal/property_value/uri.rb +11 -0
- data/lib/ri_cal/property_value/utc_offset.rb +33 -0
- data/lib/ri_cal/required_timezones.rb +55 -0
- data/ri_cal.gemspec +49 -0
- data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +71 -0
- data/spec/ri_cal/component/alarm_spec.rb +12 -0
- data/spec/ri_cal/component/calendar_spec.rb +54 -0
- data/spec/ri_cal/component/event_spec.rb +601 -0
- data/spec/ri_cal/component/freebusy_spec.rb +12 -0
- data/spec/ri_cal/component/journal_spec.rb +37 -0
- data/spec/ri_cal/component/t_z_info_timezone_spec.rb +36 -0
- data/spec/ri_cal/component/timezone_spec.rb +218 -0
- data/spec/ri_cal/component/todo_spec.rb +112 -0
- data/spec/ri_cal/component_spec.rb +224 -0
- data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
- data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
- data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
- data/spec/ri_cal/occurrence_enumerator_spec.rb +573 -0
- data/spec/ri_cal/parser_spec.rb +303 -0
- data/spec/ri_cal/property_value/date_spec.rb +53 -0
- data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
- data/spec/ri_cal/property_value/duration_spec.rb +126 -0
- data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
- data/spec/ri_cal/property_value/period_spec.rb +49 -0
- data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
- data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
- data/spec/ri_cal/property_value/text_spec.rb +25 -0
- data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
- data/spec/ri_cal/property_value_spec.rb +125 -0
- data/spec/ri_cal/required_timezones_spec.rb +67 -0
- data/spec/ri_cal_spec.rb +53 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +46 -0
- data/tasks/gem_loader/load_active_support.rb +3 -0
- data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
- data/tasks/ri_cal.rake +410 -0
- data/tasks/spec.rake +50 -0
- metadata +221 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. properties event.rb])
|
|
2
|
+
|
|
3
|
+
module RiCal
|
|
4
|
+
class Component
|
|
5
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
6
|
+
#
|
|
7
|
+
# An Event (VEVENT) calendar component groups properties describing a scheduled event.
|
|
8
|
+
# Events may have multiple occurrences
|
|
9
|
+
#
|
|
10
|
+
# Events may also contain one or more ALARM subcomponents
|
|
11
|
+
#
|
|
12
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Event module
|
|
13
|
+
# to see the methods for enumerating occurrences of recurring events see the RiCal::OccurrenceEnumerator module
|
|
14
|
+
class Event < Component
|
|
15
|
+
include OccurrenceEnumerator
|
|
16
|
+
|
|
17
|
+
include RiCal::Properties::Event
|
|
18
|
+
|
|
19
|
+
def subcomponent_class #:nodoc:
|
|
20
|
+
{:alarm => Alarm }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.entity_name #:nodoc:
|
|
24
|
+
"VEVENT"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Return a date_time representing the time at which the event starts
|
|
28
|
+
def start_time
|
|
29
|
+
dtstart_property ? dtstart.to_datetime : nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Return a date_time representing the time at which the event starts
|
|
33
|
+
def finish_property
|
|
34
|
+
if dtend_property
|
|
35
|
+
dtend_property
|
|
36
|
+
elsif duration_property
|
|
37
|
+
(dtstart_property + duration_property)
|
|
38
|
+
else
|
|
39
|
+
dtstart_property
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Return a date_time representing the time at which the event starts
|
|
44
|
+
def finish_time
|
|
45
|
+
prop = finish_property
|
|
46
|
+
prop ? prop.to_finish_time : nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def zulu_occurrence_range_start_time
|
|
50
|
+
dtstart_property ? dtstart_property.to_zulu_occurrence_range_start_time : nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def zulu_occurrence_range_finish_time
|
|
54
|
+
prop = finish_property
|
|
55
|
+
prop ? prop.to_zulu_occurrence_range_finish_time : nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. properties freebusy.rb])
|
|
2
|
+
|
|
3
|
+
module RiCal
|
|
4
|
+
class Component
|
|
5
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
6
|
+
#
|
|
7
|
+
# A Freebusy (VFREEBUSY) calendar component groups properties describing either a request for free/busy time,
|
|
8
|
+
# a response to a request for free/busy time, or a published set of busy time.
|
|
9
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Freebusy module
|
|
10
|
+
class Freebusy < Component
|
|
11
|
+
include RiCal::Properties::Freebusy
|
|
12
|
+
|
|
13
|
+
def self.entity_name #:nodoc:
|
|
14
|
+
"VFREEBUSY"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
require File.join(File.dirname(__FILE__), %w[.. properties journal.rb])
|
|
3
|
+
|
|
4
|
+
module RiCal
|
|
5
|
+
class Component
|
|
6
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
7
|
+
#
|
|
8
|
+
# A Journal (VJOURNAL) calendar component groups properties describing a journal entry.
|
|
9
|
+
# Journals may have multiple occurrences
|
|
10
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Journal module
|
|
11
|
+
# to see the methods for enumerating occurrences of recurring journals see the RiCal::OccurrenceEnumerator module
|
|
12
|
+
class Journal < Component
|
|
13
|
+
include RiCal::Properties::Journal
|
|
14
|
+
include RiCal::OccurrenceEnumerator
|
|
15
|
+
|
|
16
|
+
def self.entity_name #:nodoc:
|
|
17
|
+
"VJOURNAL"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Return a date_time representing the time at which the event starts
|
|
21
|
+
def start_time
|
|
22
|
+
dtstart.to_datetime
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Journals take up no calendar time, so the finish time is always the same as the start_time
|
|
26
|
+
alias_method :finish_time, :start_time
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
2
|
+
#
|
|
3
|
+
# A wrapper class for a Timezone implemented by the TZInfo Gem
|
|
4
|
+
# (or by Rails)
|
|
5
|
+
class RiCal::Component::TZInfoTimezone < RiCal::Component::Timezone
|
|
6
|
+
|
|
7
|
+
class Period #:nodoc: all
|
|
8
|
+
|
|
9
|
+
def initialize(which, this_period, prev_period)
|
|
10
|
+
@which = which
|
|
11
|
+
@onset = this_period.local_start.strftime("%Y%m%dT%H%M%S")
|
|
12
|
+
@offset_from = format_rfc2445_offset(prev_period.utc_total_offset)
|
|
13
|
+
@offset_to = format_rfc2445_offset(this_period.utc_total_offset)
|
|
14
|
+
@abbreviation = this_period.abbreviation
|
|
15
|
+
@rdates = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_period(this_period)
|
|
19
|
+
@rdates << this_period.local_start.strftime("%Y%m%dT%H%M%S")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def format_rfc2445_offset(seconds) #:nodoc:
|
|
24
|
+
abs_seconds = seconds.abs
|
|
25
|
+
h = (abs_seconds/3600).floor
|
|
26
|
+
m = (abs_seconds - (h * 3600))/60
|
|
27
|
+
h *= -1 if seconds < 0
|
|
28
|
+
sprintf("%+03d%02d", h, m)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def export_to(export_stream)
|
|
32
|
+
export_stream.puts "BEGIN:#{@which}"
|
|
33
|
+
export_stream.puts "DTSTART:#{@onset}"
|
|
34
|
+
export_stream.puts "RDATE:#{@rdates.join(",")}"
|
|
35
|
+
export_stream.puts "TZOFFSETFROM:#{@offset_from}"
|
|
36
|
+
export_stream.puts "TZOFFSETTO:#{@offset_to}"
|
|
37
|
+
export_stream.puts "TZNAME:#{@abbreviation}"
|
|
38
|
+
export_stream.puts "END:#{@which}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Periods #:nodoc: all
|
|
43
|
+
|
|
44
|
+
def initialize
|
|
45
|
+
@dst_period = @std_period = @previous_period = nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def daylight_period(this_period, previous_period)
|
|
49
|
+
@daylight_period ||= Period.new("DAYLIGHT", this_period, previous_period)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def standard_period(this_period, previous_period)
|
|
53
|
+
@standard_period ||= Period.new("STANDARD", this_period, previous_period)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def log_period(period)
|
|
57
|
+
@periods ||= []
|
|
58
|
+
@periods << period unless @periods.include?(period)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def add_period(this_period)
|
|
62
|
+
if @previous_period
|
|
63
|
+
if this_period.dst?
|
|
64
|
+
period = daylight_period(this_period, @previous_period)
|
|
65
|
+
else
|
|
66
|
+
period = standard_period(this_period, @previous_period)
|
|
67
|
+
end
|
|
68
|
+
period.add_period(this_period)
|
|
69
|
+
log_period(period)
|
|
70
|
+
end
|
|
71
|
+
@previous_period = this_period
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def export_to(export_stream)
|
|
75
|
+
@periods.each {|period| period.export_to(export_stream)}
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
attr_reader :tzinfo_timezone #:nodoc:
|
|
80
|
+
|
|
81
|
+
def initialize(tzinfo_timezone) #:nodoc:
|
|
82
|
+
@tzinfo_timezone = tzinfo_timezone
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# convert time from this time zone to utc time
|
|
86
|
+
def local_to_utc(time)
|
|
87
|
+
@tzinfo_timezone.local_to_utc(time.to_ri_cal_ruby_value)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# convert time from utc time to this time zone
|
|
91
|
+
def utc_to_local(time)
|
|
92
|
+
@tzinfo_timezone.utc_to_local(time.to_ri_cal_ruby_value)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# return the time zone identifier
|
|
96
|
+
def identifier
|
|
97
|
+
@tzinfo_timezone.identifier
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def export_local_to(export_stream, local_start, local_end) #:nodoc:
|
|
101
|
+
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))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def to_rfc2445_string(utc_start, utc_end) #:nodoc:
|
|
105
|
+
export_stream = StringIO.new
|
|
106
|
+
export_utc_to(export_stream, utc_start, utc_end)
|
|
107
|
+
export_stream.string
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def export_utc_to(export_stream, utc_start, utc_end) #:nodoc:
|
|
111
|
+
export_stream.puts "BEGIN:VTIMEZONE","TZID;X-RICAL-TZSOURCE=TZINFO:#{identifier}"
|
|
112
|
+
periods = Periods.new
|
|
113
|
+
period = tzinfo_timezone.period_for_utc(utc_start)
|
|
114
|
+
#start with the period before the one containing utc_start
|
|
115
|
+
period = tzinfo_timezone.period_for_utc(period.utc_start - 1)
|
|
116
|
+
while period && period.utc_start < utc_end
|
|
117
|
+
periods.add_period(period)
|
|
118
|
+
period = tzinfo_timezone.period_for_utc(period.utc_end + 1)
|
|
119
|
+
end
|
|
120
|
+
periods.export_to(export_stream)
|
|
121
|
+
export_stream.puts "END:VTIMEZONE\n"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. properties timezone.rb])
|
|
2
|
+
|
|
3
|
+
module RiCal
|
|
4
|
+
class Component
|
|
5
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
6
|
+
#
|
|
7
|
+
# An Timezone (VTIMEZONE) calendar component describes a timezone used within the calendar.
|
|
8
|
+
# A Timezone has two or more TimezonePeriod subcomponents which describe the transitions between
|
|
9
|
+
# standard and daylight saving time.
|
|
10
|
+
#
|
|
11
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Timezone module
|
|
12
|
+
class Timezone < Component
|
|
13
|
+
include RiCal::Properties::Timezone
|
|
14
|
+
|
|
15
|
+
# The identifier of the timezone, e.g. "Europe/Paris".
|
|
16
|
+
def identifier
|
|
17
|
+
tzid
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# An alias for identifier.
|
|
21
|
+
def name
|
|
22
|
+
# Don't use alias, as identifier gets overridden.
|
|
23
|
+
identifier
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def rational_utc_offset(local) #:nodoc:
|
|
27
|
+
Rational(period_for_local(local, true).utc_total_offset, 3600) / 24
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the TimezonePeriod for the given UTC time. utc can either be a DateTime,
|
|
31
|
+
# Time or integer timestamp (Time.to_i). Any timezone information in utc is ignored (it is treated as a UTC time).
|
|
32
|
+
def period_for_utc(time)
|
|
33
|
+
last_period(last_before_utc(standard, time), last_before_utc(daylight, time))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the set of TimezonePeriod instances that are valid for the given
|
|
37
|
+
# local time as an array. If you just want a single period, use
|
|
38
|
+
# period_for_local instead and specify how ambiguities should be resolved.
|
|
39
|
+
# Returns an empty array if no periods are found for the given time.
|
|
40
|
+
def periods_for_local(local)
|
|
41
|
+
local = local.to_ri_cal_date_time_value
|
|
42
|
+
candidate_standard = last_before_local(standard, local)
|
|
43
|
+
candidate_daylight = last_before_local(daylight, local)
|
|
44
|
+
if candidate_daylight && candidate_daylight.swallows_local?(local, candidate_standard)
|
|
45
|
+
[] # Invalid local time
|
|
46
|
+
elsif candidate_standard
|
|
47
|
+
if candidate_daylight
|
|
48
|
+
if candidate_daylight.dtstart > candidate_standard.dtstart
|
|
49
|
+
[candidate_daylight]
|
|
50
|
+
elsif candidate_standard.ambiguous_local?(local)
|
|
51
|
+
[candidate_daylight, candidate_standard]
|
|
52
|
+
else
|
|
53
|
+
[candidate_standard].compact
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
[candidate_standard].compact
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Returns the TimezonePeriod for the given local time. local can either be
|
|
63
|
+
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
|
|
64
|
+
# information in local is ignored (it is treated as a time in the current
|
|
65
|
+
# timezone).
|
|
66
|
+
#
|
|
67
|
+
# Warning: There are local times that have no equivalent UTC times (e.g.
|
|
68
|
+
# in the transition from standard time to daylight savings time). There are
|
|
69
|
+
# also local times that have more than one UTC equivalent (e.g. in the
|
|
70
|
+
# transition from daylight savings time to standard time).
|
|
71
|
+
#
|
|
72
|
+
# In the first case (no equivalent UTC time), a PeriodNotFound exception
|
|
73
|
+
# will be raised.
|
|
74
|
+
#
|
|
75
|
+
# In the second case (more than one equivalent UTC time), an AmbiguousTime
|
|
76
|
+
# exception will be raised unless the optional dst parameter or block
|
|
77
|
+
# handles the ambiguity.
|
|
78
|
+
#
|
|
79
|
+
# If the ambiguity is due to a transition from daylight savings time to
|
|
80
|
+
# standard time, the dst parameter can be used to select whether the
|
|
81
|
+
# daylight savings time or local time is used. For example,
|
|
82
|
+
#
|
|
83
|
+
# Timezone.get('America/New_York').period_for_local(DateTime.new(2004,10,31,1,30,0))
|
|
84
|
+
#
|
|
85
|
+
# would raise an AmbiguousTime exception.
|
|
86
|
+
#
|
|
87
|
+
# Specifying dst=true would the daylight savings period from April to
|
|
88
|
+
# October 2004. Specifying dst=false would return the standard period
|
|
89
|
+
# from October 2004 to April 2005.
|
|
90
|
+
#
|
|
91
|
+
# If the dst parameter does not resolve the ambiguity, and a block is
|
|
92
|
+
# specified, it is called. The block must take a single parameter - an
|
|
93
|
+
# array of the periods that need to be resolved. The block can select and
|
|
94
|
+
# return a single period or return nil or an empty array
|
|
95
|
+
# to cause an AmbiguousTime exception to be raised.
|
|
96
|
+
#
|
|
97
|
+
# TODO: need to check for ambiguity
|
|
98
|
+
def period_for_local(local, dst=nil)
|
|
99
|
+
results = periods_for_local(local)
|
|
100
|
+
|
|
101
|
+
if results.empty?
|
|
102
|
+
raise TZInfo::PeriodNotFound
|
|
103
|
+
elsif results.size < 2
|
|
104
|
+
results.first
|
|
105
|
+
else
|
|
106
|
+
# ambiguous result try to resolve
|
|
107
|
+
|
|
108
|
+
unless dst.nil?
|
|
109
|
+
matches = results.find_all {|period| period.dst? == dst}
|
|
110
|
+
results = matches unless matches.empty?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
if results.size < 2
|
|
114
|
+
results.first
|
|
115
|
+
else
|
|
116
|
+
# still ambiguous, try the block
|
|
117
|
+
|
|
118
|
+
if block_given?
|
|
119
|
+
results = yield results
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if results.is_a?(TimezonePeriod)
|
|
123
|
+
results
|
|
124
|
+
elsif results && results.size == 1
|
|
125
|
+
results.first
|
|
126
|
+
else
|
|
127
|
+
raise TZInfo::AmbiguousTime, "#{local} is an ambiguous local time."
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# convert time from utc time to this time zone
|
|
134
|
+
def utc_to_local(time)
|
|
135
|
+
time = time.to_ri_cal_date_time_value
|
|
136
|
+
converted = time + period_for_utc(time).tzoffsetto_property
|
|
137
|
+
converted.tzid = identifier
|
|
138
|
+
converted
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# convert time from this time zone to utc time
|
|
142
|
+
def local_to_utc(time)
|
|
143
|
+
time = time.to_ri_cal_date_time_value
|
|
144
|
+
period = period_for_local(time)
|
|
145
|
+
converted = time - period.tzoffsetto_property
|
|
146
|
+
converted.tzid = "UTC"
|
|
147
|
+
converted
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def self.entity_name #:nodoc:
|
|
152
|
+
"VTIMEZONE"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def standard #:nodoc:
|
|
156
|
+
@subcomponents["STANDARD"]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def daylight #:nodoc:
|
|
160
|
+
@subcomponents["DAYLIGHT"]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def last_period(standard, daylight) #:nodoc:
|
|
164
|
+
if standard
|
|
165
|
+
if daylight
|
|
166
|
+
standard.dtstart > daylight.dtstart ? standard : daylight
|
|
167
|
+
else
|
|
168
|
+
standard
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
daylight
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def last_before_utc(period_array, time) #:nodoc:
|
|
176
|
+
candidates = period_array.map {|period|
|
|
177
|
+
period.last_before_utc(time)
|
|
178
|
+
}
|
|
179
|
+
result = candidates.max {|a, b| a.dtstart_property <=> b.dtstart_property}
|
|
180
|
+
result
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def last_before_local(period_array, time) #:nodoc:
|
|
184
|
+
candidates = period_array.map {|period|
|
|
185
|
+
period.last_before_local(time)
|
|
186
|
+
}
|
|
187
|
+
result = candidates.max {|a, b| a.dtstart_property <=> b.dtstart_property}
|
|
188
|
+
result
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
%w[timezone_period.rb daylight_period.rb standard_period.rb].each do |filename|
|
|
195
|
+
require "#{File.dirname(__FILE__)}/timezone/#{filename}"
|
|
196
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module RiCal
|
|
2
|
+
class Component
|
|
3
|
+
class Timezone
|
|
4
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
5
|
+
#
|
|
6
|
+
# A DaylightPeriod is a TimezonePeriod during which daylight saving time *is* in effect
|
|
7
|
+
class DaylightPeriod < TimezonePeriod #:nodoc: all
|
|
8
|
+
|
|
9
|
+
def self.entity_name #:nodoc:
|
|
10
|
+
"DAYLIGHT"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def dst?
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def swallows_local?(local, std_candidate)
|
|
18
|
+
([local.year, local.month, local.day] == [dtstart.year,dtstart.month, dtstart.day]) &&
|
|
19
|
+
local >= dtstart_property &&
|
|
20
|
+
local.advance(:seconds => (std_candidate.utc_total_offset - utc_total_offset)) < dtstart_property
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|