icalendar-recurrence 0.0.1
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/.gitignore +17 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +86 -0
- data/Rakefile +6 -0
- data/icalendar-recurrence.gemspec +31 -0
- data/lib/icalendar-recurrence.rb +1 -0
- data/lib/icalendar/recurrence.rb +12 -0
- data/lib/icalendar/recurrence/event_extensions.rb +36 -0
- data/lib/icalendar/recurrence/schedule.rb +148 -0
- data/lib/icalendar/recurrence/time_util.rb +94 -0
- data/lib/icalendar/recurrence/version.rb +5 -0
- data/lib/icalendar/recurrence/weekday_extensions.rb +13 -0
- data/spec/lib/recurrence_spec.rb +154 -0
- data/spec/lib/schedule_spec.rb +52 -0
- data/spec/lib/time_util_spec.rb +137 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/fixtures/daily_event.ics +30 -0
- data/spec/support/fixtures/embedded_timezone_event.ics +36 -0
- data/spec/support/fixtures/every_monday_event.ics +28 -0
- data/spec/support/fixtures/every_other_day_event.ics +29 -0
- data/spec/support/fixtures/every_weekday_daily_event.ics +26 -0
- data/spec/support/fixtures/everyday_for_four_days_event.ics +23 -0
- data/spec/support/fixtures/first_of_every_year_event.ics +31 -0
- data/spec/support/fixtures/first_saturday_of_month_event.ics +49 -0
- data/spec/support/fixtures/first_sunday_of_january_yearly_event.ics +26 -0
- data/spec/support/fixtures/monday_until_friday_event.ics +23 -0
- data/spec/support/fixtures/multi_day_weekly_event.ics +45 -0
- data/spec/support/fixtures/on_third_every_two_months_event.ics +28 -0
- data/spec/support/fixtures/one_day_a_month_for_three_months_event.ics +29 -0
- data/spec/support/fixtures/utc_event.ics +28 -0
- data/spec/support/helpers.rb +14 -0
- metadata +266 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Event#occurrences_between" do
|
4
|
+
let(:start_time) { event.start_time }
|
5
|
+
|
6
|
+
context "event repeating daily" do
|
7
|
+
let(:event) { example_event :daily } # has exclusion on Jan 28th
|
8
|
+
it "properly calculates recurrence, including exclusion date" do
|
9
|
+
occurrences = event.occurrences_between(start_time, start_time + 2.days)
|
10
|
+
|
11
|
+
expect(occurrences.length).to eq(2)
|
12
|
+
expect(occurrences.first.start_time).to eq(Time.parse("2014-01-27"))
|
13
|
+
expect(occurrences.last.start_time).to eq(Time.parse("2014-01-29"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "event repeating every other day" do
|
18
|
+
let(:event) { example_event :every_other_day }
|
19
|
+
|
20
|
+
it "occurs 3 times over 6 days" do
|
21
|
+
occurrences = event.occurrences_between(start_time, start_time + 5.days)
|
22
|
+
|
23
|
+
expect(occurrences.length).to eq(3)
|
24
|
+
expect(occurrences[0].start_time).to eq(Time.parse("2014-01-27"))
|
25
|
+
expect(occurrences[1].start_time).to eq(Time.parse("2014-01-29"))
|
26
|
+
expect(occurrences[2].start_time).to eq(Time.parse("2014-01-31"))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "event repeating every monday" do
|
31
|
+
let(:event) { example_event :every_monday }
|
32
|
+
|
33
|
+
it "occurs twice over 8 days" do
|
34
|
+
occurrences = event.occurrences_between(start_time, start_time + 8.days)
|
35
|
+
|
36
|
+
expect(occurrences.length).to eq(2)
|
37
|
+
expect(occurrences[0].start_time).to eq(Time.parse("2014-02-03 16:00:00 -0800"))
|
38
|
+
expect(occurrences[1].start_time).to eq(Time.parse("2014-02-10 16:00:00 -0800"))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "event repeating on Mon, Wed, Fri" do
|
43
|
+
let(:event) { example_event :multi_day_weekly }
|
44
|
+
|
45
|
+
it "occurs 3 times over 7 days" do
|
46
|
+
occurrences = event.occurrences_between(start_time, start_time + 7.days)
|
47
|
+
|
48
|
+
expect(occurrences.length).to eq(3)
|
49
|
+
expect(occurrences[0].start_time).to eq(Time.parse("2014-02-03 16:00:00 -0800"))
|
50
|
+
expect(occurrences[1].start_time).to eq(Time.parse("2014-02-05 16:00:00 -0800"))
|
51
|
+
expect(occurrences[2].start_time).to eq(Time.parse("2014-02-07 16:00:00 -0800"))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "event repeating bimonthly (DST example)" do
|
56
|
+
let(:event) { example_event :on_third_every_two_months }
|
57
|
+
|
58
|
+
it "occurs twice over 60 days" do
|
59
|
+
occurrences = event.occurrences_between(start_time, start_time + 60.days)
|
60
|
+
|
61
|
+
expect(occurrences.length).to eq(2)
|
62
|
+
expect(occurrences[0].start_time).to eq(Time.parse("2014-02-03 16:00:00 -0800"))
|
63
|
+
expect(occurrences[1].start_time).to eq(Time.parse("2014-04-03 16:00:00 -0700"))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "event repeating yearly" do
|
68
|
+
let(:event) { example_event :first_of_every_year }
|
69
|
+
|
70
|
+
it "occurs twice over 366 days" do
|
71
|
+
occurrences = event.occurrences_between(start_time, start_time + 365.days)
|
72
|
+
|
73
|
+
expect(occurrences.length).to eq(2)
|
74
|
+
expect(occurrences[0].start_time).to eq(Time.parse("2014-01-01"))
|
75
|
+
expect(occurrences[1].start_time).to eq(Time.parse("2015-01-01"))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "event repeating Mon-Fri" do
|
80
|
+
let(:event) { example_event :every_weekday_daily }
|
81
|
+
|
82
|
+
it "occurrs 10 times over two weeks" do
|
83
|
+
occurrences = event.occurrences_between(start_time, start_time + 13.days)
|
84
|
+
|
85
|
+
expect(occurrences.length).to eq(10)
|
86
|
+
expect(occurrences.map(&:start_time)).to include(Time.parse("2014-01-10"))
|
87
|
+
expect(occurrences.map(&:start_time)).to_not include(Time.parse("2014-01-11"))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "event repeating daily until January 18th" do
|
92
|
+
let(:event) { example_event :monday_until_friday }
|
93
|
+
|
94
|
+
it "occurs from start date until specified 'until' date" do
|
95
|
+
occurrences = event.occurrences_between(start_time, start_time + 30.days)
|
96
|
+
|
97
|
+
expected_start_times = [
|
98
|
+
Time.parse("2014-01-15 at 12pm").force_zone("America/Los_Angeles").utc,
|
99
|
+
Time.parse("2014-01-18 at 12pm").force_zone("America/Los_Angeles").utc
|
100
|
+
]
|
101
|
+
|
102
|
+
expect(occurrences.length).to eq(5)
|
103
|
+
expect(occurrences.map(&:start_time)).to include(expected_start_times[0])
|
104
|
+
expect(occurrences.map(&:start_time)).to_not include(expected_start_times[1])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "event repeating daily with occurrence count of 4" do
|
109
|
+
let(:event) { example_event :everyday_for_four_days }
|
110
|
+
|
111
|
+
it "occurs 4 times then stops" do
|
112
|
+
occurrences = event.occurrences_between(start_time, start_time + 365.days)
|
113
|
+
expect(occurrences.length).to eq(4)
|
114
|
+
expect(occurrences.map(&:start_time)).to include(Time.parse("2014-01-15 at 12pm").force_zone("America/Los_Angeles").utc)
|
115
|
+
expect(occurrences.map(&:start_time)).to_not include(Time.parse("2014-01-17 at 12pm").force_zone("America/Los_Angeles").utc)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "event repeating on first saturday of month event" do
|
120
|
+
let(:event) { example_event :first_saturday_of_month }
|
121
|
+
|
122
|
+
it "occurs twice over two months" do
|
123
|
+
occurrences = event.occurrences_between(start_time, start_time + 55.days)
|
124
|
+
|
125
|
+
expected_start_times = [
|
126
|
+
Time.parse("2014-01-04 at 12am").force_zone("America/Los_Angeles"),
|
127
|
+
Time.parse("2014-02-01 at 12am").force_zone("America/Los_Angeles"),
|
128
|
+
]
|
129
|
+
|
130
|
+
expect(occurrences.length).to eq(2)
|
131
|
+
expect(occurrences[0].start_time).to eq(expected_start_times[0])
|
132
|
+
expect(occurrences[1].start_time).to eq(expected_start_times[1])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "event repeating once a month for three months" do
|
137
|
+
let(:event) { example_event :one_day_a_month_for_three_months }
|
138
|
+
|
139
|
+
it "only occurs twice when we look for occurrences after the first one" do
|
140
|
+
occurrences = event.occurrences_between(start_time + 30.days, start_time + 90.days)
|
141
|
+
|
142
|
+
expect(occurrences.length).to eq(2)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "event in UTC time" do
|
147
|
+
let(:event) { example_event :utc }
|
148
|
+
|
149
|
+
it "occurs at the correct time" do
|
150
|
+
occurrences = event.occurrences_between(Time.parse("2014-01-01"), Time.parse("2014-02-01"))
|
151
|
+
expect(occurrences.first.start_time).to eq(Time.parse("20140114T180000Z"))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Icalendar::Recurrence::Schedule do
|
4
|
+
describe "#transform_byday_to_hash" do
|
5
|
+
it "returns an array of days when no monthly interval is set" do
|
6
|
+
byday = ["MO", "WE", "FR"]
|
7
|
+
schedule = Schedule.new(nil)
|
8
|
+
expect(schedule.transform_byday_to_hash(byday)).to eq([:monday, :wednesday, :friday])
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns hash with day of week and interval" do
|
12
|
+
byday = ["1SA"]
|
13
|
+
schedule = Schedule.new(nil);
|
14
|
+
expect(schedule.transform_byday_to_hash(byday)).to eq({saturday: [1]})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#occurrences_between" do
|
19
|
+
let(:example_occurrence) do
|
20
|
+
daily_event = example_event :daily
|
21
|
+
schedule = Schedule.new(daily_event)
|
22
|
+
schedule.occurrences_between(Date.parse("2014-02-01"), Date.parse("2014-03-01")).first
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns object that responds to start_time and end_time" do
|
26
|
+
expect(example_occurrence).to respond_to :start_time
|
27
|
+
expect(example_occurrence).to respond_to :end_time
|
28
|
+
end
|
29
|
+
|
30
|
+
context "timezoned event" do
|
31
|
+
let(:example_occurrence) do
|
32
|
+
timezoned_event = example_event :first_saturday_of_month
|
33
|
+
schedule = Schedule.new(timezoned_event)
|
34
|
+
example_occurrence = schedule.occurrences_between(Date.parse("2014-02-01"), Date.parse("2014-03-01")).first
|
35
|
+
end
|
36
|
+
|
37
|
+
it "#occurrences_between return object that responds to #start_time and #end_time (timezoned example)" do
|
38
|
+
expect(example_occurrence).to respond_to :start_time
|
39
|
+
expect(example_occurrence).to respond_to :end_time
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#parse_ical_byday" do
|
45
|
+
let(:schedule) { Schedule.new(nil) }
|
46
|
+
|
47
|
+
it "returns a hash of data" do
|
48
|
+
expect(schedule.parse_ical_byday("1SA")).to eq({day_code: "SA", position: 1})
|
49
|
+
expect(schedule.parse_ical_byday("MO")).to eq({day_code: "MO", position: 0})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TimeUtil do
|
4
|
+
describe ".datetime_to_time" do
|
5
|
+
it "converts DateTime to Time correctly" do
|
6
|
+
datetime = Icalendar::Values::DateTime.new(DateTime.parse("2014-01-27T12:55:21-08:00"))
|
7
|
+
correct_time = Time.parse("2014-01-27T12:55:21-08:00")
|
8
|
+
expect(TimeUtil.datetime_to_time(datetime)).to eq(correct_time)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "converts UTC datetime to time with no offset" do
|
12
|
+
utc_datetime = Icalendar::Values::DateTime.new(DateTime.parse("20140114T180000Z"))
|
13
|
+
expect(TimeUtil.datetime_to_time(utc_datetime).utc_offset).to eq(0)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "converts PST datetime to time with 8 hour offset" do
|
17
|
+
pst_datetime = Icalendar::Values::DateTime.new(DateTime.parse("2014-01-27T12:55:21-08:00"))
|
18
|
+
expect(TimeUtil.datetime_to_time(pst_datetime).utc_offset).to eq(-8*60*60)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe ".to_time" do
|
23
|
+
it "uses specified timezone ID offset while converting to a Time object" do
|
24
|
+
utc_midnight = DateTime.parse("2014-01-27T12:00:00+00:00")
|
25
|
+
pst_midnight = Time.parse("2014-01-27T12:00:00-08:00")
|
26
|
+
|
27
|
+
zoned_datetime = Icalendar::Values::DateTime.new(utc_midnight, "tzid" => "America/Los_Angeles")
|
28
|
+
|
29
|
+
expect(TimeUtil.to_time(zoned_datetime)).to eq(pst_midnight)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "parses a string" do
|
33
|
+
expect(TimeUtil.to_time("20140118T075959Z")).to eq(Time.parse("20140118T075959Z"))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".date_to_time" do
|
38
|
+
it "converts date to time object in local time" do
|
39
|
+
local_time = Time.parse("2014-01-01")
|
40
|
+
expect(TimeUtil.date_to_time(Date.parse("2014-01-01"))).to eq(local_time)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "timezone_offset" do
|
45
|
+
# Avoid DST changes by freezing time
|
46
|
+
before { Timecop.freeze("2014-01-01") }
|
47
|
+
after { Timecop.return }
|
48
|
+
|
49
|
+
it "calculates negative offset" do
|
50
|
+
expect(TimeUtil.timezone_offset("America/Los_Angeles")).to eq("-08:00")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "calculates positive offset" do
|
54
|
+
expect(TimeUtil.timezone_offset("Europe/Amsterdam")).to eq("+01:00")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "handles UTC zone" do
|
58
|
+
expect(TimeUtil.timezone_offset("GMT")).to eq("+00:00")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns nil when given an unknown timezone" do
|
62
|
+
expect(TimeUtil.timezone_offset("Foo/Bar")).to eq(nil)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "removes quotes from given TZID" do
|
66
|
+
expect(TimeUtil.timezone_offset("\"America/Los_Angeles\"")).to eq("-08:00")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "uses first element from array when given" do
|
70
|
+
expect(TimeUtil.timezone_offset(["America/Los_Angeles"])).to eq("-08:00")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns nil when given nil" do
|
74
|
+
expect(TimeUtil.timezone_offset(nil)).to eq(nil)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "calculates offset at a given moment" do
|
78
|
+
after_daylight_savings = Date.parse("2014-05-01")
|
79
|
+
expect(TimeUtil.timezone_offset("America/Los_Angeles", moment: after_daylight_savings)).to eq("-07:00")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "handles daylight savings" do
|
83
|
+
# FYI, clocks turn forward an hour on Nov 2 at 9:00:00 UTC
|
84
|
+
minute_before_clocks_change = Time.parse("Nov 2 at 08:59:00 UTC") # on west coast
|
85
|
+
minute_after_clocks_change = Time.parse("Nov 2 at 09:01:00 UTC") # on west coast
|
86
|
+
|
87
|
+
expect(TimeUtil.timezone_offset("America/Los_Angeles", moment: minute_before_clocks_change)).to eq("-07:00")
|
88
|
+
expect(TimeUtil.timezone_offset("America/Los_Angeles", moment: minute_after_clocks_change)).to eq("-08:00")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe ".force_zone" do
|
93
|
+
it "replaces the exist offset with the offset from a named zone" do
|
94
|
+
eight_am_utc = Time.parse("20140101T0800Z")
|
95
|
+
forced_time = TimeUtil.force_zone(eight_am_utc, "America/Los_Angeles")
|
96
|
+
expect(forced_time.utc.to_s).to eq("2014-01-01 16:00:00 UTC")
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when forced timezone is different than original" do
|
100
|
+
it "changes the moment in time the object refers to" do
|
101
|
+
eight_am_utc = Time.parse("20140101T0800Z")
|
102
|
+
forced_time = TimeUtil.force_zone(eight_am_utc, "America/Los_Angeles")
|
103
|
+
expect(forced_time.to_i).to_not eq(eight_am_utc.to_i)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "works for non-UTC time" do
|
108
|
+
eight_am_local = Time.parse("2014-01-01 08:00")
|
109
|
+
forced_time = TimeUtil.force_zone(eight_am_local, "Asia/Hong_Kong")
|
110
|
+
expect(forced_time.utc.to_s).to eq("2014-01-01 00:00:00 UTC")
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when given an unknown TZID" do
|
114
|
+
it "raises an error" do
|
115
|
+
expect {
|
116
|
+
TimeUtil.force_zone(Time.now, "Foo/Bar")
|
117
|
+
}.to raise_error(ArgumentError)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it "works with this example" do
|
122
|
+
forced_time = Time.parse("2014-01-04 at 4pm").force_zone("America/Los_Angeles")
|
123
|
+
expect(forced_time.hour).to eq(16)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "extends Time" do
|
127
|
+
forced_time = Time.parse("2014-01-01 at 8am").force_zone("Asia/Hong_Kong")
|
128
|
+
expect(forced_time.utc.to_s).to eq("2014-01-01 00:00:00 UTC")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "doesn't change passed in time objects to UTC" do
|
132
|
+
eight_am_local = Time.parse("2014-01-01 08:00")
|
133
|
+
TimeUtil.force_zone(eight_am_local, "Asia/Hong_Kong")
|
134
|
+
expect(eight_am_local.utc?).to eq(false)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'support/helpers'
|
2
|
+
require 'timecop'
|
3
|
+
|
4
|
+
require 'icalendar/recurrence'
|
5
|
+
|
6
|
+
include Icalendar::Recurrence
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
config.filter_run :focus
|
13
|
+
config.order = 'random'
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
X-WR-CALNAME:Test Public
|
3
|
+
X-WR-CALID:f512e378-050c-4366-809a-ef471ce45b09:101165
|
4
|
+
PRODID:Zimbra-Calendar-Provider
|
5
|
+
VERSION:2.0
|
6
|
+
METHOD:PUBLISH
|
7
|
+
BEGIN:VEVENT
|
8
|
+
UID:efcb99ae-d540-419c-91fa-42cc2bd9d302
|
9
|
+
RRULE:FREQ=DAILY;INTERVAL=1
|
10
|
+
SUMMARY:Every day, except the 28th
|
11
|
+
X-ALT-DESC;FMTTYPE=text/html:<html><body></body></html>
|
12
|
+
ORGANIZER;CN=Jordan Raine:mailto:foo@sfu.ca
|
13
|
+
DTSTART;VALUE=DATE:20140127
|
14
|
+
DTEND;VALUE=DATE:20140128
|
15
|
+
STATUS:CONFIRMED
|
16
|
+
CLASS:PUBLIC
|
17
|
+
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
|
18
|
+
X-MICROSOFT-CDO-INTENDEDSTATUS:FREE
|
19
|
+
TRANSP:TRANSPARENT
|
20
|
+
LAST-MODIFIED:20140113T200625Z
|
21
|
+
DTSTAMP:20140113T200625Z
|
22
|
+
SEQUENCE:0
|
23
|
+
EXDATE;VALUE=DATE:20140128
|
24
|
+
BEGIN:VALARM
|
25
|
+
ACTION:DISPLAY
|
26
|
+
TRIGGER;RELATED=START:-PT5M
|
27
|
+
DESCRIPTION:Reminder
|
28
|
+
END:VALARM
|
29
|
+
END:VEVENT
|
30
|
+
END:VCALENDAR
|
@@ -0,0 +1,36 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
X-WR-CALNAME:Calendar
|
3
|
+
X-WR-CALID:19234061-9654-4990-a740-64ad6ed79058:10
|
4
|
+
PRODID:Zimbra-Calendar-Provider
|
5
|
+
VERSION:2.0
|
6
|
+
METHOD:PUBLISH
|
7
|
+
BEGIN:VTIMEZONE
|
8
|
+
TZID:GMT-08.00/-07.00
|
9
|
+
BEGIN:STANDARD
|
10
|
+
DTSTART:19710101T010000
|
11
|
+
TZOFFSETTO:-0800
|
12
|
+
TZOFFSETFROM:-0700
|
13
|
+
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=11;BYDAY=1SU;WKST=MO
|
14
|
+
END:STANDARD
|
15
|
+
BEGIN:DAYLIGHT
|
16
|
+
DTSTART:19710101T030000
|
17
|
+
TZOFFSETTO:-0700
|
18
|
+
TZOFFSETFROM:-0800
|
19
|
+
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYDAY=2SU;WKST=MO
|
20
|
+
END:DAYLIGHT
|
21
|
+
END:VTIMEZONE
|
22
|
+
BEGIN:VEVENT
|
23
|
+
UID:7690678C7CBC43EB82AC1E63580D39A30
|
24
|
+
SUMMARY:Event using embedded timezone
|
25
|
+
PRIORITY:0
|
26
|
+
DTSTART;TZID="GMT-08.00/-07.00":20131222T120000
|
27
|
+
DTEND;TZID="GMT-08.00/-07.00":20131222T200000
|
28
|
+
STATUS:CONFIRMED
|
29
|
+
CLASS:PUBLIC
|
30
|
+
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
31
|
+
TRANSP:OPAQUE
|
32
|
+
LAST-MODIFIED:20131211T204108Z
|
33
|
+
DTSTAMP:20131211T204108Z
|
34
|
+
SEQUENCE:0
|
35
|
+
END:VEVENT
|
36
|
+
END:VCALENDAR
|