timezone 1.3.3 → 1.3.29

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.
Files changed (276) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +15 -0
  3. data/.github/workflows/ci.yml +40 -0
  4. data/.rubocop.yml +24 -5
  5. data/CHANGES.markdown +107 -0
  6. data/CONTRIBUTING.markdown +10 -6
  7. data/README.markdown +5 -7
  8. data/Rakefile +3 -1
  9. data/benchmark.rb +16 -29
  10. data/data/Africa/Accra +1 -47
  11. data/data/Africa/Addis_Ababa +5 -4
  12. data/data/Africa/Algiers +2 -2
  13. data/data/Africa/Asmara +5 -4
  14. data/data/Africa/Asmera +5 -4
  15. data/data/Africa/Bangui +4 -1
  16. data/data/Africa/Blantyre +1 -1
  17. data/data/Africa/Brazzaville +4 -1
  18. data/data/Africa/Bujumbura +1 -1
  19. data/data/Africa/Cairo +32 -0
  20. data/data/Africa/Casablanca +8 -6
  21. data/data/Africa/Dar_es_Salaam +5 -4
  22. data/data/Africa/Djibouti +5 -4
  23. data/data/Africa/Douala +4 -1
  24. data/data/Africa/El_Aaiun +8 -6
  25. data/data/Africa/Gaborone +1 -1
  26. data/data/Africa/Harare +1 -1
  27. data/data/Africa/Juba +2 -1
  28. data/data/Africa/Kampala +5 -4
  29. data/data/Africa/Kigali +1 -1
  30. data/data/Africa/Kinshasa +4 -1
  31. data/data/Africa/Lagos +4 -1
  32. data/data/Africa/Libreville +4 -1
  33. data/data/Africa/Luanda +4 -1
  34. data/data/Africa/Lubumbashi +1 -1
  35. data/data/Africa/Lusaka +1 -1
  36. data/data/Africa/Malabo +4 -1
  37. data/data/Africa/Maputo +1 -1
  38. data/data/Africa/Mogadishu +5 -4
  39. data/data/Africa/Nairobi +5 -4
  40. data/data/Africa/Niamey +4 -1
  41. data/data/Africa/Porto-Novo +4 -1
  42. data/data/America/Anguilla +4 -1
  43. data/data/America/Antigua +4 -1
  44. data/data/America/Aruba +4 -2
  45. data/data/America/Asuncion +2 -29
  46. data/data/America/Atikokan +2 -7
  47. data/data/America/Bahia_Banderas +5 -39
  48. data/data/America/Barbados +8 -3
  49. data/data/America/Belize +44 -1
  50. data/data/America/Blanc-Sablon +3 -5
  51. data/data/America/Bogota +1 -1
  52. data/data/America/Cambridge_Bay +17 -3
  53. data/data/America/Campo_Grande +1 -40
  54. data/data/America/Cancun +7 -6
  55. data/data/America/Chihuahua +5 -37
  56. data/data/America/Ciudad_Juarez +94 -0
  57. data/data/America/Coral_Harbour +2 -7
  58. data/data/America/Coyhaique +134 -0
  59. data/data/America/Creston +11 -3
  60. data/data/America/Cuiaba +1 -40
  61. data/data/America/Curacao +4 -2
  62. data/data/America/Dawson +2 -38
  63. data/data/America/Detroit +5 -1
  64. data/data/America/Dominica +4 -1
  65. data/data/America/Edmonton +1 -5
  66. data/data/America/Ensenada +13 -7
  67. data/data/America/Godthab +31 -32
  68. data/data/America/Grand_Turk +1 -2
  69. data/data/America/Grenada +4 -1
  70. data/data/America/Guadeloupe +4 -1
  71. data/data/America/Guyana +4 -3
  72. data/data/America/Hermosillo +5 -7
  73. data/data/America/Indiana/Tell_City +9 -12
  74. data/data/America/Inuvik +17 -4
  75. data/data/America/Iqaluit +17 -3
  76. data/data/America/Kentucky/Louisville +4 -4
  77. data/data/America/Kralendijk +4 -2
  78. data/data/America/Louisville +4 -4
  79. data/data/America/Lower_Princes +4 -2
  80. data/data/America/Marigot +4 -1
  81. data/data/America/Matamoros +1 -1
  82. data/data/America/Mazatlan +5 -39
  83. data/data/America/Merida +3 -35
  84. data/data/America/Metlakatla +2 -1
  85. data/data/America/Mexico_City +4 -36
  86. data/data/America/Miquelon +2 -2
  87. data/data/America/Monterrey +6 -33
  88. data/data/America/Montreal +2 -2
  89. data/data/America/Montserrat +4 -1
  90. data/data/America/Nassau +86 -2
  91. data/data/America/Nipigon +103 -5
  92. data/data/America/Nuuk +119 -0
  93. data/data/America/Ojinaga +38 -38
  94. data/data/America/Pangnirtung +51 -38
  95. data/data/America/Port_of_Spain +4 -1
  96. data/data/America/Punta_Arenas +8 -7
  97. data/data/America/Rainy_River +118 -67
  98. data/data/America/Rankin_Inlet +17 -3
  99. data/data/America/Resolute +17 -3
  100. data/data/America/Santa_Isabel +13 -7
  101. data/data/America/Santiago +11 -11
  102. data/data/America/Sao_Paulo +1 -40
  103. data/data/America/Scoresbysund +30 -30
  104. data/data/America/St_Barthelemy +4 -1
  105. data/data/America/St_Kitts +4 -1
  106. data/data/America/St_Lucia +4 -1
  107. data/data/America/St_Thomas +4 -1
  108. data/data/America/St_Vincent +4 -1
  109. data/data/America/Thunder_Bay +99 -5
  110. data/data/America/Tijuana +13 -7
  111. data/data/America/Toronto +2 -2
  112. data/data/America/Tortola +4 -1
  113. data/data/America/Vancouver +2 -2
  114. data/data/America/Virgin +4 -1
  115. data/data/America/Whitehorse +4 -40
  116. data/data/America/Yellowknife +33 -5
  117. data/data/Antarctica/Casey +10 -0
  118. data/data/Antarctica/DumontDUrville +2 -3
  119. data/data/Antarctica/Macquarie +58 -3
  120. data/data/Antarctica/Syowa +1 -1
  121. data/data/Antarctica/Vostok +4 -1
  122. data/data/Arctic/Longyearbyen +23 -21
  123. data/data/Asia/Almaty +2 -1
  124. data/data/Asia/Amman +3 -35
  125. data/data/Asia/Brunei +18 -2
  126. data/data/Asia/Choibalsan +46 -47
  127. data/data/Asia/Chongqing +3 -1
  128. data/data/Asia/Chungking +3 -1
  129. data/data/Asia/Damascus +1 -33
  130. data/data/Asia/Dili +2 -2
  131. data/data/Asia/Gaza +46 -40
  132. data/data/Asia/Harbin +3 -1
  133. data/data/Asia/Hebron +46 -40
  134. data/data/Asia/Ho_Chi_Minh +4 -4
  135. data/data/Asia/Hong_Kong +19 -19
  136. data/data/Asia/Istanbul +22 -36
  137. data/data/Asia/Jerusalem +37 -31
  138. data/data/Asia/Kuala_Lumpur +3 -3
  139. data/data/Asia/Manila +14 -10
  140. data/data/Asia/Qostanay +2 -1
  141. data/data/Asia/Saigon +4 -4
  142. data/data/Asia/Seoul +9 -1
  143. data/data/Asia/Shanghai +3 -1
  144. data/data/Asia/Singapore +1 -1
  145. data/data/Asia/Tehran +8 -39
  146. data/data/Asia/Tel_Aviv +37 -31
  147. data/data/Atlantic/Azores +23 -28
  148. data/data/Atlantic/Bermuda +29 -2
  149. data/data/Atlantic/Jan_Mayen +23 -21
  150. data/data/Atlantic/Madeira +21 -28
  151. data/data/Atlantic/Reykjavik +1 -68
  152. data/data/Australia/ACT +9 -9
  153. data/data/Australia/Adelaide +9 -9
  154. data/data/Australia/Brisbane +9 -9
  155. data/data/Australia/Broken_Hill +9 -9
  156. data/data/Australia/Canberra +9 -9
  157. data/data/Australia/Currie +22 -10
  158. data/data/Australia/Darwin +8 -8
  159. data/data/Australia/Eucla +7 -7
  160. data/data/Australia/Hobart +12 -8
  161. data/data/Australia/Lindeman +9 -9
  162. data/data/Australia/Melbourne +9 -9
  163. data/data/Australia/NSW +9 -9
  164. data/data/Australia/North +8 -8
  165. data/data/Australia/Perth +7 -7
  166. data/data/Australia/Queensland +9 -9
  167. data/data/Australia/South +9 -9
  168. data/data/Australia/Sydney +9 -9
  169. data/data/Australia/Tasmania +12 -8
  170. data/data/Australia/Victoria +9 -9
  171. data/data/Australia/West +7 -7
  172. data/data/Australia/Yancowinna +9 -9
  173. data/data/Brazil/East +1 -40
  174. data/data/CET +56 -7
  175. data/data/CST6CDT +90 -3
  176. data/data/Canada/Eastern +2 -2
  177. data/data/Canada/Mountain +1 -5
  178. data/data/Canada/Pacific +2 -2
  179. data/data/Canada/Yukon +4 -40
  180. data/data/Chile/Continental +11 -11
  181. data/data/Chile/EasterIsland +2 -2
  182. data/data/EET +24 -8
  183. data/data/EST +3 -1
  184. data/data/EST5EDT +90 -3
  185. data/data/Egypt +32 -0
  186. data/data/Eire +2 -2
  187. data/data/Etc/UCT +1 -1
  188. data/data/Europe/Amsterdam +60 -55
  189. data/data/Europe/Brussels +2 -2
  190. data/data/Europe/Budapest +24 -24
  191. data/data/Europe/Copenhagen +23 -14
  192. data/data/Europe/Dublin +2 -2
  193. data/data/Europe/Istanbul +22 -36
  194. data/data/Europe/Kaliningrad +6 -5
  195. data/data/Europe/Kiev +8 -8
  196. data/data/Europe/Kirov +44 -44
  197. data/data/Europe/Kyiv +124 -0
  198. data/data/Europe/Lisbon +25 -22
  199. data/data/Europe/Luxembourg +38 -36
  200. data/data/Europe/Monaco +9 -10
  201. data/data/Europe/Oslo +23 -21
  202. data/data/Europe/Paris +3 -3
  203. data/data/Europe/Rome +2 -2
  204. data/data/Europe/San_Marino +2 -2
  205. data/data/Europe/Simferopol +10 -10
  206. data/data/Europe/Stockholm +28 -5
  207. data/data/Europe/Uzhgorod +18 -17
  208. data/data/Europe/Vatican +2 -2
  209. data/data/Europe/Vienna +2 -2
  210. data/data/Europe/Volgograd +48 -47
  211. data/data/Europe/Zaporozhye +17 -18
  212. data/data/HST +8 -1
  213. data/data/Hongkong +19 -19
  214. data/data/Iceland +1 -68
  215. data/data/Indian/Antananarivo +5 -4
  216. data/data/Indian/Christmas +2 -1
  217. data/data/Indian/Cocos +4 -1
  218. data/data/Indian/Comoro +5 -4
  219. data/data/Indian/Kerguelen +2 -1
  220. data/data/Indian/Mahe +1 -1
  221. data/data/Indian/Mayotte +5 -4
  222. data/data/Indian/Reunion +1 -1
  223. data/data/Iran +8 -39
  224. data/data/Israel +37 -31
  225. data/data/MET +188 -139
  226. data/data/MST +12 -1
  227. data/data/MST7MDT +12 -3
  228. data/data/Mexico/BajaNorte +13 -7
  229. data/data/Mexico/BajaSur +5 -39
  230. data/data/Mexico/General +4 -36
  231. data/data/PRC +3 -1
  232. data/data/PST8PDT +39 -2
  233. data/data/Pacific/Apia +1 -36
  234. data/data/Pacific/Chuuk +2 -6
  235. data/data/Pacific/Easter +2 -2
  236. data/data/Pacific/Efate +5 -3
  237. data/data/Pacific/Enderbury +2 -2
  238. data/data/Pacific/Fiji +5 -40
  239. data/data/Pacific/Funafuti +1 -1
  240. data/data/Pacific/Kanton +4 -0
  241. data/data/Pacific/Majuro +1 -7
  242. data/data/Pacific/Niue +2 -3
  243. data/data/Pacific/Norfolk +42 -3
  244. data/data/Pacific/Pohnpei +1 -7
  245. data/data/Pacific/Ponape +1 -7
  246. data/data/Pacific/Rarotonga +3 -2
  247. data/data/Pacific/Tongatapu +3 -3
  248. data/data/Pacific/Truk +2 -6
  249. data/data/Pacific/Wake +1 -1
  250. data/data/Pacific/Wallis +1 -1
  251. data/data/Pacific/Yap +2 -6
  252. data/data/Portugal +25 -22
  253. data/data/ROK +9 -1
  254. data/data/Singapore +1 -1
  255. data/data/Turkey +22 -36
  256. data/data/UCT +1 -1
  257. data/data/US/Michigan +5 -1
  258. data/data/WET +106 -4
  259. data/lib/timezone/error.rb +6 -0
  260. data/lib/timezone/loader.rb +8 -5
  261. data/lib/timezone/lookup/geonames.rb +1 -0
  262. data/lib/timezone/lookup/google.rb +3 -2
  263. data/lib/timezone/lookup.rb +3 -2
  264. data/lib/timezone/parser.rb +33 -18
  265. data/lib/timezone/version.rb +1 -1
  266. data/lib/timezone/zone.rb +17 -17
  267. data/test/test_timezone.rb +2 -4
  268. data/test/timezone/lookup/test_geonames.rb +16 -9
  269. data/test/timezone/lookup/test_google.rb +7 -2
  270. data/test/timezone/lookup/test_test.rb +1 -0
  271. data/test/timezone/test_lookup.rb +5 -5
  272. data/test/timezone/test_zone.rb +4 -4
  273. data/timezone.gemspec +10 -5
  274. metadata +62 -16
  275. data/.travis.yml +0 -12
  276. data/data/US/Pacific-New +0 -189
data/data/US/Michigan CHANGED
@@ -5,7 +5,11 @@
5
5
  3999600:EPT:1:-14400
6
6
  81046800:EST:0:-18000
7
7
  13302000:EDT:1:-14400
8
- 775962000:EST:0:-18000
8
+ 590540460:EST:0:-18000
9
+ 11840340:EDT:1:-14400
10
+ 15728400:EST:0:-18000
11
+ 15721200:EDT:1:-14400
12
+ 142131600:EST:0:-18000
9
13
  15721200:EDT:1:-14400
10
14
  6051600:EST:0:-18000
11
15
  25398000:EDT:1:-14400
data/data/WET CHANGED
@@ -1,11 +1,76 @@
1
- 228877199:WET:0:0
2
- 15120000:WEST:1:3600
1
+ -1830384001:LMT:0:-2205
2
+ 140828400:WET:0:0
3
+ 11754000:WEST:1:3600
4
+ 10368000:WET:0:0
5
+ 19695600:WEST:1:3600
6
+ 11840400:WET:0:0
7
+ 19695600:WEST:1:3600
8
+ 11840400:WET:0:0
9
+ 19695600:WEST:1:3600
10
+ 11926800:WET:0:0
11
+ 19695600:WEST:1:3600
12
+ 11840400:WET:0:0
13
+ 19695600:WEST:1:3600
14
+ 79056000:WET:0:0
15
+ 14774400:WEST:1:3600
16
+ 48384000:WET:0:0
17
+ 14515200:WEST:1:3600
3
18
  16329600:WET:0:0
19
+ 15120000:WEST:1:3600
20
+ 16934400:WET:0:0
21
+ 15120000:WEST:1:3600
22
+ 16934400:WET:0:0
23
+ 14515200:WEST:1:3600
24
+ 48384000:WET:0:0
25
+ 14515200:WEST:1:3600
26
+ 15724800:WET:0:0
27
+ 15724800:WEST:1:3600
28
+ 47779200:WET:0:0
4
29
  15724800:WEST:1:3600
30
+ 15120000:WET:0:0
31
+ 16329600:WEST:1:3600
32
+ 16934400:WET:0:0
33
+ 14515200:WEST:1:3600
34
+ 15724800:WET:0:0
35
+ 15724800:WEST:1:3600
36
+ 15120000:WET:0:0
37
+ 16329600:WEST:1:3600
38
+ 16934400:WET:0:0
39
+ 18748800:WEST:1:3600
40
+ 8467200:WET:0:0
41
+ 19526400:WEST:1:3600
42
+ 15552000:WET:0:0
43
+ 15811200:WEST:1:3600
44
+ 13824000:WET:0:0
45
+ 3625200:WEST:1:3600
46
+ 9676800:WEMT:1:7200
47
+ 6051600:WEST:1:3600
48
+ 12096000:WET:0:0
49
+ 3020400:WEST:1:3600
50
+ 11491200:WEMT:1:7200
51
+ 5446800:WEST:1:3600
52
+ 11491200:WET:0:0
53
+ 3625200:WEST:1:3600
54
+ 10886400:WEMT:1:7200
55
+ 5446800:WEST:1:3600
56
+ 11491200:WET:0:0
57
+ 3625200:WEST:1:3600
58
+ 10886400:WEMT:1:7200
59
+ 5446800:WEST:1:3600
60
+ 13910400:WET:0:0
61
+ 15724800:WEST:1:3600
62
+ 15735600:WET:0:0
63
+ 15724800:WEST:1:3600
64
+ 15724800:WET:0:0
65
+ 15724800:WEST:1:3600
66
+ 15724800:WET:0:0
67
+ 15724800:WEST:1:3600
68
+ 15724800:WET:0:0
69
+ 15724800:WEST:1:3600
70
+ 15724800:WET:0:0
71
+ 16329600:WEST:1:3600
5
72
  15724800:WET:0:0
6
73
  15724800:WEST:1:3600
7
- 16329600:WET:0:0
8
- 15120000:WEST:1:3600
9
74
  15724800:WET:0:0
10
75
  15724800:WEST:1:3600
11
76
  15724800:WET:0:0
@@ -34,9 +99,46 @@
34
99
  15724800:WEST:1:3600
35
100
  15724800:WET:0:0
36
101
  15724800:WEST:1:3600
102
+ 315093600:CET:0:3600
103
+ 15724800:WET:0:0
104
+ 15724800:WEST:1:3600
105
+ 16333200:WET:0:0
106
+ 15724800:WEST:1:3600
37
107
  15724800:WET:0:0
38
108
  15724800:WEST:1:3600
39
109
  16329600:WET:0:0
110
+ 15120000:WEST:1:3600
111
+ 15721200:WET:0:0
112
+ 15724800:WEST:1:3600
113
+ 15724800:WET:0:0
114
+ 15724800:WEST:1:3600
115
+ 15724800:WET:0:0
116
+ 15724800:WEST:1:3600
117
+ 15724800:WET:0:0
118
+ 16329600:WEST:1:3600
119
+ 15724800:WET:0:0
120
+ 15724800:WEST:1:3600
121
+ 15728400:WET:0:0
122
+ 15724800:WEST:1:3600
123
+ 15724800:WET:0:0
124
+ 15724800:WEST:1:3600
125
+ 15724800:WET:0:0
126
+ 15724800:WEST:1:3600
127
+ 15724800:WET:0:0
128
+ 15724800:WEST:1:3600
129
+ 15724800:WET:0:0
130
+ 16329600:WEST:1:3600
131
+ 15724800:WET:0:0
132
+ 15724800:WEST:1:3600
133
+ 15724800:WET:0:0
134
+ 15724800:WEST:1:3600
135
+ 15724800:CET:0:3600
136
+ 15724800:CEST:1:7200
137
+ 15724800:CET:0:3600
138
+ 15724800:CEST:1:7200
139
+ 15724800:CET:0:3600
140
+ 15724800:CEST:1:7200
141
+ 16329600:CET:0:3600
40
142
  18144000:WEST:1:3600
41
143
  13305600:WET:0:0
42
144
  18144000:WEST:1:3600
@@ -12,16 +12,22 @@ module Timezone
12
12
  module Error
13
13
  # Top-level error. All other timezone errors subclass this one.
14
14
  class Base < StandardError; end
15
+
15
16
  # Indicates an invalid timezone name.
16
17
  class InvalidZone < Base; end
18
+
17
19
  # Indicates a lookup failure.
18
20
  class Lookup < Base; end
21
+
19
22
  # Indicates an error during lookup using the geonames API.
20
23
  class GeoNames < Lookup; end
24
+
21
25
  # Indicates an error during lookup using the google API.
22
26
  class Google < Lookup; end
27
+
23
28
  # Indicates a missing stub during a test lookup.
24
29
  class Test < Lookup; end
30
+
25
31
  # Indicates an invalid configuration.
26
32
  class InvalidConfig < Base; end
27
33
  end
@@ -2,18 +2,21 @@
2
2
 
3
3
  require 'timezone/error'
4
4
 
5
- module Timezone # rubocop:disable Style/Documentation
5
+ module Timezone
6
6
  # Responsible for loading and parsing timezone data from files.
7
7
  module Loader
8
- ZONE_FILE_PATH = File.expand_path(File.dirname(__FILE__) + '/../../data')
8
+ ZONE_FILE_PATH = File.expand_path("#{File.dirname(__FILE__)}/../../data")
9
9
  SOURCE_BIT = 0
10
10
 
11
+ @rules = {} # cache of loaded rules
12
+
11
13
  class << self
12
14
  def load(name)
13
- raise ::Timezone::Error::InvalidZone unless valid?(name)
15
+ @rules.fetch(name) do
16
+ raise ::Timezone::Error::InvalidZone unless valid?(name)
14
17
 
15
- @rules ||= {}
16
- @rules[name] ||= parse_zone_data(get_zone_data(name))
18
+ @rules[name] = parse_zone_data(get_zone_data(name))
19
+ end
17
20
  end
18
21
 
19
22
  def names
@@ -55,6 +55,7 @@ module Timezone
55
55
  return unless data['gmtOffset'].is_a? Numeric
56
56
 
57
57
  return 'Etc/UTC' if data['gmtOffset'].zero?
58
+
58
59
  "Etc/GMT#{format('%+d', -data['gmtOffset'])}"
59
60
  end
60
61
 
@@ -14,7 +14,7 @@ module Timezone
14
14
  class Google < ::Timezone::Lookup::Basic
15
15
  # Indicates that no time zone data could be found for the specified
16
16
  # <lat, lng>. This can occur if the query is incomplete or ambiguous.
17
- NO_TIMEZONE_INFORMATION = 'ZERO_RESULTS'.freeze
17
+ NO_TIMEZONE_INFORMATION = 'ZERO_RESULTS'
18
18
 
19
19
  def initialize(config)
20
20
  if config.api_key.nil?
@@ -34,7 +34,8 @@ module Timezone
34
34
  raise(Timezone::Error::Google, '403 Forbidden')
35
35
  end
36
36
 
37
- return unless response.code =~ /^2\d\d$/
37
+ return unless /^2\d\d$/.match?(response.code)
38
+
38
39
  data = JSON.parse(response.body)
39
40
 
40
41
  return if data['status'] == NO_TIMEZONE_INFORMATION
@@ -4,12 +4,13 @@ require 'timezone/lookup/geonames'
4
4
  require 'timezone/lookup/google'
5
5
  require 'timezone/lookup/test'
6
6
  require 'timezone/net_http_client'
7
+ require 'ostruct'
7
8
 
8
9
  module Timezone
9
10
  # Configure timezone lookups.
10
11
  module Lookup
11
12
  class << self
12
- MISSING_LOOKUP = 'No lookup configured'.freeze
13
+ MISSING_LOOKUP = 'No lookup configured'
13
14
  private_constant :MISSING_LOOKUP
14
15
 
15
16
  # Returns the lookup object
@@ -45,7 +46,7 @@ module Timezone
45
46
  test: ::Timezone::Lookup::Test
46
47
  }.freeze
47
48
 
48
- INVALID_LOOKUP = 'Invalid lookup specified'.freeze
49
+ INVALID_LOOKUP = 'Invalid lookup specified'
49
50
 
50
51
  attr_reader :config
51
52
 
@@ -9,30 +9,45 @@ module Timezone
9
9
  MIN_YEAR = -500
10
10
  MAX_YEAR = 2039
11
11
 
12
- LINE = /\s*(.+)\s*=\s*(.+)\s*isdst=(\d+)\s*gmtoff=([\+\-]*\d+)/
12
+ LINE = /\s*(.+)\s*=\s*(.+)\s*isdst=(\d+)\s*gmtoff=([+\-]*\d+)/.freeze
13
13
 
14
- ZONEINFO_DIR = '/usr/share/zoneinfo'.freeze
14
+ # Bookkeeping files that we do not want to parse.
15
+ IGNORE = ['leapseconds', 'posixrules', 'tzdata.zi'].freeze
15
16
 
16
- attr_reader :zoneinfo
17
-
18
- def initialize(zoneinfo = ZONEINFO_DIR)
19
- @zoneinfo = zoneinfo
17
+ def initialize(root)
18
+ @config = Config.new(root)
20
19
  end
21
20
 
22
21
  def perform
23
22
  FileUtils.rm_rf('data')
24
23
 
25
- Dir["#{zoneinfo}/posix/**/*"].each do |file|
24
+ Dir["#{@config.zoneinfo}/**/*"].each do |file|
26
25
  next if File.directory?(file)
26
+ next if file.end_with?('.tab')
27
+ next if IGNORE.include?(File.basename(file))
28
+
27
29
  parse(file)
28
30
  end
29
31
  end
30
32
 
33
+ # Represents a timezone database config.
34
+ class Config
35
+ def initialize(root)
36
+ @root = root
37
+ @zoneinfo = File.join(@root, 'usr/share/zoneinfo')
38
+ @zdump = File.join(@root, 'usr/bin/zdump')
39
+ end
40
+
41
+ attr_reader :root, :zoneinfo, :zdump
42
+ end
43
+
44
+ private_constant :Config
45
+
31
46
  # Represents a single timezone data file line for a reference timezone.
32
47
  class RefLine
33
- def initialize(zone)
48
+ def initialize(config, file)
34
49
  first =
35
- `zdump -i posix/#{zone}`
50
+ `#{config.zdump} -i #{file}`
36
51
  .split("\n")
37
52
  .reject(&:empty?)
38
53
  .reject { |line| line.start_with?('TZ=') }
@@ -51,7 +66,7 @@ module Timezone
51
66
  def parse_offset(offset)
52
67
  arity = offset.start_with?('-') ? -1 : 1
53
68
 
54
- match = offset.match(/^[\-\+](\d{2})$/)
69
+ match = offset.match(/^[\-+](\d{2})$/)
55
70
  arity * match[1].to_i * 60 * 60
56
71
  end
57
72
  end
@@ -62,10 +77,10 @@ module Timezone
62
77
  class Line
63
78
  attr_accessor :source, :name, :dst, :offset
64
79
 
65
- SOURCE_FORMAT = '%a %b %e %H:%M:%S %Y %Z'.freeze
80
+ SOURCE_FORMAT = '%a %b %e %H:%M:%S %Y %Z'
66
81
 
67
82
  def initialize(match)
68
- self.source = Time.strptime(match[1] + 'C', SOURCE_FORMAT).to_i
83
+ self.source = Time.strptime("#{match[1]}C", SOURCE_FORMAT).to_i
69
84
  self.name = match[2].split(' ').last
70
85
  self.dst = match[3].to_i
71
86
  self.offset = match[4].to_i
@@ -85,15 +100,15 @@ module Timezone
85
100
  private
86
101
 
87
102
  def parse(file)
88
- zone = file.gsub("#{zoneinfo}/posix/", '')
103
+ zone = file.gsub("#{@config.zoneinfo}/", '')
89
104
  print "Parsing #{zone}... "
90
- data = zdump(zone)
105
+ data = zdump(file)
91
106
 
92
107
  last = 0
93
108
  result = []
94
109
 
95
110
  data.split("\n").each do |line|
96
- match = line.gsub('posix/' + zone + ' ', '').match(LINE)
111
+ match = line.gsub(/^#{file}\s+/, '').match(LINE)
97
112
  next if match.nil?
98
113
 
99
114
  line = Line.new(match)
@@ -112,14 +127,14 @@ module Timezone
112
127
  result << line
113
128
  end
114
129
 
115
- result << RefLine.new(zone) if result.empty?
130
+ result << RefLine.new(@config, file) if result.empty?
116
131
 
117
132
  write(zone, result)
118
133
  puts 'DONE'
119
134
  end
120
135
 
121
- def zdump(zone)
122
- `zdump -v -c #{MIN_YEAR},#{MAX_YEAR} posix/#{zone}`
136
+ def zdump(file)
137
+ `#{@config.zdump} -V -c #{MIN_YEAR},#{MAX_YEAR} #{file}`
123
138
  end
124
139
 
125
140
  def write(zone, data)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Timezone
4
- VERSION = '1.3.3'.freeze
4
+ VERSION = '1.3.29'
5
5
  end
data/lib/timezone/zone.rb CHANGED
@@ -6,7 +6,6 @@ require 'time'
6
6
 
7
7
  require 'timezone/loader'
8
8
  require 'timezone/error'
9
- require 'timezone/loader'
10
9
 
11
10
  module Timezone
12
11
  # This object represents a real-world timezone. Each instance provides
@@ -179,10 +178,6 @@ module Timezone
179
178
 
180
179
  private
181
180
 
182
- def private_rules
183
- @rules ||= Loader.load(name)
184
- end
185
-
186
181
  def sanitize(time)
187
182
  time.to_time
188
183
  end
@@ -200,11 +195,15 @@ module Timezone
200
195
 
201
196
  def rule_for_local(local)
202
197
  local = local.to_i
198
+ rules = Loader.load(name)
203
199
 
204
200
  # For each rule, convert the local time into the UTC equivalent for
205
201
  # that rule offset, and then check if the UTC time matches the rule.
206
- index = binary_search(local) { |t, r| match?(t - r[OFFSET_BIT], r) }
207
- match = private_rules[index]
202
+ index =
203
+ binary_search(rules, local) do |t, r|
204
+ match?(t - r[OFFSET_BIT], r)
205
+ end
206
+ match = rules[index]
208
207
 
209
208
  utc = local - match[OFFSET_BIT]
210
209
 
@@ -213,7 +212,7 @@ module Timezone
213
212
  return RuleSet.new(:missing, [match]) if rule_for_utc(utc) != match
214
213
 
215
214
  # If the match is the last rule, then return it.
216
- return RuleSet.new(:single, [match]) if index == private_rules.length - 1
215
+ return RuleSet.new(:single, [match]) if index == rules.length - 1
217
216
 
218
217
  # If the UTC equivalent time falls within the last hour(s) of the time
219
218
  # change which were replayed during a fall-back in time, then return
@@ -234,10 +233,10 @@ module Timezone
234
233
  last_hour =
235
234
  match[SOURCE_BIT] -
236
235
  match[OFFSET_BIT] +
237
- private_rules[index + 1][OFFSET_BIT]
236
+ rules[index + 1][OFFSET_BIT]
238
237
 
239
238
  if utc > last_hour
240
- RuleSet.new(:double, private_rules[index..(index + 1)])
239
+ RuleSet.new(:double, rules[index..(index + 1)])
241
240
  else
242
241
  RuleSet.new(:single, [match])
243
242
  end
@@ -245,27 +244,28 @@ module Timezone
245
244
 
246
245
  def rule_for_utc(time)
247
246
  time = time.to_i
247
+ rules = Loader.load(name)
248
248
 
249
- private_rules[binary_search(time) { |t, r| match?(t, r) }]
249
+ rules[binary_search(rules, time) { |t, r| match?(t, r) }]
250
250
  end
251
251
 
252
252
  # Find the first rule that matches using binary search.
253
- def binary_search(time, from = 0, to = nil, &block)
254
- to = private_rules.length - 1 if to.nil?
253
+ def binary_search(rules, time, from = 0, to = nil, &block)
254
+ to = rules.length - 1 if to.nil?
255
255
 
256
256
  return from if from == to
257
257
 
258
258
  mid = (from + to).div(2)
259
259
 
260
- unless yield(time, private_rules[mid])
261
- return binary_search(time, mid + 1, to, &block)
260
+ unless yield(time, rules[mid])
261
+ return binary_search(rules, time, mid + 1, to, &block)
262
262
  end
263
263
 
264
264
  return mid if mid.zero?
265
265
 
266
- return mid unless yield(time, private_rules[mid - 1])
266
+ return mid unless yield(time, rules[mid - 1])
267
267
 
268
- binary_search(time, from, mid - 1, &block)
268
+ binary_search(rules, time, from, mid - 1, &block)
269
269
  end
270
270
  end
271
271
  end
@@ -32,13 +32,11 @@ class TestTimezone < ::Minitest::Test
32
32
  end
33
33
 
34
34
  def test_fetch_warning
35
- warning = false
36
-
37
- Timezone.stub(:warn, ->(_) { warning = true }) do
35
+ _out, err = capture_io do
38
36
  Timezone.fetch('foo/bar', 'a') { 'b' }
39
37
  end
40
38
 
41
- assert warning, 'warning was not issued'
39
+ assert_equal "warning: block supersedes default value argument\n", err
42
40
  end
43
41
 
44
42
  def test_lookup
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'timezone/lookup/geonames'
4
4
  require 'minitest/autorun'
5
+ require 'ostruct'
5
6
  require_relative '../../http_test_client'
6
7
 
7
8
  class TestGeonames < ::Minitest::Test
@@ -19,6 +20,10 @@ class TestGeonames < ::Minitest::Test
19
20
  }
20
21
  end
21
22
 
23
+ def lookup_file(file_name, &block)
24
+ lookup(File.open(File.join(mock_path, file_name)).read, &block)
25
+ end
26
+
22
27
  def lookup(body = nil, &_block)
23
28
  config = OpenStruct.new
24
29
  config.username = 'timezone'
@@ -40,23 +45,25 @@ class TestGeonames < ::Minitest::Test
40
45
  end
41
46
 
42
47
  def test_lookup
43
- mine = lookup(File.open(mock_path + '/lat_lon_coords.txt').read)
48
+ mine = lookup_file('/lat_lon_coords.txt')
44
49
 
45
50
  assert_equal 'Australia/Adelaide', mine.lookup(*coordinates)
46
51
  end
47
52
 
48
53
  def test_lookup_with_etc
49
54
  etc_data.each do |key, data|
50
- body = File.open(mock_path + "/lat_lon_coords_#{key}.txt").read
51
- mine = lookup(body) { |c| c.offset_etc_zones = true }
55
+ mine = lookup_file("/lat_lon_coords_#{key}.txt") do |c|
56
+ c.offset_etc_zones = true
57
+ end
52
58
 
53
59
  assert_equal data[:name], mine.lookup(*data[:coordinates])
54
60
  end
55
61
  end
56
62
 
57
63
  def test_wrong_offset
58
- body = File.open(mock_path + '/lat_lon_coords_wrong_offset.txt').read
59
- mine = lookup(body) { |c| c.offset_etc_zones = true }
64
+ mine = lookup_file('/lat_lon_coords_wrong_offset.txt') do |c|
65
+ c.offset_etc_zones = true
66
+ end
60
67
 
61
68
  assert_nil mine.lookup(*coordinates)
62
69
  end
@@ -80,7 +87,7 @@ class TestGeonames < ::Minitest::Test
80
87
  end
81
88
 
82
89
  def test_api_limit
83
- mine = lookup(File.open(mock_path + '/api_limit_reached.json').read)
90
+ mine = lookup_file('/api_limit_reached.json')
84
91
 
85
92
  assert_exception(
86
93
  mine,
@@ -90,19 +97,19 @@ class TestGeonames < ::Minitest::Test
90
97
  end
91
98
 
92
99
  def test_invalid_latlong
93
- mine = lookup(File.open(mock_path + '/invalid_latlong.json').read)
100
+ mine = lookup_file('/invalid_latlong.json')
94
101
 
95
102
  assert_exception(mine, 'invalid lat/lng')
96
103
  end
97
104
 
98
105
  def test_no_result_found
99
- mine = lookup(File.open(mock_path + '/no_result_found.json').read)
106
+ mine = lookup_file('/no_result_found.json')
100
107
 
101
108
  assert_nil(mine.lookup(10, 10))
102
109
  end
103
110
 
104
111
  def test_invalid_parameter
105
- mine = lookup(File.open(mock_path + '/invalid_parameter.json').read)
112
+ mine = lookup_file('/invalid_parameter.json')
106
113
 
107
114
  assert_exception(mine, 'error parsing parameter')
108
115
  end
@@ -3,6 +3,7 @@
3
3
  require 'timezone/lookup/google'
4
4
  require 'minitest/autorun'
5
5
  require 'timecop'
6
+ require 'ostruct'
6
7
  require_relative '../../http_test_client'
7
8
 
8
9
  class TestGoogle < ::Minitest::Test
@@ -12,6 +13,10 @@ class TestGoogle < ::Minitest::Test
12
13
  [-34.92771808058, 138.477041423321]
13
14
  end
14
15
 
16
+ def lookup_file(file_name, &block)
17
+ lookup(File.open(File.join(mock_path, file_name)).read, &block)
18
+ end
19
+
15
20
  def lookup(body = nil, &_block)
16
21
  config = OpenStruct.new
17
22
  config.api_key = 'MTIzYWJj'
@@ -33,7 +38,7 @@ class TestGoogle < ::Minitest::Test
33
38
  end
34
39
 
35
40
  def test_google_using_lat_long_coordinates
36
- mine = lookup(File.open(mock_path + '/google_lat_lon_coords.txt').read)
41
+ mine = lookup_file('/google_lat_lon_coords.txt')
37
42
 
38
43
  assert_equal 'Australia/Adelaide', mine.lookup(*coordinates)
39
44
  end
@@ -76,7 +81,7 @@ class TestGoogle < ::Minitest::Test
76
81
  end
77
82
 
78
83
  def test_no_result_found
79
- mine = lookup(File.open(mock_path + '/google_no_result_found.json').read)
84
+ mine = lookup_file('/google_no_result_found.json')
80
85
 
81
86
  assert_nil(mine.lookup(26.188703, -78.987053))
82
87
  end
@@ -3,6 +3,7 @@
3
3
  require 'timezone/lookup/test'
4
4
  require 'timezone'
5
5
  require 'minitest/autorun'
6
+ require 'ostruct'
6
7
 
7
8
  class TestTest < ::Minitest::Test
8
9
  parallelize_me!
@@ -8,7 +8,7 @@ class TestLookup < ::Minitest::Test
8
8
  Timezone::Lookup.config(:test)
9
9
 
10
10
  assert_equal Timezone::Lookup::Test,
11
- Timezone::Lookup.lookup.class
11
+ Timezone::Lookup.lookup.class
12
12
  end
13
13
 
14
14
  def test_geonames_config
@@ -17,10 +17,10 @@ class TestLookup < ::Minitest::Test
17
17
  end
18
18
 
19
19
  assert_equal Timezone::Lookup::Geonames,
20
- Timezone::Lookup.lookup.class
20
+ Timezone::Lookup.lookup.class
21
21
 
22
22
  assert_equal Timezone::NetHTTPClient,
23
- Timezone::Lookup.lookup.config.request_handler
23
+ Timezone::Lookup.lookup.config.request_handler
24
24
  end
25
25
 
26
26
  def test_google_config
@@ -29,10 +29,10 @@ class TestLookup < ::Minitest::Test
29
29
  end
30
30
 
31
31
  assert_equal Timezone::Lookup::Google,
32
- Timezone::Lookup.lookup.class
32
+ Timezone::Lookup.lookup.class
33
33
 
34
34
  assert_equal Timezone::NetHTTPClient,
35
- Timezone::Lookup.lookup.config.request_handler
35
+ Timezone::Lookup.lookup.config.request_handler
36
36
  end
37
37
 
38
38
  def test_custom_config
@@ -130,12 +130,12 @@ class TestZone < ::Minitest::Test
130
130
 
131
131
  # http://www.timeanddate.com/worldclock/clockchange.html?n=2364&year=1940
132
132
  def test_historical_time_change_in_hebron
133
- local = Time.strptime('1940-05-31T23:59:59 UTC', '%Y-%m-%dT%H:%M:%S %Z')
134
- utc = Time.strptime('1940-05-31T21:59:59 UTC', '%Y-%m-%dT%H:%M:%S %Z')
133
+ local = Time.strptime('1940-06-01T01:59:59 UTC', '%Y-%m-%dT%H:%M:%S %Z')
134
+ utc = Time.strptime('1940-05-31T23:59:59 UTC', '%Y-%m-%dT%H:%M:%S %Z')
135
135
  assert_equal local.to_i, zone('Asia/Hebron').time(utc).to_i
136
136
 
137
- local = Time.strptime('1940-06-01T01:00:00 UTC', '%Y-%m-%dT%H:%M:%S %Z')
138
- utc = Time.strptime('1940-05-31T22:00:00 UTC', '%Y-%m-%dT%H:%M:%S %Z')
137
+ local = Time.strptime('1940-06-01T03:00:00 UTC', '%Y-%m-%dT%H:%M:%S %Z')
138
+ utc = Time.strptime('1940-06-01T00:00:00 UTC', '%Y-%m-%dT%H:%M:%S %Z')
139
139
  assert_equal local.to_i, zone('Asia/Hebron').time(utc).to_i
140
140
  end
141
141