icalendar 2.10.2 → 2.11.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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +4 -3
- data/CHANGELOG.md +10 -0
- data/icalendar.gemspec +3 -0
- data/lib/icalendar/offset/active_support_exact.rb +23 -0
- data/lib/icalendar/offset/active_support_partial.rb +27 -0
- data/lib/icalendar/offset/null.rb +17 -0
- data/lib/icalendar/offset/time_zone_store.rb +29 -0
- data/lib/icalendar/offset.rb +42 -0
- data/lib/icalendar/value.rb +8 -0
- data/lib/icalendar/values/helpers/time_with_zone.rb +13 -40
- data/lib/icalendar/version.rb +1 -1
- data/spec/spec_helper.rb +6 -0
- data/spec/values/date_spec.rb +24 -0
- metadata +51 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a3ae780c56015cbd4ff657ce042c086038f2f922069fa40eaaeaeedd3b7144c
|
4
|
+
data.tar.gz: 88d669f06da7a9b91c3c8a92dd08e11e70b8712a2c4b05a752427abc1ed56435
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 250a5156dbb7f7ec7cd7f9fddd51982676d81b5a41ae2815c85b41f3f114e3b3c812c07794463d3563f85a93d8097075a19bb7d37b2ca4694682cc8804ff9be7
|
7
|
+
data.tar.gz: 11c71867043fd1fd94cc6199e470b9ff4ce55c7c5e2455d0435b46676b4017743cec130de8f5383faf66eee4e28a400b6b807bf76eb1c781fd47604b7276a4c5
|
data/.github/workflows/main.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## Unreleased
|
2
2
|
|
3
|
+
## 2.11.0 - 2025-04-12
|
4
|
+
- Require gems for ruby 3.4 that used to be in stdlib - Go Sueyoshi
|
5
|
+
- Refactor how timezone offsets are calculated - Pat Allan
|
6
|
+
- 'tzid' param is always returned as an array when accessing `ical_params` directly, rather than matching whatever the input parsed. ICS output remains unchanged
|
7
|
+
|
8
|
+
## 2.10.3 - 2024-09-21
|
9
|
+
- Override Icalendar::Value.== so that value objects can be compared to each other.
|
10
|
+
- Correctly load activesupport before activesupport/time
|
11
|
+
- Load ostruct to address deprecation warning - aki77
|
12
|
+
|
3
13
|
## 2.10.2 - 2024-07-21
|
4
14
|
- Raise Icalendar::Parser::ParseError on bad .ics file input instead of RuntimeError - Micah Geisel
|
5
15
|
- Remove Ruby 3.0 from the test matrix - still should work for now with ice_cube < 0.17.0
|
data/icalendar.gemspec
CHANGED
@@ -31,7 +31,10 @@ ActiveSupport is required for TimeWithZone support, but not required for general
|
|
31
31
|
|
32
32
|
s.required_ruby_version = '>= 2.4.0'
|
33
33
|
|
34
|
+
s.add_dependency 'base64'
|
34
35
|
s.add_dependency 'ice_cube', '~> 0.16'
|
36
|
+
s.add_dependency 'logger'
|
37
|
+
s.add_dependency 'ostruct'
|
35
38
|
|
36
39
|
s.add_development_dependency 'rake', '~> 13.0'
|
37
40
|
s.add_development_dependency 'bundler', '~> 2.0'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class ActiveSupportExact < Icalendar::Offset
|
6
|
+
def valid?
|
7
|
+
support_classes_defined? && tz
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
Icalendar.logger.debug("Plan a - parsing #{value}/#{tzid} as ActiveSupport::TimeWithZone")
|
12
|
+
# plan a - use ActiveSupport::TimeWithZone
|
13
|
+
Icalendar::Values::Helpers::ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def tz
|
19
|
+
@tz ||= ActiveSupport::TimeZone[tzid]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class ActiveSupportPartial < Offset
|
6
|
+
def valid?
|
7
|
+
support_classes_defined? && tz
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
# plan c - try to find an ActiveSupport::TimeWithZone based on the first word of the tzid
|
12
|
+
Icalendar.logger.debug("Plan c - parsing #{value}/#{tz.tzinfo.name} as ActiveSupport::TimeWithZone")
|
13
|
+
Icalendar::Values::Helpers::ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def normalized_tzid
|
17
|
+
[tz.tzinfo.name]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def tz
|
23
|
+
@tz ||= ActiveSupport::TimeZone[tzid.split.first]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class Null < Offset
|
6
|
+
def valid?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
# plan d - just ignore the tzid
|
12
|
+
Icalendar.logger.info("Ignoring timezone #{tzid} for time #{value}")
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class TimeZoneStore < Offset
|
6
|
+
def valid?
|
7
|
+
timezone_store && tz_info
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
# plan b - use definition from provided `VTIMEZONE`
|
12
|
+
offset = tz_info.offset_for_local(value).to_s
|
13
|
+
|
14
|
+
Icalendar.logger.debug("Plan b - parsing #{value} with offset: #{offset}")
|
15
|
+
if value.respond_to?(:change)
|
16
|
+
value.change offset: offset
|
17
|
+
else
|
18
|
+
::Time.new value.year, value.month, value.day, value.hour, value.min, value.sec, offset
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def tz_info
|
25
|
+
@tz_info ||= timezone_store.retrieve(tzid)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
def self.build(value, params, timezone_store)
|
6
|
+
return nil if params.nil? || params['tzid'].nil?
|
7
|
+
|
8
|
+
tzid = Array(params['tzid']).first
|
9
|
+
|
10
|
+
[
|
11
|
+
Icalendar::Offset::ActiveSupportExact,
|
12
|
+
Icalendar::Offset::TimeZoneStore,
|
13
|
+
Icalendar::Offset::ActiveSupportPartial,
|
14
|
+
Icalendar::Offset::Null
|
15
|
+
].lazy.map { |klass| klass.new(tzid, value, timezone_store) }.detect(&:valid?)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(tzid, value, timezone_store)
|
19
|
+
@tzid = tzid
|
20
|
+
@value = value
|
21
|
+
@timezone_store = timezone_store
|
22
|
+
end
|
23
|
+
|
24
|
+
def normalized_tzid
|
25
|
+
Array(tzid)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :tzid, :value, :timezone_store
|
31
|
+
|
32
|
+
def support_classes_defined?
|
33
|
+
defined?(ActiveSupport::TimeZone) &&
|
34
|
+
defined?(Icalendar::Values::Helpers::ActiveSupportTimeWithZoneAdapter)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require_relative "offset/active_support_exact"
|
40
|
+
require_relative "offset/active_support_partial"
|
41
|
+
require_relative "offset/null"
|
42
|
+
require_relative "offset/time_zone_store"
|
data/lib/icalendar/value.rb
CHANGED
@@ -45,6 +45,14 @@ module Icalendar
|
|
45
45
|
self.class.value_type
|
46
46
|
end
|
47
47
|
|
48
|
+
def ==(other)
|
49
|
+
if other.is_a?(Icalendar::Value)
|
50
|
+
super(other.value) && ical_params == other.ical_params
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
48
56
|
private
|
49
57
|
|
50
58
|
def needs_value_type?(default_type)
|
@@ -1,17 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "icalendar/offset"
|
4
|
+
|
3
5
|
begin
|
6
|
+
require 'active_support'
|
4
7
|
require 'active_support/time'
|
5
8
|
|
6
9
|
if defined?(ActiveSupport::TimeWithZone)
|
7
10
|
require_relative 'active_support_time_with_zone_adapter'
|
8
11
|
end
|
9
|
-
rescue NameError
|
10
|
-
# ActiveSupport v7+ needs the base require to be run first before loading
|
11
|
-
# specific parts of it.
|
12
|
-
# https://guides.rubyonrails.org/active_support_core_extensions.html#stand-alone-active-support
|
13
|
-
require 'active_support'
|
14
|
-
retry
|
15
12
|
rescue LoadError
|
16
13
|
# tis ok, just a bit less fancy
|
17
14
|
end
|
@@ -26,7 +23,13 @@ module Icalendar
|
|
26
23
|
params = Icalendar::DowncasedHash(params)
|
27
24
|
@tz_utc = params['tzid'] == 'UTC'
|
28
25
|
@timezone_store = params.delete 'x-tz-store'
|
29
|
-
|
26
|
+
|
27
|
+
offset = Icalendar::Offset.build(value, params, timezone_store)
|
28
|
+
|
29
|
+
@offset_value = offset&.normalized_value
|
30
|
+
params['tzid'] = offset.normalized_tzid if offset
|
31
|
+
|
32
|
+
super (@offset_value || value), params
|
30
33
|
end
|
31
34
|
|
32
35
|
def __getobj__
|
@@ -34,9 +37,9 @@ module Icalendar
|
|
34
37
|
if set_offset?
|
35
38
|
orig_value
|
36
39
|
else
|
37
|
-
|
38
|
-
__setobj__(
|
39
|
-
|
40
|
+
new_value = Icalendar::Offset.build(orig_value, ical_params, timezone_store)&.normalized_value
|
41
|
+
__setobj__(new_value) unless new_value.nil?
|
42
|
+
new_value || orig_value
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -47,36 +50,6 @@ module Icalendar
|
|
47
50
|
|
48
51
|
private
|
49
52
|
|
50
|
-
def offset_value(value, params)
|
51
|
-
@offset_value = unless params.nil? || params['tzid'].nil?
|
52
|
-
tzid = params['tzid'].is_a?(::Array) ? params['tzid'].first : params['tzid']
|
53
|
-
support_classes_defined = defined?(ActiveSupport::TimeZone) && defined?(ActiveSupportTimeWithZoneAdapter)
|
54
|
-
if support_classes_defined && (tz = ActiveSupport::TimeZone[tzid])
|
55
|
-
Icalendar.logger.debug("Plan a - parsing #{value}/#{tzid} as ActiveSupport::TimeWithZone")
|
56
|
-
# plan a - use ActiveSupport::TimeWithZone
|
57
|
-
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
58
|
-
elsif !timezone_store.nil? && !(x_tz_info = timezone_store.retrieve(tzid)).nil?
|
59
|
-
# plan b - use definition from provided `VTIMEZONE`
|
60
|
-
offset = x_tz_info.offset_for_local(value).to_s
|
61
|
-
Icalendar.logger.debug("Plan b - parsing #{value} with offset: #{offset}")
|
62
|
-
if value.respond_to?(:change)
|
63
|
-
value.change offset: offset
|
64
|
-
else
|
65
|
-
::Time.new value.year, value.month, value.day, value.hour, value.min, value.sec, offset
|
66
|
-
end
|
67
|
-
elsif support_classes_defined && (tz = ActiveSupport::TimeZone[tzid.split.first])
|
68
|
-
# plan c - try to find an ActiveSupport::TimeWithZone based on the first word of the tzid
|
69
|
-
Icalendar.logger.debug("Plan c - parsing #{value}/#{tz.tzinfo.name} as ActiveSupport::TimeWithZone")
|
70
|
-
params['tzid'] = [tz.tzinfo.name]
|
71
|
-
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
72
|
-
else
|
73
|
-
# plan d - just ignore the tzid
|
74
|
-
Icalendar.logger.info("Ignoring timezone #{tzid} for time #{value}")
|
75
|
-
nil
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
53
|
def set_offset?
|
81
54
|
!!@offset_value
|
82
55
|
end
|
data/lib/icalendar/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -12,6 +12,12 @@ end
|
|
12
12
|
require 'timecop'
|
13
13
|
require 'icalendar'
|
14
14
|
|
15
|
+
if defined?(ActiveSupport)
|
16
|
+
# this has been default behavior for new Rails apps for a long time, and the
|
17
|
+
# only option once ActiveSupport goes to 8.x
|
18
|
+
ActiveSupport.to_time_preserves_timezone = true if ActiveSupport.respond_to?(:to_time_preserves_timezone=)
|
19
|
+
end
|
20
|
+
|
15
21
|
RSpec.configure do |config|
|
16
22
|
config.run_all_when_everything_filtered = true
|
17
23
|
config.filter_run :focus
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Icalendar::Values::Date do
|
4
|
+
|
5
|
+
subject { described_class.new value, params }
|
6
|
+
let(:value) { '20140209' }
|
7
|
+
let(:params) { {} }
|
8
|
+
|
9
|
+
describe "#==" do
|
10
|
+
let(:other) { described_class.new value }
|
11
|
+
|
12
|
+
it "is equivalent to another Icalendar::Values::Date" do
|
13
|
+
expect(subject).to eq other
|
14
|
+
end
|
15
|
+
|
16
|
+
context "differing params" do
|
17
|
+
let(:params) { {"x-custom-param": "param-value"} }
|
18
|
+
|
19
|
+
it "is no longer equivalent" do
|
20
|
+
expect(subject).not_to eq other
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: icalendar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Ahearn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: base64
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: ice_cube
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,6 +38,34 @@ dependencies:
|
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: logger
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: ostruct
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: rake
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -181,6 +223,11 @@ files:
|
|
181
223
|
- lib/icalendar/journal.rb
|
182
224
|
- lib/icalendar/logger.rb
|
183
225
|
- lib/icalendar/marshable.rb
|
226
|
+
- lib/icalendar/offset.rb
|
227
|
+
- lib/icalendar/offset/active_support_exact.rb
|
228
|
+
- lib/icalendar/offset/active_support_partial.rb
|
229
|
+
- lib/icalendar/offset/null.rb
|
230
|
+
- lib/icalendar/offset/time_zone_store.rb
|
184
231
|
- lib/icalendar/parser.rb
|
185
232
|
- lib/icalendar/timezone.rb
|
186
233
|
- lib/icalendar/timezone_store.rb
|
@@ -237,6 +284,7 @@ files:
|
|
237
284
|
- spec/todo_spec.rb
|
238
285
|
- spec/tzinfo_spec.rb
|
239
286
|
- spec/values/date_or_date_time_spec.rb
|
287
|
+
- spec/values/date_spec.rb
|
240
288
|
- spec/values/date_time_spec.rb
|
241
289
|
- spec/values/duration_spec.rb
|
242
290
|
- spec/values/period_spec.rb
|
@@ -304,6 +352,7 @@ test_files:
|
|
304
352
|
- spec/todo_spec.rb
|
305
353
|
- spec/tzinfo_spec.rb
|
306
354
|
- spec/values/date_or_date_time_spec.rb
|
355
|
+
- spec/values/date_spec.rb
|
307
356
|
- spec/values/date_time_spec.rb
|
308
357
|
- spec/values/duration_spec.rb
|
309
358
|
- spec/values/period_spec.rb
|