TimezoneParser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+ # encoding: utf-8
2
+
3
+ module TimezoneParser
4
+ # Timezone
5
+ class Timezone < ZoneInfo
6
+
7
+ protected
8
+ @@Locales = []
9
+ @@Regions = []
10
+
11
+ public
12
+ # Locales which will be used for Timezone methods if not specified there
13
+ #
14
+ # Each locale is language identifier based on IETF BCP 47
15
+ # @return [Array<String>] list containing locale identifiers
16
+ # @see http://en.wikipedia.org/wiki/IETF_language_tag
17
+ # @see http://unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
18
+ # @see http://www.unicode.org/cldr/charts/latest/supplemental/language_territory_information.html
19
+ def self.Locales
20
+ @@Locales
21
+ end
22
+
23
+ # Regions which will be used for Timezone methods if not specified there
24
+ #
25
+ # Each region is CLDR territory (UN M.49)
26
+ # @return [Array<String>] list containing region identifiers
27
+ # @see http://www.unicode.org/cldr/charts/latest/supplemental/territory_containment_un_m_49.html
28
+ def self.Regions
29
+ @@Regions
30
+ end
31
+
32
+ attr_accessor :Locales
33
+ attr_accessor :Regions
34
+ attr_accessor :All
35
+
36
+ # Timezone instance
37
+ # @param timezone [String] Timezone name
38
+ def initialize(timezone)
39
+ @Timezone = timezone
40
+ @Data = Data.new
41
+ @Valid = nil
42
+ setTime
43
+ set(@@Locales.dup, @@Regions.dup, true)
44
+ end
45
+
46
+ # Set locales, regions and all
47
+ # @param locales [Array<String>] search only in these locales
48
+ # @param regions [Array<String>] filter for these regions
49
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
50
+ # @return [Timezone] self
51
+ def set(locales = nil, regions = nil, all = true)
52
+ @Locales = locales unless locales.nil?
53
+ @Regions = regions unless regions.nil?
54
+ @All = all ? true : false
55
+ self
56
+ end
57
+
58
+ # Check if timezone is valid
59
+ # @return [Boolean] whether timezone is valid
60
+ def isValid?
61
+ if @Valid.nil?
62
+ locales = @Locales
63
+ locales = Data::Storage.Timezones.keys if locales.empty?
64
+ locales.each do |locale|
65
+ next unless Data::Storage.Timezones.has_key?(locale)
66
+ if Data::Storage.Timezones[locale].has_key?(@Timezone)
67
+ @Valid = true
68
+ return @Valid
69
+ end
70
+ end
71
+ end
72
+ @Valid = false
73
+ end
74
+
75
+ # Abbreviation data
76
+ # @return [Data] data
77
+ def getData
78
+ unless @Loaded
79
+ @Loaded = true
80
+ @Valid = false
81
+ locales = @Locales
82
+ locales = Data::Storage.Timezones.keys if locales.empty?
83
+ locales.each do |locale|
84
+ next unless Data::Storage.Timezones.has_key?(locale)
85
+ entry = Data::Storage.Timezones[locale][@Timezone]
86
+ if entry
87
+ @Data.processEntry(entry, @ToTime, @FromTime, @Regions)
88
+ @Valid = true
89
+ return @Data unless @All
90
+ end
91
+ end
92
+ end
93
+ @Data
94
+ end
95
+
96
+ # Get UTC offsets in seconds
97
+ # @return [Array<Fixnum>] list of timezone offsets in seconds
98
+ def getOffsets
99
+ if not @Offsets and not getTimezones.empty?
100
+ types = [@Type] if @Type
101
+ @Offsets = @Data.findOffsets(@ToTime, @FromTime, @Regions, types).to_a
102
+ else
103
+ super
104
+ end
105
+ @Offsets
106
+ end
107
+
108
+ # Check if given Timezone name is a valid timezone
109
+ # @param timezone [String] Timezone name
110
+ # @param locales [Array<String>] search Timezone name only for these locales
111
+ # @return [Boolean] whether Timezone is valid
112
+ # @see Locales
113
+ def self.isValid?(timezone, locales = nil)
114
+ self.new(timezone).set(locales).isValid?
115
+ end
116
+
117
+ # Get UTC offsets in seconds for given Timezone name
118
+ # @param timezone [String] Timezone name
119
+ # @param toTime [DateTime] look for offsets which came into effect before this date, exclusive
120
+ # @param fromTime [DateTime] look for offsets which came into effect at this date, inclusive
121
+ # @param locales [Array<String>] search Timezone name only for these locales
122
+ # @param regions [Array<String>] look for offsets only for these regions
123
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
124
+ # @return [Array<Fixnum>] list of timezone offsets in seconds
125
+ # @see Locales
126
+ # @see Regions
127
+ def self.getOffsets(timezone, toTime = nil, fromTime = nil, locales = nil, regions = nil, all = true)
128
+ self.new(timezone).setTime(toTime, fromTime).set(locales, regions, all).getOffsets
129
+ end
130
+
131
+ # Get Timezone identifiers for given Timezone name
132
+ # @param timezone [String] Timezone name
133
+ # @param toTime [DateTime] look for timezones which came into effect before this date, exclusive
134
+ # @param fromTime [DateTime] look for timezones which came into effect at this date, inclusive
135
+ # @param locales [Array<String>] search Timezone name only for these locales
136
+ # @param regions [Array<String>] look for timezones only for these regions
137
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
138
+ # @return [Array<String>] list of timezone identifiers
139
+ # @see Locales
140
+ # @see Regions
141
+ def self.getTimezones(timezone, toTime = nil, fromTime = nil, locales = nil, regions = nil, all = true)
142
+ self.new(timezone).setTime(toTime, fromTime).set(locales, regions, all).getTimezones
143
+ end
144
+
145
+ # Get Metazone identifiers for given Timezone name
146
+ # @param timezone [String] Timezone name
147
+ # @param toTime [DateTime] look for timezones which came into effect before this date, exclusive
148
+ # @param fromTime [DateTime] look for timezones which came into effect at this date, inclusive
149
+ # @param locales [Array<String>] search Timezone name only for these locales
150
+ # @param regions [Array<String>] look for timezones only for these regions
151
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
152
+ # @return [Array<String>] list of metazone identifiers
153
+ # @see Locales
154
+ # @see Regions
155
+ def self.getMetazones(timezone, toTime = nil, fromTime = nil, locales = nil, regions = nil, all = true)
156
+ self.new(timezone).setTime(toTime, fromTime).set(locales, regions, all).getMetazones
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+ module TimezoneParser
3
+ # Version
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,169 @@
1
+ # encoding: utf-8
2
+
3
+ module TimezoneParser
4
+ # Windows Timezone data
5
+ class WindowsData < Data
6
+ protected
7
+ @WindowsZone = nil
8
+
9
+ public
10
+ attr_reader :WindowsZone
11
+ def processEntry(entry, region)
12
+ @Types += entry['Types'] if entry['Types']
13
+ if entry.has_key?('Metazones')
14
+ entry['Metazones'].each do |zone|
15
+ @WindowsZone = zone
16
+ @Metazones << zone
17
+ @Timezones += Storage.getTimezones2(zone, region)
18
+ @Offsets += Storage.getOffsets(zone, entry['Types'])
19
+ end
20
+ end
21
+ self
22
+ end
23
+ end
24
+
25
+ # Windows Timezone
26
+ class WindowsZone < ZoneInfo
27
+ protected
28
+ @@Locales = []
29
+ @@Regions = []
30
+
31
+ public
32
+ # Locales which will be used for WindowsZone methods if not specified there
33
+ #
34
+ # Each locale consists of language identifier and country/region identifier
35
+ # @return [Array<String>] list containing locale identifiers
36
+ # @see http://msdn.microsoft.com/en-us/library/dd318693.aspx
37
+ def self.Locales
38
+ @@Locales
39
+ end
40
+
41
+ # Regions which will be used for WindowsZone methods if not specified there
42
+ #
43
+ # Each region is either ISO 3166-1 alpha-2 code
44
+ # @return [Array<String>] list containing region identifiers
45
+ # @see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
46
+ def self.Regions
47
+ @@Regions
48
+ end
49
+
50
+ attr_accessor :Locales
51
+ attr_accessor :Regions
52
+ attr_accessor :All
53
+
54
+ # Windows Timezone instance
55
+ # @param name [String] Windows Timezone name
56
+ def initialize(name)
57
+ @Name = name
58
+ @Data = WindowsData.new
59
+ @Valid = nil
60
+ set(@@Locales.dup, @@Regions.dup, true)
61
+ end
62
+
63
+ # Set locales, regions and all
64
+ # @param locales [Array<String>] search only in these locales
65
+ # @param regions [Array<String>] filter for these regions
66
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
67
+ # @return [WindowsZone] self
68
+ # @see Locales
69
+ # @see Regions
70
+ def set(locales = nil, regions = nil, all = true)
71
+ @Locales = locales unless locales.nil?
72
+ @Regions = regions unless regions.nil?
73
+ @All = all ? true : false
74
+ self
75
+ end
76
+
77
+ # Check if timezone is valid
78
+ # @return [Boolean] whether timezone is valid
79
+ def isValid?
80
+ if @Valid.nil?
81
+ locales = @Locales
82
+ locales = Data::Storage.WindowsZones.keys if locales.empty?
83
+ locales.each do |locale|
84
+ next unless Data::Storage.WindowsZones.has_key?(locale)
85
+ @Valid = Data::Storage.WindowsZones[locale].has_key?(@Name)
86
+ return @Valid if @Valid
87
+ end
88
+ end
89
+ @Valid = false
90
+ end
91
+
92
+ # Windows Timezone data
93
+ # @return [WindowsData] data
94
+ def getData
95
+ unless @Loaded
96
+ @Loaded = true
97
+ @Valid = false
98
+ locales = @Locales
99
+ locales = Data::Storage.WindowsZones.keys if locales.empty?
100
+ locales.each do |locale|
101
+ next unless Data::Storage.WindowsZones.has_key?(locale)
102
+ entry = Data::Storage.WindowsZones[locale][@Name]
103
+ if entry
104
+ @Data.processEntry(entry, @Regions)
105
+ @Valid = true
106
+ return @Data unless @All
107
+ end
108
+ end
109
+ end
110
+ @Data
111
+ end
112
+
113
+ # Windows Timezone identifier
114
+ # @return [String] Timezone identifier
115
+ def getZone
116
+ getData.WindowsZone
117
+ end
118
+
119
+ # Check if given Windows Timezone name is a valid timezone
120
+ # @param name [String] Windows Timezone name
121
+ # @param locales [Array<String>] search Timezone name only for these locales
122
+ # @return [Boolean] whether Timezone is valid
123
+ # @see Locales
124
+ def self.isValid?(name, locales = nil)
125
+ self.new(name).set(locales).isValid?
126
+ end
127
+
128
+ # Get UTC offsets in seconds for given Windows Timezone name
129
+ # @param name [String] Windows Timezone name
130
+ # @param locales [Array<String>] search Timezone name only for these locales
131
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
132
+ # @return [Array<Fixnum>] list of timezone offsets in seconds
133
+ # @see Locales
134
+ def self.getOffsets(name, locales = nil, all = true)
135
+ self.new(name).set(locales, nil, all).getOffsets
136
+ end
137
+
138
+ # Get Timezone identifiers for given Windows Timezone name
139
+ # @param name [String] Windows Timezone name
140
+ # @param locales [Array<String>] search Timezone name only for these locales
141
+ # @param regions [Array<String>] look for timezones only for these regions
142
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
143
+ # @return [Array<String>] list of timezone identifiers
144
+ # @see Locales
145
+ # @see Regions
146
+ def self.getTimezones(name, locales = nil, regions = nil, all = true)
147
+ self.new(name).set(locales, regions, all).getTimezones
148
+ end
149
+
150
+ # Get Metazone identifiers for given Windows Timezone name
151
+ # @param name [String] Windows Timezone name
152
+ # @param locales [Array<String>] search Timezone name only for these locales
153
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
154
+ # @return [Array<String>] list of metazone identifiers
155
+ # @see Locales
156
+ def self.getMetazones(name, locales = nil, all = true)
157
+ self.new(name).set(locales, nil, all).getMetazones
158
+ end
159
+
160
+ # Windows Timezone identifier
161
+ # @param name [String] Windows Timezone name
162
+ # @param locales [Array<String>] search Timezone name only for these locales
163
+ # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
164
+ # @return [String] Timezone identifier
165
+ def self.getZone(name, locales = nil, all = true)
166
+ self.new(name).set(locales, nil, all).getZone
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+
3
+ module TimezoneParser
4
+ # Generic Timezone class
5
+ class ZoneInfo
6
+ protected
7
+ @Data = nil
8
+ @Loaded = false
9
+ @Offsets = nil
10
+ @Timezones = nil
11
+ @Metazones = nil
12
+
13
+ public
14
+ attr_accessor :ToTime
15
+ attr_accessor :FromTime
16
+ # Set time range
17
+ # @param toTime [DateTime] filter timezones before this date, exclusive
18
+ # @param fromTime [DateTime] filter timezones at this date, inclusive
19
+ # @return [ZoneInfo] self
20
+ def setTime(toTime = nil, fromTime = nil)
21
+ @ToTime = toTime
22
+ @ToTime = DateTime.now unless @ToTime
23
+ @FromTime = fromTime
24
+ @FromTime = DateTime.new(@ToTime.year - 1) unless @FromTime
25
+ self
26
+ end
27
+
28
+ # Get Timezone data
29
+ def getData
30
+ raise StandardError, '#getData must be implemented in subclass'
31
+ end
32
+
33
+ # Get UTC offsets in seconds
34
+ # @return [Array<Fixnum>] list of timezone offsets in seconds
35
+ def getOffsets
36
+ unless @Offsets
37
+ @Offsets = getData.Offsets.to_a
38
+ end
39
+ @Offsets
40
+ end
41
+
42
+ # Get Timezone identifiers
43
+ # @return [Array<String>] list of timezone identifiers
44
+ def getTimezones
45
+ unless @Timezones
46
+ @Timezones = getData.Timezones.to_a
47
+ end
48
+ @Timezones
49
+ end
50
+
51
+ # Get types
52
+ # @return [Symbol] types
53
+ def getTypes
54
+ unless @Types
55
+ @Types = getData.Types.to_a
56
+ end
57
+ @Types
58
+ end
59
+
60
+ # Get Metazone identifiers
61
+ # @return [Array<String>] list of Metazone identifiers
62
+ def getMetazones
63
+ unless @Metazones
64
+ @Metazones = getData.Metazones.to_a
65
+ end
66
+ @Metazones
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,122 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe TimezoneParser do
5
+ describe TimezoneParser::Abbreviation do
6
+ describe '#new' do
7
+ it 'should be an instance of a Abbreviation class' do
8
+ expect(TimezoneParser::Abbreviation.new('CET')).to be_an_instance_of TimezoneParser::Abbreviation
9
+ end
10
+ end
11
+
12
+ describe '#isValid?' do
13
+ it 'should be valid abbreviation' do
14
+ expect(TimezoneParser::Abbreviation.new('CET').isValid?).to be_true
15
+ end
16
+
17
+ it 'should not be valid abbreviation' do
18
+ expect(TimezoneParser::Abbreviation.new('LOL').isValid?).to be_false
19
+ end
20
+
21
+ it 'should be valid case-insensitive abbreviation' do
22
+ expect(TimezoneParser::Abbreviation.new('Pst').isValid?).to be_true
23
+ end
24
+ end
25
+
26
+ describe '#getOffsets' do
27
+ it 'should return offsets for CET abbreviation' do
28
+ expect(TimezoneParser::Abbreviation.new('CET').getOffsets).to eq([3600])
29
+ end
30
+
31
+ it 'should return offsets for WEZ abbreviation' do
32
+ expect(TimezoneParser::Abbreviation.new('WEZ').getOffsets).to eq([0, 3600])
33
+ end
34
+
35
+ context 'before specified time' do
36
+ it 'should return correct offsets for ADT' do
37
+ expect(TimezoneParser::Abbreviation.new('ADT').setTime(DateTime.parse('1982-04-30T21:00:00+00:00')).getOffsets).to eq([-10800])
38
+ expect(TimezoneParser::Abbreviation.new('ADT').setTime(DateTime.parse('1982-04-30T21:00:01+00:00')).getOffsets).to eq([-10800, 14400])
39
+ expect(TimezoneParser::Abbreviation.new('ADT').setTime(DateTime.parse('1982-09-30T19:59:59+00:00')).getOffsets).to eq([-10800, 14400])
40
+ expect(TimezoneParser::Abbreviation.new('ADT').setTime(DateTime.parse('1982-09-30T20:00:00+00:00')).getOffsets).to eq([-10800, 14400])
41
+ expect(TimezoneParser::Abbreviation.new('ADT').setTime(DateTime.parse('1983-03-30T21:00:00+00:00'), DateTime.parse('1982-09-30T20:00:00+00:00')).getOffsets).to eq([-10800])
42
+ end
43
+ end
44
+
45
+ context 'between specified time' do
46
+ it 'should return correct offsets for SAST' do
47
+ expect(TimezoneParser::Abbreviation.new('SAST').setTime(DateTime.parse('1943-09-19T00:00:00+00:00'), DateTime.parse('1943-03-20T23:00:00+00:00')).getOffsets).to eq([7200])
48
+ expect(TimezoneParser::Abbreviation.new('SAST').setTime(DateTime.parse('1943-09-19T00:00:00+00:00'), DateTime.parse('1943-03-20T22:59:59+00:00')).getOffsets).to eq([7200, 10800])
49
+ expect(TimezoneParser::Abbreviation.new('SAST').setTime(DateTime.parse('1943-09-19T00:00:01+00:00'), DateTime.parse('1943-03-20T23:00:00+00:00')).getOffsets).to eq([7200, 10800])
50
+ expect(TimezoneParser::Abbreviation.new('SAST').setTime(DateTime.parse('1943-09-19T00:00:01+00:00'), DateTime.parse('1943-09-19T00:00:00+00:00')).getOffsets).to eq([7200, 10800])
51
+ expect(TimezoneParser::Abbreviation.new('SAST').setTime(DateTime.parse('1944-03-18T23:00:00+00:00'), DateTime.parse('1944-03-18T22:59:59+00:00')).getOffsets).to eq([7200, 10800])
52
+ expect(TimezoneParser::Abbreviation.new('SAST').setTime(DateTime.now, DateTime.parse('1944-03-18T23:00:00+00:00')).getOffsets).to eq([7200])
53
+ end
54
+ end
55
+
56
+ context 'in specified region' do
57
+ it 'should return correct offsets for ADT' do
58
+ expect(TimezoneParser::Abbreviation.new('ADT').set([]).getOffsets).to eq([-10800])
59
+ expect(TimezoneParser::Abbreviation.new('ADT').set(['IQ']).getOffsets).to eq([14400])
60
+ expect(TimezoneParser::Abbreviation.new('ADT').set(['GL']).getOffsets).to eq([-10800])
61
+ expect(TimezoneParser::Abbreviation.new('ADT').setTime(DateTime.parse('2007-10-01T00:00:00+00:00')).set(['IQ', 'CA']).getOffsets).to eq([-10800, 14400])
62
+ end
63
+ end
64
+
65
+ context 'with specified type' do
66
+ it 'should return correct offsets for HAT' do
67
+ expect(TimezoneParser::Abbreviation.new('HAT').setTime(DateTime.parse('2014-02-05T10:00:00+00:00')).set(nil).getOffsets).to eq([-36000, -32400, -12600, -9000])
68
+ expect(TimezoneParser::Abbreviation.new('HAT').setTime(DateTime.parse('2014-04-05T10:00:00+00:00')).set(nil).getOffsets).to eq([-36000, -32400, -12600, -9000])
69
+ expect(TimezoneParser::Abbreviation.new('HAT').setTime(DateTime.parse('2014-02-05T10:00:00+00:00')).set(nil, :standard ).getOffsets).to eq([-36000, -12600])
70
+ expect(TimezoneParser::Abbreviation.new('HAT').setTime(DateTime.parse('2014-02-05T10:00:00+00:00')).set(nil, :daylight ).getOffsets).to eq([-32400, -9000])
71
+ expect(TimezoneParser::Abbreviation.new('HAT').setTime(DateTime.parse('2014-04-05T10:00:00+00:00')).set(nil, :standard ).getOffsets).to eq([-36000, -12600])
72
+ expect(TimezoneParser::Abbreviation.new('HAT').setTime(DateTime.parse('2014-04-05T10:00:00+00:00')).set(nil, :daylight ).getOffsets).to eq([-32400, -9000])
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#getTimezones' do
78
+ it 'should return all timezones for KMT abbreviation' do
79
+ expect(TimezoneParser::Abbreviation.new('KMT').getTimezones).to eq(['Europe/Kiev'])
80
+ end
81
+
82
+ context 'between specified time' do
83
+ it 'should include correct timezones for EET' do
84
+ expect(TimezoneParser::Abbreviation.new('EET').setTime(DateTime.parse('1919-04-15T00:00:00+00:00'), DateTime.parse('1918-09-16T01:00:00+00:00')).getTimezones).to include('Asia/Beirut', 'Europe/Istanbul', 'Europe/Warsaw')
85
+ expect(TimezoneParser::Abbreviation.new('EET').setTime(DateTime.parse('1919-09-16T00:00:00+00:00'), DateTime.parse('1919-04-15T00:00:00+00:00')).getTimezones).to_not include('Europe/Warsaw')
86
+ expect(TimezoneParser::Abbreviation.new('EET').setTime(DateTime.parse('1985-04-19T21:00:00+00:00'), DateTime.parse('1978-10-14T21:00:00+00:00')).getTimezones).to_not include('Europe/Istanbul')
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#getMetazones' do
92
+ it 'should return all metazones for HAT abbreviation' do
93
+ expect(TimezoneParser::Abbreviation.new('HAT').getMetazones).to eq(['Hawaii_Aleutian', 'Newfoundland'])
94
+ end
95
+ end
96
+
97
+ describe '.isValid?' do
98
+ it 'should be valid abbreviation' do
99
+ expect(TimezoneParser::Abbreviation::isValid?('WGT')).to be_true
100
+ end
101
+ end
102
+
103
+ describe '.getOffsets' do
104
+ it 'should return offsets for WGT abbreviation in GL region' do
105
+ expect(TimezoneParser::Abbreviation::getOffsets('WGT', DateTime.now, nil, ['GL'])).to eq([-10800])
106
+ end
107
+ end
108
+
109
+ describe '.getTimezones' do
110
+ it 'should return timezones for WGT abbreviation' do
111
+ expect(TimezoneParser::Abbreviation::getTimezones('WGT', DateTime.parse('1916-07-28T03:26:56+00:00'), DateTime.parse('1916-07-28T01:14:40+00:00'), ['GL'])).to eq(['America/Danmarkshavn'])
112
+ end
113
+ end
114
+
115
+ describe '.getMetazones' do
116
+ it 'should return metazones for HKT abbreviation' do
117
+ expect(TimezoneParser::Abbreviation::getMetazones('HKT')).to eq(['Hong_Kong'])
118
+ end
119
+ end
120
+
121
+ end
122
+ end