TimezoneParser 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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