TimezoneParser 0.4.0 → 1.0.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.
@@ -15,6 +15,7 @@ module TimezoneParser
15
15
 
16
16
  protected
17
17
  @@Version = nil
18
+ @@Timezones = nil
18
19
  @@TimezoneCountries = nil
19
20
 
20
21
  public
@@ -57,6 +58,13 @@ module TimezoneParser
57
58
  ::TZInfo::DataSource.set(:ruby, TZInfoData)
58
59
  end
59
60
 
61
+ def self.getTimezones
62
+ unless @@Timezones
63
+ @@Timezones = ::TZInfo::Timezone.all_identifiers
64
+ end
65
+ @@Timezones
66
+ end
67
+
60
68
  def self.getTimezoneCountries
61
69
  unless @@TimezoneCountries
62
70
  @@TimezoneCountries = {}
@@ -1,34 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module TimezoneParser
4
- # Rails zone data
5
- class RailsData < Data
6
- protected
7
- @RailsZone = nil
8
-
9
- public
10
- attr_reader :RailsZone
11
- def processEntry(data, rails)
12
- if rails
13
- @RailsZone = rails
14
- @Metazones << rails
15
- @Timezones << data
16
- else
17
- rails = Storage.RailsZones[data]
18
- if rails
19
- @RailsZone = data
20
- @Metazones << data
21
- @Timezones << rails
22
- end
23
- end
24
- self
25
- end
26
- end
27
-
28
4
  # Rails zone
29
5
  class RailsZone < ZoneInfo
30
6
  protected
31
7
  @@Locales = []
8
+ @@Regions = []
32
9
 
33
10
  public
34
11
  # Locales which will be used for RailsZone methods if not specified there
@@ -40,26 +17,38 @@ module TimezoneParser
40
17
  @@Locales
41
18
  end
42
19
 
43
- attr_accessor :All
20
+ # Regions which will be used for WindowsZone methods if not specified there
21
+ #
22
+ # Each region is either ISO 3166-1 alpha-2 code
23
+ # @return [Array<String>] list containing region identifiers
24
+ # @see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
25
+ def self.Regions
26
+ @@Regions
27
+ end
28
+
29
+ attr_accessor :Locales
30
+ attr_accessor :Regions
31
+ attr_accessor :Types
44
32
 
45
33
  # Rails zone instance
46
34
  # @param name [String] Rails zone name
47
35
  def initialize(name)
48
36
  @Name = name
49
- @Data = RailsData.new
50
37
  @Valid = nil
51
38
  setTime
52
- set(@@Locales.dup, true)
39
+ set(@@Locales.dup, @@Regions.dup)
53
40
  end
54
41
 
55
- # Set locales and all
42
+ # Set locales and regions
56
43
  # @param locales [Array<String>] search only in these locales
57
- # @param all [Boolean] specify whether should search for all zones or return as soon as found any
58
- # @return [RailsZone] self
44
+ # @param regions [Array<String>] filter for these regions
45
+ # @return [WindowsZone] self
59
46
  # @see Locales
60
- def set(locales = nil, all = true)
47
+ # @see Regions
48
+ def set(locales = nil, regions = nil, types = nil)
61
49
  @Locales = locales unless locales.nil?
62
- @All = all ? true : false
50
+ @Regions = regions unless regions.nil?
51
+ @Types = types unless types.nil?
63
52
  self
64
53
  end
65
54
 
@@ -67,61 +56,31 @@ module TimezoneParser
67
56
  # @return [Boolean] whether Rails zone is valid
68
57
  def isValid?
69
58
  if @Valid.nil?
70
- @Valid = false
71
- @Valid = Data::Storage.RailsZones.has_key?(@Name) if (not @Locales) or (@Locales and @Locales.include?('en'))
72
- return @Valid if @Valid
73
- locales = @Locales
74
- locales = Data::Storage.RailsTranslated.keys if locales.empty?
75
- locales.each do |locale|
76
- next unless Data::Storage.RailsTranslated.has_key?(locale)
77
- @Valid = Data::Storage.RailsTranslated[locale].has_key?(@Name)
78
- return @Valid if @Valid
59
+ params = []
60
+ joins = ''
61
+ where = ''
62
+
63
+ if not @Locales.empty?
64
+ joins += ' LEFT JOIN `Locales` AS L ON RI.Locale = L.ID'
65
+ where = 'L.Name COLLATE NOCASE IN (' + Array.new(@Locales.count, '?').join(',') + ') AND '
66
+ params += @Locales
79
67
  end
80
- end
81
- @Valid = false
82
- end
83
68
 
84
- # Rails zone data
85
- # @return [RailsData] data
86
- def getData
87
- unless @Loaded
88
- @Loaded = true
89
- @Valid = false
90
- @Valid = Data::Storage.RailsZones.has_key?(@Name) if (not @Locales) or (@Locales and @Locales.include?('en'))
91
- if @Valid
92
- @Data.processEntry(Data::Storage.RailsZones[@Name], @Name)
93
- return @Data unless @All
94
- end
95
- locales = @Locales
96
- locales = Data::Storage.RailsTranslated.keys if locales.empty?
97
- locales.each do |locale|
98
- next unless Data::Storage.RailsTranslated.has_key?(locale)
99
- entry = Data::Storage.RailsTranslated[locale][@Name]
100
- if entry
101
- @Data.processEntry(entry, false)
102
- @Valid = true
103
- return @Data unless @All
104
- end
105
- end
106
- end
107
- @Data
108
- end
69
+ sql = "SELECT 1 FROM `RailsI18N` RI #{joins} WHERE #{where}RI.`NameLowercase` = ? LIMIT 1"
70
+ params << @Name.downcase
109
71
 
110
- # Get UTC offsets in seconds
111
- # @return [Array<Fixnum>] list of timezone offsets in seconds
112
- def getOffsets
113
- if not @Offsets and not getTimezones.empty?
114
- @Offsets = @Data.findOffsets(@ToTime, @FromTime).to_a
115
- else
116
- super
72
+ @Valid = Data::Storage.getStatement(sql).execute(*params).count > 0
117
73
  end
118
- @Offsets
74
+ @Valid
119
75
  end
120
76
 
121
77
  # Rails zone identifier
122
78
  # @return [String] Rails zone identifier
123
79
  def getZone
124
- getData.RailsZone
80
+ unless @Zone
81
+ @Zone = self.getFilteredData(:Zone).first
82
+ end
83
+ @Zone
125
84
  end
126
85
 
127
86
  # Check if given Rails zone name is a valid timezone
@@ -138,41 +97,93 @@ module TimezoneParser
138
97
  # @param toTime [DateTime] look for offsets which came into effect before this date, exclusive
139
98
  # @param fromTime [DateTime] look for offsets which came into effect at this date, inclusive
140
99
  # @param locales [Array<String>] search zone name only for these locales
141
- # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
142
100
  # @return [Array<Fixnum>] list of timezone offsets in seconds
143
101
  # @see Locales
144
- def self.getOffsets(name, toTime = nil, fromTime = nil, locales = nil, all = true)
145
- self.new(name).setTime(toTime, fromTime).set(locales, all).getOffsets
102
+ def self.getOffsets(name, toTime = nil, fromTime = nil, locales = nil, types = nil)
103
+ self.new(name).setTime(toTime, fromTime).set(locales, nil, types).getOffsets
146
104
  end
147
105
 
148
106
  # Get Timezone identifiers for given Rails zone name
149
107
  # @param name [String] Rails zone name
150
108
  # @param locales [Array<String>] search zone name only for these locales
151
- # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
152
109
  # @return [Array<String>] list of timezone identifiers
153
110
  # @see Locales
154
- def self.getTimezones(name, locales = nil, all = true)
155
- self.new(name).set(locales, all).getTimezones
111
+ def self.getTimezones(name, locales = nil)
112
+ self.new(name).set(locales).getTimezones
156
113
  end
157
114
 
158
115
  # Get Metazone identifiers for given Rails zone name
159
116
  # @param name [String] Rails zone name
160
117
  # @param locales [Array<String>] search zone name only for these locales
161
- # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
162
118
  # @return [Array<String>] list of metazone identifiers
163
119
  # @see Locales
164
120
  # @see Regions
165
- def self.getMetazones(name, locales = nil, all = true)
166
- self.new(name).set(locales, all).getMetazones
121
+ def self.getMetazones(name, locales = nil)
122
+ self.new(name).set(locales).getMetazones
167
123
  end
168
124
 
169
125
  # Rails zone identifier
170
126
  # @param name [String] Rails zone name
171
127
  # @param locales [Array<String>] search zone name only for these locales
172
- # @param all [Boolean] specify whether should search for all timezones or return as soon as found any
173
128
  # @return [String] Timezone identifier
174
- def self.getZone(name, locales = nil, all = true)
175
- self.new(name).set(locales, all).getZone
129
+ def self.getZone(name, locales = nil)
130
+ self.new(name).set(locales).getZone
131
+ end
132
+
133
+
134
+ protected
135
+
136
+ def getFilteredData(dataType)
137
+ params = []
138
+ column = nil
139
+ joins = ''
140
+ regionJoins = ''
141
+ useRegionFilter = !@Regions.nil? && !@Regions.empty?
142
+ case dataType
143
+ when :Zone
144
+ column = '`RailsTimezones`.`Name`'
145
+ joins += ' INNER JOIN `RailsTimezones` ON RailsTimezones.ID = RI.Zone'
146
+ when :Offsets, :Timezones
147
+ column = '`Timezones`.`Name`'
148
+ joins += ' INNER JOIN `RailsTimezones` ON RailsTimezones.ID = RI.Zone'
149
+ joins += ' INNER JOIN `Timezones` ON RailsTimezones.Timezone = Timezones.ID'
150
+ regionJoins += ' LEFT JOIN `TimezoneTerritories` ON TimezoneTerritories.Timezone = RailsTimezones.Timezone'
151
+ regionJoins += ' LEFT JOIN `Territories` ON TimezoneTerritories.Territory = Territories.ID'
152
+ when :Metazones
153
+ raise StandardError, "Metazones is not implemented!"
154
+ when :Types
155
+ raise StandardError, "Types is not implemented!"
156
+ else
157
+ raise StandardError, "Unkown dataType '#{dataType}'"
158
+ end
159
+
160
+ if not @Locales.empty?
161
+ joins += ' LEFT JOIN `Locales` AS L ON RI.Locale = L.ID'
162
+ where = 'L.Name COLLATE NOCASE IN (' + Array.new(@Locales.count, '?').join(',') + ') AND '
163
+ params += @Locales
164
+ end
165
+
166
+ sql = 'SELECT DISTINCT ' + column + ' FROM `RailsI18N` AS RI'
167
+ sql += joins
168
+ if useRegionFilter
169
+ sql += regionJoins
170
+ end
171
+
172
+ sql += " WHERE #{where}RI.NameLowercase = ?"
173
+ params << @Name.downcase
174
+
175
+ if useRegionFilter
176
+ sql += ' AND Territories.Territory IN (' + Array.new(@Regions.count, '?').join(',') + ')'
177
+ params += @Regions
178
+ end
179
+ sql += ' ORDER BY ' + column
180
+
181
+ result = Data::Storage.getStatement(sql).execute(*params).collect { |row| row.first }
182
+ if dataType == :Offsets
183
+ result = self.class.findOffsets(result, @ToTime, @FromTime, @Types)
184
+ end
185
+ result
176
186
  end
187
+
177
188
  end
178
189
  end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'set'
4
+
3
5
  module TimezoneParser
4
6
  # Timezone
5
7
  class Timezone < ZoneInfo
@@ -31,27 +33,23 @@ module TimezoneParser
31
33
 
32
34
  attr_accessor :Locales
33
35
  attr_accessor :Regions
34
- attr_accessor :All
35
36
 
36
37
  # Timezone instance
37
38
  # @param timezone [String] Timezone name
38
39
  def initialize(timezone)
39
40
  @Timezone = timezone
40
- @Data = Data.new
41
41
  @Valid = nil
42
42
  setTime
43
- set(@@Locales.dup, @@Regions.dup, true)
43
+ set(@@Locales.dup, @@Regions.dup)
44
44
  end
45
45
 
46
- # Set locales, regions and all
46
+ # Set locales and regions
47
47
  # @param locales [Array<String>] search only in these locales
48
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
49
  # @return [Timezone] self
51
- def set(locales = nil, regions = nil, all = true)
50
+ def set(locales = nil, regions = nil)
52
51
  @Locales = locales unless locales.nil?
53
52
  @Regions = regions unless regions.nil?
54
- @All = all ? true : false
55
53
  self
56
54
  end
57
55
 
@@ -59,50 +57,22 @@ module TimezoneParser
59
57
  # @return [Boolean] whether timezone is valid
60
58
  def isValid?
61
59
  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
60
+ params = []
61
+ joins = ''
62
+ where = ''
74
63
 
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
64
+ if not @Locales.empty?
65
+ joins += ' LEFT JOIN `Locales` AS L ON TN.Locale = L.ID'
66
+ where = 'L.Name COLLATE NOCASE IN (' + Array.new(@Locales.count, '?').join(',') + ') AND '
67
+ params += @Locales
91
68
  end
92
- end
93
- @Data
94
- end
95
69
 
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
70
+ sql = "SELECT 1 FROM `TimezoneNames` TN #{joins} WHERE #{where}TN.NameLowercase = ? COLLATE NOCASE LIMIT 1"
71
+ params << @Timezone.downcase
72
+
73
+ @Valid = Data::Storage.getStatement(sql).execute(*params).count > 0
104
74
  end
105
- @Offsets
75
+ @Valid
106
76
  end
107
77
 
108
78
  # Check if given Timezone name is a valid timezone
@@ -120,12 +90,11 @@ module TimezoneParser
120
90
  # @param fromTime [DateTime] look for offsets which came into effect at this date, inclusive
121
91
  # @param locales [Array<String>] search Timezone name only for these locales
122
92
  # @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
93
  # @return [Array<Fixnum>] list of timezone offsets in seconds
125
94
  # @see Locales
126
95
  # @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
96
+ def self.getOffsets(timezone, toTime = nil, fromTime = nil, locales = nil, regions = nil)
97
+ self.new(timezone).setTime(toTime, fromTime).set(locales, regions).getOffsets
129
98
  end
130
99
 
131
100
  # Get Timezone identifiers for given Timezone name
@@ -134,12 +103,11 @@ module TimezoneParser
134
103
  # @param fromTime [DateTime] look for timezones which came into effect at this date, inclusive
135
104
  # @param locales [Array<String>] search Timezone name only for these locales
136
105
  # @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
106
  # @return [Array<String>] list of timezone identifiers
139
107
  # @see Locales
140
108
  # @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
109
+ def self.getTimezones(timezone, toTime = nil, fromTime = nil, locales = nil, regions = nil)
110
+ self.new(timezone).setTime(toTime, fromTime).set(locales, regions).getTimezones
143
111
  end
144
112
 
145
113
  # Get Metazone identifiers for given Timezone name
@@ -148,12 +116,94 @@ module TimezoneParser
148
116
  # @param fromTime [DateTime] look for timezones which came into effect at this date, inclusive
149
117
  # @param locales [Array<String>] search Timezone name only for these locales
150
118
  # @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
119
  # @return [Array<String>] list of metazone identifiers
153
120
  # @see Locales
154
121
  # @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
122
+ def self.getMetazones(timezone, toTime = nil, fromTime = nil, locales = nil, regions = nil)
123
+ self.new(timezone).setTime(toTime, fromTime).set(locales, regions).getMetazones
124
+ end
125
+
126
+ protected
127
+
128
+ def getFilteredData(dataType)
129
+ params = []
130
+ column = nil
131
+ joins = ''
132
+ regionJoins = ''
133
+ useTimeFilter = false
134
+ useRegionFilter = !@Regions.nil? && !@Regions.empty?
135
+ case dataType
136
+ when :Offsets, :Timezones
137
+ column = '`Timezones`.`Name`'
138
+ if dataType == :Offsets
139
+ column += ', TN.`Types`'
140
+ end
141
+ joins += ' LEFT JOIN `TimezoneName_Timezones` AS T ON T.Name = TN.ID'
142
+ joins += ' LEFT JOIN `TimezoneName_Metazones` AS M ON M.Name = TN.ID'
143
+ joins += ' LEFT JOIN `MetazonePeriods` MP ON M.Metazone = MP.Metazone'
144
+ joins += ' LEFT JOIN `MetazonePeriod_Timezones` MPT ON MPT.MetazonePeriod = MP.ID'
145
+ joins += ' INNER JOIN `Timezones` ON (T.Timezone = Timezones.ID OR MPT.Timezone = Timezones.ID)'
146
+ regionJoins += ' LEFT JOIN `TimezoneTerritories` ON (TimezoneTerritories.Timezone = T.Timezone OR TimezoneTerritories.Timezone = MPT.Timezone)'
147
+ regionJoins += ' LEFT JOIN `Territories` ON TimezoneTerritories.Territory = Territories.ID'
148
+ useTimeFilter = true
149
+ when :Metazones
150
+ column = '`Metazones`.`Name`'
151
+ joins += ' LEFT JOIN `TimezoneName_Metazones` AS M ON M.Name = TN.ID'
152
+ joins += ' INNER JOIN `Metazones` ON M.Metazone = Metazones.ID'
153
+ regionJoins += ' LEFT JOIN `TimezoneName_Timezones` AS T ON T.Name = TN.ID'
154
+ regionJoins += ' LEFT JOIN `TimezoneTerritories` ON TimezoneTerritories.Timezone = T.Timezone'
155
+ regionJoins += ' LEFT JOIN `Territories` ON TimezoneTerritories.Territory = Territories.ID'
156
+ when :Types
157
+ column = 'TN.Types'
158
+ useRegionFilter = false
159
+ else
160
+ raise StandardError, "Unkown dataType '#{dataType}'"
161
+ end
162
+
163
+ if not @Locales.empty?
164
+ joins += ' LEFT JOIN `Locales` AS L ON TN.Locale = L.ID'
165
+ where = 'L.Name COLLATE NOCASE IN (' + Array.new(@Locales.count, '?').join(',') + ') AND '
166
+ params += @Locales
167
+ end
168
+
169
+ sql = 'SELECT DISTINCT ' + column + ' FROM `TimezoneNames` AS TN'
170
+ sql += joins
171
+ if useRegionFilter
172
+ sql += regionJoins
173
+ end
174
+
175
+ sql += " WHERE #{where}TN.NameLowercase = ?"
176
+ params << @Timezone.downcase
177
+ if useTimeFilter and not @FromTime.nil?
178
+ fromsql = '((MP.`From` IS NULL AND MP.`To` > ?) OR (MP.`To` IS NULL AND MP.`From` <= ?) OR (MP.`From` <= ? AND MP.`To` > ?) OR (MP.`From` IS NULL AND MP.`To` IS NULL))'
179
+ params += Array.new(4, @FromTime.to_s)
180
+ if @ToTime.nil?
181
+ sql += ' AND ' + fromsql
182
+ end
183
+ end
184
+ if useTimeFilter and not @ToTime.nil?
185
+ tosql = '((MP.`From` IS NULL AND MP.`To` >= ?) OR (MP.`To` IS NULL AND MP.`From` < ?) OR (MP.`From` < ? AND MP.`To` >= ?) OR (MP.`From` IS NULL AND MP.`To` IS NULL))'
186
+ params += Array.new(4, @ToTime.to_s)
187
+ if not @FromTime.nil?
188
+ sql += ' AND ((' + fromsql + ') OR (' + tosql + '))'
189
+ else
190
+ sql += ' AND ' + tosql
191
+ end
192
+ end
193
+ if useRegionFilter
194
+ sql += ' AND Territories.Territory IN (' + Array.new(@Regions.count, '?').join(',') + ')'
195
+ params += @Regions
196
+ end
197
+ sql += ' ORDER BY ' + column
198
+
199
+ if dataType == :Offsets
200
+ result = self.class.findOffsetsFromTimezonesTypes(Data::Storage.getStatement(sql).execute(*params), @ToTime, @FromTime, nil)
201
+ else
202
+ result = Data::Storage.getStatement(sql).execute(*params).collect { |row| row.first }
203
+ result = self.class.convertTypes(result) if dataType == :Types
204
+ end
205
+ result
157
206
  end
207
+
158
208
  end
159
209
  end