tz_magic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +75 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/tz_magic/application.js +15 -0
  5. data/app/assets/javascripts/tz_magic/detect_timezone.js +302 -0
  6. data/app/assets/stylesheets/tz_magic/application.css +13 -0
  7. data/app/controllers/tz_magic/application_controller.rb +5 -0
  8. data/app/controllers/tz_magic/time_zones_controller.rb +13 -0
  9. data/app/helpers/tz_magic/application_helper.rb +9 -0
  10. data/app/views/layouts/tz_magic/application.html.erb +14 -0
  11. data/app/views/tz_magic/time_zones/new.html.erb +4 -0
  12. data/config/routes.rb +3 -0
  13. data/lib/tasks/tz_magic_tasks.rake +4 -0
  14. data/lib/tz_magic.rb +5 -0
  15. data/lib/tz_magic/before_filter.rb +17 -0
  16. data/lib/tz_magic/engine.rb +5 -0
  17. data/lib/tz_magic/version.rb +3 -0
  18. data/test/dummy/README.rdoc +261 -0
  19. data/test/dummy/Rakefile +7 -0
  20. data/test/dummy/app/assets/javascripts/application.js +15 -0
  21. data/test/dummy/app/assets/javascripts/main.js +2 -0
  22. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  23. data/test/dummy/app/assets/stylesheets/main.css +4 -0
  24. data/test/dummy/app/controllers/application_controller.rb +5 -0
  25. data/test/dummy/app/controllers/main_controller.rb +5 -0
  26. data/test/dummy/app/helpers/application_helper.rb +4 -0
  27. data/test/dummy/app/helpers/main_helper.rb +2 -0
  28. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  29. data/test/dummy/app/views/main/index.html.erb +1 -0
  30. data/test/dummy/config.ru +4 -0
  31. data/test/dummy/config/application.rb +58 -0
  32. data/test/dummy/config/boot.rb +10 -0
  33. data/test/dummy/config/database.yml +25 -0
  34. data/test/dummy/config/environment.rb +5 -0
  35. data/test/dummy/config/environments/development.rb +27 -0
  36. data/test/dummy/config/environments/test.rb +29 -0
  37. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/test/dummy/config/initializers/inflections.rb +15 -0
  39. data/test/dummy/config/initializers/mime_types.rb +5 -0
  40. data/test/dummy/config/initializers/secret_token.rb +7 -0
  41. data/test/dummy/config/initializers/session_store.rb +8 -0
  42. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  43. data/test/dummy/config/locales/en.yml +5 -0
  44. data/test/dummy/config/routes.rb +9 -0
  45. data/test/dummy/log/test.log +18 -0
  46. data/test/dummy/public/404.html +26 -0
  47. data/test/dummy/public/422.html +26 -0
  48. data/test/dummy/public/500.html +25 -0
  49. data/test/dummy/public/favicon.ico +0 -0
  50. data/test/dummy/script/rails +6 -0
  51. data/test/dummy/test/functional/main_controller_test.rb +20 -0
  52. data/test/dummy/test/unit/helpers/main_helper_test.rb +4 -0
  53. data/test/integration/navigation_test.rb +8 -0
  54. data/test/test_helper.rb +15 -0
  55. data/test/tz_magic_test.rb +12 -0
  56. metadata +176 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,75 @@
1
+ = TzMagic
2
+
3
+ = Description
4
+
5
+ While every sysadmin worth his or her salt runs their server in the UTC timezone, it is occasionally useful, even necessary, to know the timezone that the end user's browser is set to. Surprisingly, there isn't an easy way to do this - you'd think that the browser might supply that in a header, but no... getting it is an exercise in plumbing together raw pieces of the internet. This gem gives that to your average rails app with a minimum of fuss.
6
+
7
+
8
+ = How to use it
9
+
10
+ Its a rails engine. You install it via a gem, then include various bits.
11
+
12
+ == add this to your Gemfile with:
13
+
14
+ <tt>
15
+ gem 'tz-magic'
16
+ </tt>
17
+
18
+ == then wire it up in your rails app:
19
+
20
+ add this line to your config/routes.rb file:
21
+
22
+ <tt>
23
+ mount TzMagic::Engine => "/tz_magic"
24
+ </tt>
25
+
26
+ add this line to your helpers/application_helper.rb file:
27
+
28
+ <tt>
29
+ include TzMagic::ApplicationHelper
30
+ </tt>
31
+
32
+ add this line to your app/assets/javascripts/application.js file:
33
+
34
+ <tt>
35
+ require tz_magic/application
36
+ </tt>
37
+
38
+ add this line to the controller that you need to ensure the timezone exists for (application_controller.rb is an acceptable, if brute-force option):
39
+
40
+ <tt>
41
+ include TzMagic::BeforeFilter
42
+ </tt>
43
+
44
+ you now have a helper you can refer to in your controllers or views like:
45
+
46
+ <tt>
47
+ time_zone
48
+ </tt>
49
+
50
+ or
51
+
52
+ <tt>
53
+ <%= time_zone.name %>
54
+ </tt>
55
+
56
+ This is a full fleged TimeZone instance.
57
+
58
+ = How it works
59
+
60
+ by including the TzMagic::BeforeFilter into your controller, you are adding a check that the user's session contains a :timezone_name element. If that element exists, all is fine. If it doesn't exist:
61
+
62
+ - we redirect the user to our /timezone/new view
63
+ - this view runs some javascript that determines the user's timezone, then submits it via an ajax push to /timezone
64
+ - this hits our timezone#create method, which sets the cookie and returns the original url the user was going to via a json block.
65
+ - the javascript receives the json payload, extracts the original url, and redirects, with the timezone happily in the session.
66
+
67
+ = the magic javascript code
68
+
69
+
70
+ The javascript at the heart of this Rube Goldberg contraption is Copyright (c) 2012 Jon Nylander, project maintained at
71
+ https://bitbucket.org/pellepim/jstimezonedetect
72
+
73
+ = contributing
74
+
75
+ The usual. Fork, modify, test, pullup request.
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'TzMagic'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,302 @@
1
+ /*jslint undef: true */
2
+ /*global console*/
3
+ /*version 2012-05-10*/
4
+
5
+ /**
6
+ * Namespace to hold all the code for timezone detection.
7
+ */
8
+ var jstz = (function () {
9
+ 'use strict';
10
+ var HEMISPHERE_SOUTH = 's',
11
+
12
+ /**
13
+ * Gets the offset in minutes from UTC for a certain date.
14
+ * @param {Date} date
15
+ * @returns {Number}
16
+ */
17
+ get_date_offset = function (date) {
18
+ var offset = -date.getTimezoneOffset();
19
+ return (offset !== null ? offset : 0);
20
+ },
21
+
22
+ get_january_offset = function () {
23
+ return get_date_offset(new Date(2010, 0, 1, 0, 0, 0, 0));
24
+ },
25
+
26
+ get_june_offset = function () {
27
+ return get_date_offset(new Date(2010, 5, 1, 0, 0, 0, 0));
28
+ },
29
+
30
+ /**
31
+ * Private method.
32
+ * Checks whether a given date is in daylight savings time.
33
+ * If the date supplied is after june, we assume that we're checking
34
+ * for southern hemisphere DST.
35
+ * @param {Date} date
36
+ * @returns {Boolean}
37
+ */
38
+ date_is_dst = function (date) {
39
+ var base_offset = ((date.getMonth() > 5 ? get_june_offset()
40
+ : get_january_offset())),
41
+ date_offset = get_date_offset(date);
42
+
43
+ return (base_offset - date_offset) !== 0;
44
+ },
45
+
46
+ /**
47
+ * This function does some basic calculations to create information about
48
+ * the user's timezone.
49
+ *
50
+ * Returns a key that can be used to do lookups in jstz.olson.timezones.
51
+ *
52
+ * @returns {String}
53
+ */
54
+
55
+ lookup_key = function () {
56
+ var january_offset = get_january_offset(),
57
+ june_offset = get_june_offset(),
58
+ diff = get_january_offset() - get_june_offset();
59
+
60
+ if (diff < 0) {
61
+ return january_offset + ",1";
62
+ } else if (diff > 0) {
63
+ return june_offset + ",1," + HEMISPHERE_SOUTH;
64
+ }
65
+
66
+ return january_offset + ",0";
67
+ },
68
+
69
+ /**
70
+ * Uses get_timezone_info() to formulate a key to use in the olson.timezones dictionary.
71
+ *
72
+ * Returns a primitive object on the format:
73
+ * {'timezone': TimeZone, 'key' : 'the key used to find the TimeZone object'}
74
+ *
75
+ * @returns Object
76
+ */
77
+ determine = function () {
78
+ var key = lookup_key();
79
+ return new jstz.TimeZone(jstz.olson.timezones[key]);
80
+ };
81
+
82
+ return {
83
+ determine_timezone : function () {
84
+ if (typeof console !== 'undefined') {
85
+ console.log("jstz.determine_timezone() is deprecated and will be removed in an upcoming version. Please use jstz.determine() instead.");
86
+ }
87
+ return determine();
88
+ },
89
+ determine: determine,
90
+ date_is_dst : date_is_dst
91
+ };
92
+ }());
93
+
94
+ /**
95
+ * Simple object to perform ambiguity check and to return name of time zone.
96
+ */
97
+ jstz.TimeZone = function (tz_name) {
98
+ 'use strict';
99
+ var timezone_name = null,
100
+
101
+ name = function () {
102
+ return timezone_name;
103
+ },
104
+
105
+ /**
106
+ * Checks if a timezone has possible ambiguities. I.e timezones that are similar.
107
+ *
108
+ * For example, if the preliminary scan determines that we're in America/Denver.
109
+ * We double check here that we're really there and not in America/Mazatlan.
110
+ *
111
+ * This is done by checking known dates for when daylight savings start for different
112
+ * timezones during 2010 and 2011.
113
+ */
114
+ ambiguity_check = function () {
115
+ var ambiguity_list = jstz.olson.ambiguity_list[timezone_name],
116
+ length = ambiguity_list.length,
117
+ i = 0,
118
+ tz = ambiguity_list[0];
119
+
120
+ for (; i < length; i += 1) {
121
+ tz = ambiguity_list[i];
122
+
123
+ if (jstz.date_is_dst(jstz.olson.dst_start_dates[tz])) {
124
+ timezone_name = tz;
125
+ return;
126
+ }
127
+ }
128
+ },
129
+
130
+ /**
131
+ * Checks if it is possible that the timezone is ambiguous.
132
+ */
133
+ is_ambiguous = function () {
134
+ return typeof (jstz.olson.ambiguity_list[timezone_name]) !== 'undefined';
135
+ };
136
+
137
+
138
+
139
+ timezone_name = tz_name;
140
+ if (is_ambiguous()) {
141
+ ambiguity_check();
142
+ }
143
+
144
+ return {
145
+ name: name
146
+ };
147
+ };
148
+
149
+ jstz.olson = {};
150
+
151
+ /*
152
+ * The keys in this dictionary are comma separated as such:
153
+ *
154
+ * First the offset compared to UTC time in minutes.
155
+ *
156
+ * Then a flag which is 0 if the timezone does not take daylight savings into account and 1 if it
157
+ * does.
158
+ *
159
+ * Thirdly an optional 's' signifies that the timezone is in the southern hemisphere,
160
+ * only interesting for timezones with DST.
161
+ *
162
+ * The mapped arrays is used for constructing the jstz.TimeZone object from within
163
+ * jstz.determine_timezone();
164
+ */
165
+ jstz.olson.timezones = {
166
+ '-720,0' : 'Etc/GMT+12',
167
+ '-660,0' : 'Pacific/Pago_Pago',
168
+ '-600,1' : 'America/Adak',
169
+ '-600,0' : 'Pacific/Honolulu',
170
+ '-570,0' : 'Pacific/Marquesas',
171
+ '-540,0' : 'Pacific/Gambier',
172
+ '-540,1' : 'America/Anchorage',
173
+ '-480,1' : 'America/Los_Angeles',
174
+ '-480,0' : 'Pacific/Pitcairn',
175
+ '-420,0' : 'America/Phoenix',
176
+ '-420,1' : 'America/Denver',
177
+ '-360,0' : 'America/Guatemala',
178
+ '-360,1' : 'America/Chicago',
179
+ '-360,1,s' : 'Pacific/Easter',
180
+ '-300,0' : 'America/Bogota',
181
+ '-300,1' : 'America/New_York',
182
+ '-270,0' : 'America/Caracas',
183
+ '-240,1' : 'America/Halifax',
184
+ '-240,0' : 'America/Santo_Domingo',
185
+ '-240,1,s' : 'America/Asuncion',
186
+ '-210,1' : 'America/St_Johns',
187
+ '-180,1' : 'America/Godthab',
188
+ '-180,0' : 'America/Argentina/Buenos_Aires',
189
+ '-180,1,s' : 'America/Montevideo',
190
+ '-120,0' : 'America/Noronha',
191
+ '-120,1' : 'Etc/GMT+2',
192
+ '-60,1' : 'Atlantic/Azores',
193
+ '-60,0' : 'Atlantic/Cape_Verde',
194
+ '0,0' : 'Etc/UTC',
195
+ '0,1' : 'Europe/London',
196
+ '60,1' : 'Europe/Berlin',
197
+ '60,0' : 'Africa/Lagos',
198
+ '60,1,s' : 'Africa/Windhoek',
199
+ '120,1' : 'Asia/Beirut',
200
+ '120,0' : 'Africa/Johannesburg',
201
+ '180,1' : 'Europe/Moscow',
202
+ '180,0' : 'Asia/Baghdad',
203
+ '210,1' : 'Asia/Tehran',
204
+ '240,0' : 'Asia/Dubai',
205
+ '240,1' : 'Asia/Yerevan',
206
+ '270,0' : 'Asia/Kabul',
207
+ '300,1' : 'Asia/Yekaterinburg',
208
+ '300,0' : 'Asia/Karachi',
209
+ '330,0' : 'Asia/Kolkata',
210
+ '345,0' : 'Asia/Kathmandu',
211
+ '360,0' : 'Asia/Dhaka',
212
+ '360,1' : 'Asia/Omsk',
213
+ '390,0' : 'Asia/Rangoon',
214
+ '420,1' : 'Asia/Krasnoyarsk',
215
+ '420,0' : 'Asia/Jakarta',
216
+ '480,0' : 'Asia/Shanghai',
217
+ '480,1' : 'Asia/Irkutsk',
218
+ '525,0' : 'Australia/Eucla',
219
+ '525,1,s' : 'Australia/Eucla',
220
+ '540,1' : 'Asia/Yakutsk',
221
+ '540,0' : 'Asia/Tokyo',
222
+ '570,0' : 'Australia/Darwin',
223
+ '570,1,s' : 'Australia/Adelaide',
224
+ '600,0' : 'Australia/Brisbane',
225
+ '600,1' : 'Asia/Vladivostok',
226
+ '600,1,s' : 'Australia/Sydney',
227
+ '630,1,s' : 'Australia/Lord_Howe',
228
+ '660,1' : 'Asia/Kamchatka',
229
+ '660,0' : 'Pacific/Noumea',
230
+ '690,0' : 'Pacific/Norfolk',
231
+ '720,1,s' : 'Pacific/Auckland',
232
+ '720,0' : 'Pacific/Tarawa',
233
+ '765,1,s' : 'Pacific/Chatham',
234
+ '780,0' : 'Pacific/Tongatapu',
235
+ '780,1,s' : 'Pacific/Apia',
236
+ '840,0' : 'Pacific/Kiritimati'
237
+ };
238
+
239
+
240
+ /**
241
+ * This object contains information on when daylight savings starts for
242
+ * different timezones.
243
+ *
244
+ * The list is short for a reason. Often we do not have to be very specific
245
+ * to single out the correct timezone. But when we do, this list comes in
246
+ * handy.
247
+ *
248
+ * Each value is a date denoting when daylight savings starts for that timezone.
249
+ */
250
+ jstz.olson.dst_start_dates = {
251
+ 'America/Denver' : new Date(2011, 2, 13, 3, 0, 0, 0),
252
+ 'America/Mazatlan' : new Date(2011, 3, 3, 3, 0, 0, 0),
253
+ 'America/Chicago' : new Date(2011, 2, 13, 3, 0, 0, 0),
254
+ 'America/Mexico_City' : new Date(2011, 3, 3, 3, 0, 0, 0),
255
+ 'Atlantic/Stanley' : new Date(2011, 8, 4, 7, 0, 0, 0),
256
+ 'America/Asuncion' : new Date(2011, 9, 2, 3, 0, 0, 0),
257
+ 'America/Santiago' : new Date(2011, 9, 9, 3, 0, 0, 0),
258
+ 'America/Campo_Grande' : new Date(2011, 9, 16, 5, 0, 0, 0),
259
+ 'America/Montevideo' : new Date(2011, 9, 2, 3, 0, 0, 0),
260
+ 'America/Sao_Paulo' : new Date(2011, 9, 16, 5, 0, 0, 0),
261
+ 'America/Los_Angeles' : new Date(2011, 2, 13, 8, 0, 0, 0),
262
+ 'America/Santa_Isabel' : new Date(2011, 3, 5, 8, 0, 0, 0),
263
+ 'America/Havana' : new Date(2011, 2, 13, 2, 0, 0, 0),
264
+ 'America/New_York' : new Date(2011, 2, 13, 7, 0, 0, 0),
265
+ 'Asia/Gaza' : new Date(2011, 2, 26, 23, 0, 0, 0),
266
+ 'Asia/Beirut' : new Date(2011, 2, 27, 1, 0, 0, 0),
267
+ 'Europe/Minsk' : new Date(2011, 2, 27, 2, 0, 0, 0),
268
+ 'Europe/Helsinki' : new Date(2011, 2, 27, 4, 0, 0, 0),
269
+ 'Europe/Istanbul' : new Date(2011, 2, 28, 5, 0, 0, 0),
270
+ 'Asia/Damascus' : new Date(2011, 3, 1, 2, 0, 0, 0),
271
+ 'Asia/Jerusalem' : new Date(2011, 3, 1, 6, 0, 0, 0),
272
+ 'Africa/Cairo' : new Date(2010, 3, 30, 4, 0, 0, 0),
273
+ 'Asia/Yerevan' : new Date(2011, 2, 27, 4, 0, 0, 0),
274
+ 'Asia/Baku' : new Date(2011, 2, 27, 8, 0, 0, 0),
275
+ 'Pacific/Auckland' : new Date(2011, 8, 26, 7, 0, 0, 0),
276
+ 'Pacific/Fiji' : new Date(2010, 11, 29, 23, 0, 0, 0),
277
+ 'America/Halifax' : new Date(2011, 2, 13, 6, 0, 0, 0),
278
+ 'America/Goose_Bay' : new Date(2011, 2, 13, 2, 1, 0, 0),
279
+ 'America/Miquelon' : new Date(2011, 2, 13, 5, 0, 0, 0),
280
+ 'America/Godthab' : new Date(2011, 2, 27, 1, 0, 0, 0)
281
+ };
282
+
283
+ /**
284
+ * The keys in this object are timezones that we know may be ambiguous after
285
+ * a preliminary scan through the olson_tz object.
286
+ *
287
+ * The array of timezones to compare must be in the order that daylight savings
288
+ * starts for the regions.
289
+ */
290
+ jstz.olson.ambiguity_list = {
291
+ 'America/Denver' : ['America/Denver', 'America/Mazatlan'],
292
+ 'America/Chicago' : ['America/Chicago', 'America/Mexico_City'],
293
+ 'America/Asuncion' : ['Atlantic/Stanley', 'America/Asuncion', 'America/Santiago', 'America/Campo_Grande'],
294
+ 'America/Montevideo' : ['America/Montevideo', 'America/Sao_Paulo'],
295
+ 'Asia/Beirut' : ['Asia/Gaza', 'Asia/Beirut', 'Europe/Minsk', 'Europe/Helsinki', 'Europe/Istanbul', 'Asia/Damascus', 'Asia/Jerusalem', 'Africa/Cairo'],
296
+ 'Asia/Yerevan' : ['Asia/Yerevan', 'Asia/Baku'],
297
+ 'Pacific/Auckland' : ['Pacific/Auckland', 'Pacific/Fiji'],
298
+ 'America/Los_Angeles' : ['America/Los_Angeles', 'America/Santa_Isabel'],
299
+ 'America/New_York' : ['America/Havana', 'America/New_York'],
300
+ 'America/Halifax' : ['America/Goose_Bay', 'America/Halifax'],
301
+ 'America/Godthab' : ['America/Miquelon', 'America/Godthab']
302
+ };