├── .rspec
├── spec
├── dummy
│ ├── public
│ │ └── favicon.ico
│ ├── app
│ │ ├── assets
│ │ │ └── javascripts
│ │ │ │ └── application.js
│ │ ├── controllers
│ │ │ └── application_controller.rb
│ │ └── views
│ │ │ └── layouts
│ │ │ └── application.html.erb
│ ├── config
│ │ ├── initializers
│ │ │ └── teabag.rb
│ │ ├── environment.rb
│ │ ├── boot.rb
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ └── test.rb
│ │ ├── routes.rb
│ │ └── application.rb
│ ├── config.ru
│ ├── Rakefile
│ └── script
│ │ └── rails
├── javascripts
│ ├── spec_helper.coffee
│ └── temporal_spec.coffee
├── engine
│ └── temporal_spec.rb
├── controllers
│ └── application_controller_spec.rb
└── spec_helper.rb
├── .rvmrc.example
├── lib
├── temporal-rails.rb
└── temporal
│ ├── version.rb
│ ├── rails.rb
│ ├── engine.rb
│ └── controller_additions.rb
├── .travis.yml
├── .gitignore
├── Gemfile
├── temporal-rails.gemspec
├── MIT.LICENSE
├── Rakefile
├── README.md
├── distro
├── temporal.min.js
└── temporal.js
└── vendor
└── assets
└── javascripts
└── temporal.js.coffee
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 |
--------------------------------------------------------------------------------
/spec/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/javascripts/spec_helper.coffee:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.rvmrc.example:
--------------------------------------------------------------------------------
1 | rvm use --create 1.9.3@temporal
2 |
--------------------------------------------------------------------------------
/lib/temporal-rails.rb:
--------------------------------------------------------------------------------
1 | require 'temporal/rails'
2 |
--------------------------------------------------------------------------------
/lib/temporal/version.rb:
--------------------------------------------------------------------------------
1 | module Temporal
2 | VERSION = '0.2.4'
3 | end
4 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | //= require temporal
2 |
3 | Temporal.detect('jejacks0n');
4 |
--------------------------------------------------------------------------------
/lib/temporal/rails.rb:
--------------------------------------------------------------------------------
1 | module Temporal
2 | require 'temporal/version'
3 | require 'temporal/engine'
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/teabag.rb:
--------------------------------------------------------------------------------
1 | Teabag.setup do |config|
2 |
3 | config.root = Temporal::Engine.root
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | rvm:
2 | - 1.9.3
3 | before_script:
4 | - 'sh -e /etc/init.d/xvfb start'
5 | -env:
6 | - DISPLAY=':99.0'
7 | script: bundle exec rake --trace
8 |
--------------------------------------------------------------------------------
/spec/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Dummy::Application
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application
5 | Dummy::Application.initialize!
6 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 |
3 | def welcome
4 | render text: "Welcome, your time zone is: #{Time.zone}", layout: 'application'
5 | end
6 |
7 | end
8 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Temporal: Regression Test
5 | <%= javascript_include_tag :application %>
6 |
7 |
8 | <%= yield %>
9 |
10 |
11 |
--------------------------------------------------------------------------------
/spec/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | gemfile = File.expand_path('../../../../Gemfile', __FILE__)
2 |
3 | if File.exist?(gemfile)
4 | ENV['BUNDLE_GEMFILE'] = gemfile
5 | require 'bundler'
6 | Bundler.setup
7 | end
8 |
9 | $:.unshift File.expand_path('../../../../lib', __FILE__)
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # General
2 | .bundle
3 | .rvmrc
4 | .powenv
5 |
6 | # Local config files
7 | Gemfile.lock
8 | .rvmrc
9 |
10 | # Build files
11 | distro/build
12 | pkg/
13 |
14 | # Tempfiles
15 | tmp/
16 | coverage
17 | capybara*
18 |
19 | # Dummy app
20 | spec/dummy/log
21 | spec/dummy/db/*.sqlite3
22 |
--------------------------------------------------------------------------------
/spec/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | # Add your own tasks in files placed in lib/tasks ending in .rake,
3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4 |
5 | require File.expand_path('../config/application', __FILE__)
6 |
7 | Dummy::Application.load_tasks
8 |
--------------------------------------------------------------------------------
/spec/dummy/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/lib/temporal/engine.rb:
--------------------------------------------------------------------------------
1 | require 'temporal/controller_additions'
2 |
3 | module Temporal
4 | class Engine < ::Rails::Engine
5 | initializer "temporal.controller_additions" do
6 | ActiveSupport.on_load(:action_controller) do
7 | include Temporal::ControllerAdditions
8 | end
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 |
3 | gemspec
4 |
5 | # used by the dummy application
6 | gem 'rails', '>= 3.2.8'
7 |
8 | group :development, :test do
9 | gem 'rspec-rails'
10 | gem 'uglifier'
11 | gem 'teabag'
12 |
13 | # required for travis-ci and linux environments
14 | gem "phantomjs-linux" if RUBY_PLATFORM =~ /linux/
15 | end
16 |
--------------------------------------------------------------------------------
/lib/temporal/controller_additions.rb:
--------------------------------------------------------------------------------
1 | module Temporal
2 | module ControllerAdditions
3 | def self.included(base)
4 | if Rails::VERSION::MAJOR >= 5
5 | base.send(:before_action, :set_time_zone)
6 | else
7 | base.send(:before_filter, :set_time_zone)
8 | end
9 | end
10 |
11 | def set_time_zone
12 | Time.zone = cookies[:timezone] ? ActiveSupport::TimeZone.new(cookies[:timezone]) : Rails.application.config.time_zone
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/engine/temporal_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Temporal do
4 |
5 | it "is a module" do
6 | Temporal.should be_a(Module)
7 | end
8 |
9 | it "has a version" do
10 | Temporal::VERSION.should be_a(String)
11 | end
12 |
13 | it "defines ControllerAdditions" do
14 | Temporal::ControllerAdditions.should be_a(Module)
15 | end
16 |
17 | it "includes ControllerAdditions in ActionController::Base" do
18 | ActionController::Base.new.methods.should include(:set_time_zone)
19 | end
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/spec/controllers/application_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe ApplicationController, type: :controller do
4 |
5 | before do
6 | Rails.application.config.time_zone = 'Central Time (US & Canada)'
7 | end
8 |
9 | it "set the timezone to the configured default if the cookie isn't set" do
10 | get :welcome
11 | Time.zone.to_s.should =~ /Central Time/
12 | response.code.should == "200"
13 | end
14 |
15 | it "set the timezone based on the cookie" do
16 | @request.cookies[:timezone] = 'America/Denver'
17 | get :welcome
18 | Time.zone.to_s.should =~ /Denver/
19 | response.code.should == "200"
20 | end
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/temporal-rails.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | $:.push File.expand_path("../lib", __FILE__)
3 | require 'temporal/version'
4 |
5 | Gem::Specification.new do |s|
6 | s.name = 'temporal-rails'
7 | s.version = Temporal::VERSION
8 | s.authors = ['Jeremy Jackson']
9 | s.email = ['jejacks0n@gmail.com']
10 | s.homepage = 'http://github.com/jejacks0n/temporal'
11 | s.summary = 'Temporal: Javascript timezone detection for Rails'
12 | s.description = 'Javascript timezone detection that also sets Time.zone for Rails to use'
13 | s.licenses = ['MIT']
14 |
15 | s.files = Dir["{lib,vendor}/**/*"] + ["MIT.LICENSE", "README.md"]
16 | s.test_files = Dir["{spec}/**/*"]
17 |
18 | # Runtime Dependencies
19 | s.add_dependency 'railties', ['>= 3.2.5']
20 | s.add_dependency 'coffee-rails'
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/MIT.LICENSE:
--------------------------------------------------------------------------------
1 | Temporal uses Javascript timezone detection and sets Time.zone for
2 | Rails to use.
3 |
4 | Documentation and other useful information can be found at
5 | https://github.com/jejacks0n/temporal
6 |
7 | Copyright (c) 2012 Jeremy Jackson
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining
10 | a copy of this software and associated documentation files (the
11 | "Software"), to deal in the Software without restriction, including
12 | without limitation the rights to use, copy, modify, merge, publish,
13 | distribute, sublicense, and/or sell copies of the Software, and to
14 | permit persons to whom the Software is furnished to do so, subject to
15 | the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be
18 | included in all copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Log error messages when you accidentally call methods on nil.
10 | config.whiny_nils = true
11 |
12 | # Show full error reports and disable caching
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send
17 | #config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger
20 | config.active_support.deprecation = :log
21 |
22 | # Only use best-standards-support built into browsers
23 | config.action_dispatch.best_standards_support = :builtin
24 |
25 | # Raise exception on mass assignment protection for Active Record models
26 | #config.active_record.mass_assignment_sanitizer = :strict
27 |
28 | # Log the query plan for queries taking more than this (works
29 | # with SQLite, MySQL, and PostgreSQL)
30 | #config.active_record.auto_explain_threshold_in_seconds = 0.5
31 |
32 | # Do not compress assets
33 | config.assets.compress = false
34 |
35 | # Expands the lines which load the assets
36 | config.assets.debug = true
37 | end
38 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] ||= 'test'
2 | ENV['RAILS_ROOT'] = File.expand_path('../dummy', __FILE__)
3 | require File.expand_path('../dummy/config/environment', __FILE__)
4 |
5 | require 'rspec/rails'
6 | require 'rspec/autorun'
7 |
8 | # Requires supporting ruby files with custom matchers and macros, etc,
9 | # in spec/support/ and its subdirectories.
10 | Dir[Temporal::Engine.root.join('spec/support/**/*.rb')].each { |f| require f }
11 |
12 | RSpec.configure do |config|
13 | # ## Mock Framework
14 | #
15 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
16 | #
17 | # config.mock_with :mocha
18 | # config.mock_with :flexmock
19 | # config.mock_with :rr
20 |
21 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
22 | #config.fixture_path = "#{::Temporal::Engine.root}/spec/fixtures"
23 |
24 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
25 | # examples within a transaction, remove the following line or assign false
26 | # instead of true.
27 | #config.use_transactional_fixtures = true
28 |
29 | # If true, the base class of anonymous controllers will be inferred
30 | # automatically. This will be the default behavior in future versions of
31 | # rspec-rails.
32 | config.infer_base_class_for_anonymous_controllers = false
33 |
34 | # Run specs in random order to surface order dependencies. If you find an
35 | # order dependency and want to debug it, you can fix the order by providing
36 | # the seed, which is printed after each run.
37 | # --seed 1234
38 | config.order = "random"
39 | end
40 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Configure static asset server for tests with Cache-Control for performance
11 | config.serve_static_assets = true
12 | config.static_cache_control = "public, max-age=3600"
13 |
14 | # Log error messages when you accidentally call methods on nil
15 | config.whiny_nils = true
16 |
17 | # Show full error reports and disable caching
18 | config.consider_all_requests_local = true
19 | config.action_controller.perform_caching = false
20 |
21 | # Raise exceptions instead of rendering exception templates
22 | config.action_dispatch.show_exceptions = false
23 |
24 | # Disable request forgery protection in test environment
25 | config.action_controller.allow_forgery_protection = false
26 |
27 | # Tell Action Mailer not to deliver emails to the real world.
28 | # The :test delivery method accumulates sent emails in the
29 | # ActionMailer::Base.deliveries array.
30 | #config.action_mailer.delivery_method = :test
31 |
32 | # Raise exception on mass assignment protection for Active Record models
33 | #config.active_record.mass_assignment_sanitizer = :strict
34 |
35 | # Print deprecation notices to the stderr
36 | config.active_support.deprecation = :stderr
37 | end
38 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
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 |
8 | # Dummy App
9 | # -----------------------------------------------------------------------------
10 | APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
11 | load 'rails/tasks/engine.rake'
12 | Bundler::GemHelper.install_tasks
13 |
14 | # RSpec
15 | # -----------------------------------------------------------------------------
16 | task 'db:test:prepare' => 'app:db:test:prepare'
17 | load 'rspec/rails/tasks/rspec.rake'
18 |
19 | namespace :spec do
20 |
21 | [:engine].each do |sub|
22 | desc "Run the code examples in spec/#{sub}"
23 | RSpec::Core::RakeTask.new(sub => 'db:test:prepare') do |t|
24 | t.pattern = "./spec/#{sub}/**/*_spec.rb"
25 | end
26 | end
27 |
28 | end
29 |
30 | # Teabag
31 | # -----------------------------------------------------------------------------
32 | desc "Run javascript specs"
33 | task :teabag => 'app:teabag'
34 |
35 | # Temporal
36 | # -----------------------------------------------------------------------------
37 | namespace :temporal do
38 | desc "Builds Temporal into the distribution ready bundle"
39 | task :build => "build:javascripts"
40 |
41 | namespace :build do
42 |
43 | desc "Compile coffeescripts into javacripts"
44 | task :javascripts => :environment do
45 | env = Rails.application.assets
46 |
47 | %w(temporal.js).each do |path|
48 | asset = env.find_asset(path)
49 | asset.write_to(Temporal::Engine.root.join("distro/#{path}"))
50 | File.open(Temporal::Engine.root.join("distro/#{path.gsub(/\.js/, '.min.js')}"), 'w') do |file|
51 | file.write(Uglifier.compile(asset.source))
52 | end
53 | end
54 | end
55 |
56 | end
57 |
58 | end
59 |
60 | # Default
61 | # -----------------------------------------------------------------------------
62 | Rake::Task['default'].prerequisites.clear
63 | Rake::Task['default'].clear
64 |
65 | task :default => [:spec, :teabag]
66 |
--------------------------------------------------------------------------------
/spec/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.routes.draw do
2 |
3 | root to: 'application#welcome'
4 |
5 | # The priority is based upon order of creation:
6 | # first created -> highest priority.
7 |
8 | # Sample of regular route:
9 | # match 'products/:id' => 'catalog#view'
10 | # Keep in mind you can assign values other than :controller and :action
11 |
12 | # Sample of named route:
13 | # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
14 | # This route can be invoked with purchase_url(:id => product.id)
15 |
16 | # Sample resource route (maps HTTP verbs to controller actions automatically):
17 | # resources :products
18 |
19 | # Sample resource route with options:
20 | # resources :products do
21 | # member do
22 | # get 'short'
23 | # post 'toggle'
24 | # end
25 | #
26 | # collection do
27 | # get 'sold'
28 | # end
29 | # end
30 |
31 | # Sample resource route with sub-resources:
32 | # resources :products do
33 | # resources :comments, :sales
34 | # resource :seller
35 | # end
36 |
37 | # Sample resource route with more complex sub-resources
38 | # resources :products do
39 | # resources :comments
40 | # resources :sales do
41 | # get 'recent', :on => :collection
42 | # end
43 | # end
44 |
45 | # Sample resource route within a namespace:
46 | # namespace :admin do
47 | # # Directs /admin/products/* to Admin::ProductsController
48 | # # (app/controllers/admin/products_controller.rb)
49 | # resources :products
50 | # end
51 |
52 | # You can have the root of your site routed with "root"
53 | # just remember to delete public/index.html.
54 | # root :to => 'welcome#index'
55 |
56 | # See how all your routes lay out with "rake routes"
57 |
58 | # This is a legacy wild controller route that's not recommended for RESTful applications.
59 | # Note: This route will make all actions in every controller accessible via GET requests.
60 | # match ':controller(/:action(/:id))(.:format)'
61 |
62 | end
63 |
--------------------------------------------------------------------------------
/spec/javascripts/temporal_spec.coffee:
--------------------------------------------------------------------------------
1 | #= require temporal
2 |
3 | describe "Temporal", ->
4 |
5 | beforeEach ->
6 | document.cookie = 'timezone='
7 | document.cookie = 'timezone_offset='
8 | @timezoneStub = {name: 'foo', offset: 0} # 0 here ensure the jsonp request isn't made
9 |
10 | describe "signature", ->
11 |
12 | it "has a detect and reference method", ->
13 | expect(Object.keys(Temporal).length).toBe 2
14 | expect(Object.keys(Temporal)).toEqual ['detect', 'reference']
15 | expect(typeof(Temporal.detect)).toBe 'function'
16 | expect(typeof(Temporal.reference)).toBe 'function'
17 |
18 |
19 | describe ".detect", ->
20 |
21 | beforeEach ->
22 | window.Temporal = Temporal.reference() if Temporal.reference
23 | @spy = spyOn(Temporal.prototype, 'detectLocally').andReturn @timezoneStub
24 | @callback = ->
25 |
26 | it "instantiates an instance and passes arguments", ->
27 | instance = Temporal.detect('username', @callback)
28 | expect(@spy).toHaveBeenCalled()
29 | expect(instance.username).toBe 'username'
30 | expect(instance.callback).toBe @callback
31 |
32 |
33 | describe "constructor", ->
34 |
35 | it "calls #detectLocally", ->
36 | spy = spyOn(Temporal.prototype, 'detectLocally').andReturn @timezoneStub
37 | new Temporal()
38 | expect(spy.callCount).toBe 1
39 |
40 | it "calls #geoLocate if there's a username for the GeoName API", ->
41 | spyOn(Temporal.prototype, 'detectLocally').andReturn name: 'foo', offset: 1
42 | spy = spyOn(Temporal.prototype, 'geoLocate')
43 | new Temporal('username')
44 | if navigator.geolocation
45 | expect(spy.callCount).toBe 1
46 |
47 | it "doesn't call #geoLocate if there isn't a username", ->
48 | spyOn(Temporal.prototype, 'detectLocally').andReturn name: 'foo', offset: 1
49 | spy = spyOn(Temporal.prototype, 'geoLocate')
50 | new Temporal()
51 | expect(spy.callCount).toBe 0
52 |
53 | it "calls #set", ->
54 | spyOn(Temporal.prototype, 'detectLocally').andReturn @timezoneStub
55 | spy = spyOn(Temporal.prototype, 'set')
56 | new Temporal()
57 | expect(spy.callCount).toBe 1
58 | expect(spy).toHaveBeenCalledWith name: 'foo', offset: 0
59 |
60 |
61 | describe "#detectLocally", ->
62 |
63 | beforeEach ->
64 | spyOn(Temporal.prototype, 'detect')
65 | @temporal = new Temporal()
66 |
67 | it "returns a quickly determined time zone", ->
68 | spyOn(Temporal.prototype, 'januaryOffset').andReturn -420
69 | spyOn(Temporal.prototype, 'juneOffset').andReturn -360
70 | timezone = @temporal.detectLocally()
71 | expect(timezone).toEqual name: 'America/Denver', offset: -7
72 |
73 | it "handles other locations than denver", ->
74 | spyOn(Temporal.prototype, 'januaryOffset').andReturn 120
75 | spyOn(Temporal.prototype, 'juneOffset').andReturn 120
76 | timezone = @temporal.detectLocally()
77 | expect(timezone).toEqual name: 'Africa/Johannesburg', offset: 2
78 |
--------------------------------------------------------------------------------
/spec/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'action_controller/railtie'
4 | require 'sprockets/railtie'
5 |
6 | if defined?(Bundler)
7 | # If you precompile assets before deploying to production, use this line
8 | Bundler.require(*Rails.groups(:assets => %w(development test)))
9 | # If you want your assets lazily compiled in production, use this line
10 | # Bundler.require(:default, :assets, Rails.env)
11 | end
12 |
13 | module Dummy
14 | class Application < Rails::Application
15 | # Settings in config/environments/* take precedence over those specified here.
16 | # Application configuration should go into files in config/initializers
17 | # -- all .rb files in that directory are automatically loaded.
18 |
19 | # Custom directories with classes and modules you want to be autoloadable.
20 | # config.autoload_paths += %W(#{config.root}/extras)
21 |
22 | # Only load the plugins named here, in the order given (default is alphabetical).
23 | # :all can be used as a placeholder for all plugins not explicitly named.
24 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
25 |
26 | # Activate observers that should always be running.
27 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
28 |
29 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
30 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
31 | # config.time_zone = 'Central Time (US & Canada)'
32 |
33 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
34 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
35 | # config.i18n.default_locale = :de
36 |
37 | # Configure the default encoding used in templates for Ruby 1.9.
38 | config.encoding = "utf-8"
39 |
40 | # Configure sensitive parameters which will be filtered from the log file.
41 | config.filter_parameters += [:password]
42 |
43 | # Enable escaping HTML in JSON.
44 | config.active_support.escape_html_entities_in_json = true
45 |
46 | # Use SQL instead of Active Record's schema dumper when creating the database.
47 | # This is necessary if your schema can't be completely dumped by the schema dumper,
48 | # like if you have constraints or database-specific column types
49 | #config.active_record.schema_format = :sql
50 |
51 | # Enforce whitelist mode for mass assignment.
52 | # This will create an empty whitelist of attributes available for mass-assignment for all models
53 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
54 | # parameters by using an attr_accessible or attr_protected declaration.
55 | #config.active_record.whitelist_attributes = true
56 |
57 | Dummy::Application.config.session_store :cookie_store, :key => '_dummy_session'
58 | Dummy::Application.config.secret_token = '990bac5b6d4f068883259503539cad4e9ec00f137986fcbc718a1e0b645af7a3eb0340468f8fae09958a6281190396d65789b6316af61e47a8cbda3c8c071a0e'
59 |
60 | # Enable the asset pipeline
61 | config.assets.enabled = true
62 |
63 | # Version of your assets, change this if you want to expire all your assets
64 | config.assets.version = '1.0'
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Temporal
2 |
3 | [](http://travis-ci.org/jejacks0n/temporal)
4 | [](http://opensource.org/licenses/MIT)
5 |
6 | Temporal is a small (~7.5k) Javascript library that uses a collection of techniques to determine a clients time zone.
7 | Once a time zone has been determined, a cookie is set which can be used on the server. Now you can display local times
8 | throughout the rest of the response cycle.
9 |
10 | The first method is based on checking various times on different dates to resolve to a short list of time zones. The
11 | short list is by no means extensive, and is meant to provide the time zone offset -- not specifically the location of
12 | the client. The data is comprised of the most useful aspects of the [Time Zone Database](http://www.iana.org/time-zones) which keeps the data loaded
13 | on the client small.
14 |
15 | The second method is to use the HTML5 Geolocation API combined with the [GeoNames API](http://www.geonames.org/export/web-services.html). Latitude and longitude is
16 | provided by the client (when approved), and the name/offset of the time zone is fetched using JSONP via the GeoNames
17 | API. This method provides much more accurate location information -- though I haven't been able to get the two to
18 | methods to disagree about the actual offset, so if you don't need this level of location accuracy you can get by
19 | without it.
20 |
21 |
22 | ## The Story
23 |
24 | If you've ever done time zone specific logic in your applications you know it's a bit of a beast. First, you'll need
25 | to ask each user where they're located to know what time zone you should use when displaying any times converted to
26 | their local time. Wouldn't it be nice not to have to ask users for their time zone? That's one step removed from your
27 | sign up / configuration process, and maybe you don't even have a sign up process, in which case it's even harder to
28 | display local times.
29 |
30 | I haven't found a really good solution for detecting a users time zone, and I'm not happy asking a user for it, so I
31 | wrote Temporal to determine it for me before ever having to ask. I'm putting it out there in case anyone else finds it
32 | useful as a tool, or a learning opportunity.
33 |
34 |
35 | ## Installation
36 |
37 | ### Rails
38 |
39 | gem 'temporal-rails'
40 |
41 | Then require temporal in your application.js:
42 |
43 | //= require temporal
44 |
45 | ### Just the Javascript?
46 |
47 | Download [temporal.js](https://raw.github.com/jejacks0n/temporal/master/distro/temporal.js) or [temporal.min.js](https://raw.github.com/jejacks0n/temporal/master/distro/temporal.min.js)
48 | and add them to your project. The same API applies, but you would need to consume the cookie that's set on the back
49 | end -- using data similar to [TZInfo](http://tzinfo.rubyforge.org/).
50 |
51 |
52 | ## Usage
53 |
54 | There's really not much to it. Call `Temporal.detect()` to trigger the detection. This method sets a cookie on the
55 | client, and the next request to the server will have that cookie, letting us use it to set the Rails Time.zone (which
56 | is done for you in the Gem). It's important to note that the exact location of the user isn't guaranteed, so it should
57 | be treated as the time zone, and not the location (eg. don't display it unless you have a way to change it).
58 |
59 | The `Temporal.detect` method takes two arguments:
60 |
61 | - your GeoNames username -- can be created [here](http://www.geonames.org/login) and also turn on the web service [here](http://www.geonames.org/manageaccount) (it's near the bottom)
62 | - a callback function
63 |
64 | If you don't provide the first argument the HTML5 geolocation and GeoNames APIs will not be used.
65 |
66 | The callback is called whenever the time zone is set or changed -- it can be called twice if you're using the GeoName
67 | API because Temporal first does the quick detection and sets the cookie based on that, and then calls through to the
68 | GeoNames API. Since the GoeNames API uses JSONP, the reponse may take a moment to update the cookie further and with
69 | more accuracy.
70 |
71 | More reading about time zone handling in rails: [What time is it? Or, handling timezones in Rails](http://databasically.com/2010/10/22/what-time-is-it-or-handling-timezones-in-rails/)
72 |
73 | Temporal is as efficient as possible when it comes to refreshing the users time zone and using the GeoNames API. It
74 | does this by caching the determined time zone for a month, and does a quick check on each page load to see if it looks
75 | like the users time zone has changed (if they're traveling, or have moved, etc.). If it looks like the time zone may
76 | have changed it will trigger the GoeNames API hit again for clarification.
77 |
78 |
79 | ## License
80 |
81 | Licensed under the [MIT License](http://opensource.org/licenses/mit-license.php)
82 |
83 | Copyright 2012 [Jeremy Jackson](https://github.com/jejacks0n)
84 |
85 |
86 | ## Enjoy =)
87 |
--------------------------------------------------------------------------------
/distro/temporal.min.js:
--------------------------------------------------------------------------------
1 | (function(){var e,t,n,r,i,s,o,u,a=function(e,t){return function(){return e.apply(t,arguments)}},f={}.hasOwnProperty;o=function(){function t(e,t){this.username=e!=null?e:null,this.callback=t!=null?t:null,this.parseGeoResponse=a(this.parseGeoResponse,this),this.geoSuccess=a(this.geoSuccess,this),this.detect()}var e;return e="geoSuccessCallback"+parseInt(Math.random()*1e4),t.detect=function(e,n){return e==null&&(e=null),n==null&&(n=null),new t(e,n)},t.prototype.detect=function(){var e;return e=this.detectLocally(),this.username&&navigator.geolocation&&e.offset!==this.get().offset&&this.geoLocate(),this.set(e)},t.prototype.detectLocally=function(){var e,t,s;return e=this.januaryOffset(),t=this.juneOffset(),s={offset:e,dst:0,hemisphere:i},e-t<0?s={offset:e,dst:1,hemisphere:n}:e-t>0&&(s={offset:t,dst:1,hemisphere:r}),new u(""+[s.offset,s.dst].join(",")+(s.hemisphere===r?",s":""))},t.prototype.geoLocate=function(){return navigator.geolocation.getCurrentPosition(this.geoSuccess,function(){})},t.prototype.geoSuccess=function(t){var n;return window[e]=this.parseGeoResponse,n=document.createElement("script"),n.setAttribute("src","http://api.geonames.org/timezoneJSON?lat="+t.coords.latitude+"&lng="+t.coords.longitude+"&username="+this.username+"&callback="+e),document.getElementsByTagName("head")[0].appendChild(n)},t.prototype.parseGeoResponse=function(t){delete window[e];if(t.timezoneId)return this.set(new u({name:t.timezoneId,offset:t.rawOffset}))},t.prototype.set=function(e){var t;return this.timezone=e,window.timezone=this.timezone,t=new Date,t.setMonth(t.getMonth()+1),document.cookie="timezone="+this.timezone.name+"; expires="+t.toGMTString(),document.cookie="timezone_offset="+this.timezone.offset+"; expires="+t.toGMTString(),typeof this.callback=="function"?this.callback(this.timezone):void 0},t.prototype.get=function(){return{name:this.getCookie("timezone"),offset:parseFloat(this.getCookie("timezone_offset"))||0}},t.prototype.getCookie=function(e){var t;return t=document.cookie.match(new RegExp("(?:^|;)\\s?"+e+"=(.*?)(?:;|$)","i")),t&&unescape(t[1])},t.prototype.januaryOffset=function(){return this.dateOffset(new Date(2011,0,1,0,0,0,0))},t.prototype.juneOffset=function(){return this.dateOffset(new Date(2011,5,1,0,0,0,0))},t.prototype.dateOffset=function(e){return-e.getTimezoneOffset()},t}.call(this),u=function(){function i(e){var t,n,i;if(typeof e=="string"){i=s[e];for(t in i){if(!f.call(i,t))continue;n=i[t],this[t]=n}r()}else for(t in e){if(!f.call(e,t))continue;n=e[t],this[t]=n}}var n,r;return n=function(e){return(e.getMonth()>5?this.juneOffset():this.januaryOffset())-this.dateOffset(e)!==0},r=function(){var r,i,s;r=e[this.name];if(typeof r=="undefined")return;for(i in r){s=r[i];if(n(t[s])){this.name=s;return}}},i}(),this.Temporal={detect:o.detect,reference:function(){return o}},r="SOUTH",n="NORTH",i="N/A",e={"America/Denver":["America/Denver","America/Mazatlan"],"America/Chicago":["America/Chicago","America/Mexico_City"],"America/Asuncion":["Atlantic/Stanley","America/Asuncion","America/Santiago","America/Campo_Grande"],"America/Montevideo":["America/Montevideo","America/Sao_Paolo"],"Asia/Beirut":["Asia/Gaza","Asia/Beirut","Europe/Minsk","Europe/Istanbul","Asia/Damascus","Asia/Jerusalem","Africa/Cairo"],"Asia/Yerevan":["Asia/Yerevan","Asia/Baku"],"Pacific/Auckland":["Pacific/Auckland","Pacific/Fiji"],"America/Los_Angeles":["America/Los_Angeles","America/Santa_Isabel"],"America/New_York":["America/Havana","America/New_York"],"America/Halifax":["America/Goose_Bay","America/Halifax"],"America/Godthab":["America/Miquelon","America/Godthab"]},t={"America/Denver":new Date(2011,2,13,3,0,0,0),"America/Mazatlan":new Date(2011,3,3,3,0,0,0),"America/Chicago":new Date(2011,2,13,3,0,0,0),"America/Mexico_City":new Date(2011,3,3,3,0,0,0),"Atlantic/Stanley":new Date(2011,8,4,7,0,0,0),"America/Asuncion":new Date(2011,9,2,3,0,0,0),"America/Santiago":new Date(2011,9,9,3,0,0,0),"America/Campo_Grande":new Date(2011,9,16,5,0,0,0),"America/Montevideo":new Date(2011,9,2,3,0,0,0),"America/Sao_Paolo":new Date(2011,9,16,5,0,0,0),"America/Los_Angeles":new Date(2011,2,13,8,0,0,0),"America/Santa_Isabel":new Date(2011,3,5,8,0,0,0),"America/Havana":new Date(2011,2,13,2,0,0,0),"America/New_York":new Date(2011,2,13,7,0,0,0),"Asia/Gaza":new Date(2011,2,26,23,0,0,0),"Asia/Beirut":new Date(2011,2,27,1,0,0,0),"Europe/Minsk":new Date(2011,2,27,3,0,0,0),"Europe/Istanbul":new Date(2011,2,27,7,0,0,0),"Asia/Damascus":new Date(2011,3,1,2,0,0,0),"Asia/Jerusalem":new Date(2011,3,1,6,0,0,0),"Africa/Cairo":new Date(2011,3,29,4,0,0,0),"Asia/Yerevan":new Date(2011,2,27,4,0,0,0),"Asia/Baku":new Date(2011,2,27,8,0,0,0),"Pacific/Auckland":new Date(2011,8,26,7,0,0,0),"Pacific/Fiji":new Date(2010,11,29,23,0,0,0),"America/Halifax":new Date(2011,2,13,6,0,0,0),"America/Goose_Bay":new Date(2011,2,13,2,1,0,0),"America/Miquelon":new Date(2011,2,13,5,0,0,0),"America/Godthab":new Date(2011,2,27,1,0,0,0)},s={"-720,0":{offset:-12,name:"Etc/GMT+12"},"-660,0":{offset:-11,name:"Pacific/Pago_Pago"},"-600,1":{offset:-11,name:"America/Adak"},"-660,1,s":{offset:-11,name:"Pacific/Apia"},"-600,0":{offset:-10,name:"Pacific/Honolulu"},"-570,0":{offset:-10.5,name:"Pacific/Marquesas"},"-540,0":{offset:-9,name:"Pacific/Gambier"},"-540,1":{offset:-9,name:"America/Anchorage"},"-480,1":{offset:-8,name:"America/Los_Angeles"},"-480,0":{offset:-8,name:"Pacific/Pitcairn"},"-420,0":{offset:-7,name:"America/Phoenix"},"-420,1":{offset:-7,name:"America/Denver"},"-360,0":{offset:-6,name:"America/Guatemala"},"-360,1":{offset:-6,name:"America/Chicago"},"-360,1,s":{offset:-6,name:"Pacific/Easter"},"-300,0":{offset:-5,name:"America/Bogota"},"-300,1":{offset:-5,name:"America/New_York"},"-270,0":{offset:-4.5,name:"America/Caracas"},"-240,1":{offset:-4,name:"America/Halifax"},"-240,0":{offset:-4,name:"America/Santo_Domingo"},"-240,1,s":{offset:-4,name:"America/Asuncion"},"-210,1":{offset:-3.5,name:"America/St_Johns"},"-180,1":{offset:-3,name:"America/Godthab"},"-180,0":{offset:-3,name:"America/Argentina/Buenos_Aires"},"-180,1,s":{offset:-3,name:"America/Montevideo"},"-120,0":{offset:-2,name:"America/Noronha"},"-120,1":{offset:-2,name:"Etc/GMT+2"},"-60,1":{offset:-1,name:"Atlantic/Azores"},"-60,0":{offset:-1,name:"Atlantic/Cape_Verde"},"0,0":{offset:0,name:"Africa/Casablanca"},"0,1":{offset:0,name:"Europe/London"},"60,1":{offset:1,name:"Europe/Berlin"},"60,0":{offset:1,name:"Africa/Lagos"},"60,1,s":{offset:1,name:"Africa/Windhoek"},"120,1":{offset:2,name:"Asia/Beirut"},"120,0":{offset:2,name:"Africa/Johannesburg"},"180,1":{offset:3,name:"Europe/Moscow"},"180,0":{offset:3,name:"Asia/Baghdad"},"210,1":{offset:3.5,name:"Asia/Tehran"},"240,0":{offset:4,name:"Asia/Dubai"},"240,1":{offset:4,name:"Asia/Yerevan"},"270,0":{offset:4.5,name:"Asia/Kabul"},"300,1":{offset:5,name:"Asia/Yekaterinburg"},"300,0":{offset:5,name:"Asia/Karachi"},"330,0":{offset:5,name:"Asia/Kolkata"},"345,0":{offset:5.75,name:"Asia/Kathmandu"},"360,0":{offset:6,name:"Asia/Dhaka"},"360,1":{offset:6,name:"Asia/Omsk"},"390,0":{offset:6,name:"Asia/Rangoon"},"420,1":{offset:7,name:"Asia/Krasnoyarsk"},"420,0":{offset:7,name:"Asia/Jakarta"},"480,0":{offset:8,name:"Asia/Shanghai"},"480,1":{offset:8,name:"Asia/Irkutsk"},"525,0":{offset:8.75,name:"Australia/Eucla"},"525,1,s":{offset:8.75,name:"Australia/Eucla"},"540,1":{offset:9,name:"Asia/Yakutsk"},"540,0":{offset:9,name:"Asia/Tokyo"},"570,0":{offset:9.5,name:"Australia/Darwin"},"570,1,s":{offset:9.5,name:"Australia/Adelaide"},"600,0":{offset:10,name:"Australia/Brisbane"},"600,1":{offset:10,name:"Asia/Vladivostok"},"600,1,s":{offset:10,name:"Australia/Sydney"},"630,1,s":{offset:10.5,name:"Australia/Lord_Howe"},"660,1":{offset:11,name:"Asia/Kamchatka"},"660,0":{offset:11,name:"Pacific/Noumea"},"690,0":{offset:11.5,name:"Pacific/Norfolk"},"720,1,s":{offset:12,name:"Pacific/Auckland"},"720,0":{offset:12,name:"Pacific/Tarawa"},"765,1,s":{offset:12.75,name:"Pacific/Chatham"},"780,0":{offset:13,name:"Pacific/Tongatapu"},"840,0":{offset:14,name:"Pacific/Kiritimati"}}}).call(this);
2 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/temporal.js.coffee:
--------------------------------------------------------------------------------
1 | # Temporal -- use detect() to detect, and reference() if you need a reference
2 | # to the class so you can use it.
3 | # -----------------------------------------------------------------------------
4 | class Temporal
5 |
6 | jsonpCallback = "geoSuccessCallback#{parseInt(Math.random() * 10000)}"
7 |
8 | @detect: (username = null, callback = null) =>
9 | new Temporal(username, callback)
10 |
11 | constructor: (@username = null, @callback = null) ->
12 | @detect()
13 |
14 | detect: ->
15 | timezone = @detectLocally()
16 | @geoLocate() if @username and navigator.geolocation and timezone.offset != @get().offset
17 | @set(timezone)
18 |
19 | detectLocally: ->
20 | januaryOffset = @januaryOffset()
21 | juneOffset = @juneOffset()
22 | key = {offset: januaryOffset, dst: 0, hemisphere: HEMISPHERE_UNKNOWN}
23 | if januaryOffset - juneOffset < 0
24 | key = {offset: januaryOffset, dst: 1, hemisphere: HEMISPHERE_NORTH}
25 | else if januaryOffset - juneOffset > 0
26 | key = {offset: juneOffset, dst: 1, hemisphere: HEMISPHERE_SOUTH}
27 | new TimeZone("#{([key.offset, key.dst].join(','))}#{if key.hemisphere is HEMISPHERE_SOUTH then ',s' else ''}")
28 |
29 | geoLocate: ->
30 | navigator.geolocation.getCurrentPosition(@geoSuccess, ->)
31 |
32 | geoSuccess: (position) =>
33 | window[jsonpCallback] = @parseGeoResponse
34 | script = document.createElement('script')
35 | script.setAttribute('src', "http://api.geonames.org/timezoneJSON?lat=#{position.coords.latitude}&lng=#{position.coords.longitude}&username=#{@username}&callback=#{jsonpCallback}")
36 | document.getElementsByTagName('head')[0].appendChild(script)
37 |
38 | parseGeoResponse: (response) =>
39 | delete(window[jsonpCallback])
40 | @set(new TimeZone(name: response.timezoneId, offset: response.rawOffset)) if response.timezoneId
41 |
42 | set: (@timezone) ->
43 | window.timezone = @timezone
44 | expiration = new Date()
45 | expiration.setMonth(expiration.getMonth() + 1)
46 | document.cookie = "timezone=#{@timezone.name}; expires=#{expiration.toGMTString()}"
47 | document.cookie = "timezone_offset=#{@timezone.offset}; expires=#{expiration.toGMTString()}"
48 | @callback?(@timezone)
49 |
50 | get: ->
51 | name: @getCookie('timezone')
52 | offset: parseFloat(@getCookie('timezone_offset')) || 0
53 |
54 | getCookie: (name) ->
55 | match = document.cookie.match(new RegExp("(?:^|;)\\s?#{name}=(.*?)(?:;|$)", 'i'))
56 | match && unescape(match[1])
57 |
58 | januaryOffset: ->
59 | @dateOffset(new Date(2011, 0, 1, 0, 0, 0, 0))
60 |
61 | juneOffset: ->
62 | @dateOffset(new Date(2011, 5, 1, 0, 0, 0, 0))
63 |
64 | dateOffset: (date) ->
65 | -date.getTimezoneOffset()
66 |
67 |
68 | # Timezone -- contains offset and timezone name
69 | # -----------------------------------------------------------------------------
70 | class TimeZone
71 |
72 | dateIsDst = (date) ->
73 | (((if date.getMonth() > 5 then @juneOffset() else @januaryOffset())) - @dateOffset(date)) isnt 0
74 |
75 | resolveAmbiguity = ->
76 | ambiguous = AMBIGIOUS_ZONES[@name]
77 | return if typeof(ambiguous) is 'undefined'
78 | for key, value of ambiguous
79 | if dateIsDst(DST_START_DATES[value])
80 | @name = value
81 | return
82 |
83 | constructor: (keyOrProperties) ->
84 | if typeof(keyOrProperties) == 'string'
85 | zone = TIMEZONES[keyOrProperties]
86 | @[property] = value for own property, value of zone
87 | resolveAmbiguity()
88 | else
89 | @[property] = value for own property, value of keyOrProperties
90 |
91 |
92 | # Expose Temporal to the global scope
93 | # -----------------------------------------------------------------------------
94 | @Temporal = {detect: Temporal.detect, reference: -> Temporal}
95 |
96 |
97 | # Data
98 | # -----------------------------------------------------------------------------
99 | HEMISPHERE_SOUTH = 'SOUTH'
100 | HEMISPHERE_NORTH = 'NORTH'
101 | HEMISPHERE_UNKNOWN = 'N/A'
102 | AMBIGIOUS_ZONES =
103 | 'America/Denver': ['America/Denver', 'America/Mazatlan']
104 | 'America/Chicago': ['America/Chicago', 'America/Mexico_City']
105 | 'America/Asuncion': ['Atlantic/Stanley', 'America/Asuncion', 'America/Santiago', 'America/Campo_Grande']
106 | 'America/Montevideo': ['America/Montevideo', 'America/Sao_Paolo']
107 | 'Asia/Beirut': ['Asia/Gaza', 'Asia/Beirut', 'Europe/Minsk', 'Europe/Istanbul', 'Asia/Damascus', 'Asia/Jerusalem', 'Africa/Cairo']
108 | 'Asia/Yerevan': ['Asia/Yerevan', 'Asia/Baku']
109 | 'Pacific/Auckland': ['Pacific/Auckland', 'Pacific/Fiji']
110 | 'America/Los_Angeles': ['America/Los_Angeles', 'America/Santa_Isabel']
111 | 'America/New_York': ['America/Havana', 'America/New_York']
112 | 'America/Halifax': ['America/Goose_Bay', 'America/Halifax']
113 | 'America/Godthab': ['America/Miquelon', 'America/Godthab']
114 | DST_START_DATES =
115 | 'America/Denver': new Date(2011, 2, 13, 3, 0, 0, 0)
116 | 'America/Mazatlan': new Date(2011, 3, 3, 3, 0, 0, 0)
117 | 'America/Chicago': new Date(2011, 2, 13, 3, 0, 0, 0)
118 | 'America/Mexico_City': new Date(2011, 3, 3, 3, 0, 0, 0)
119 | 'Atlantic/Stanley': new Date(2011, 8, 4, 7, 0, 0, 0)
120 | 'America/Asuncion': new Date(2011, 9, 2, 3, 0, 0, 0)
121 | 'America/Santiago': new Date(2011, 9, 9, 3, 0, 0, 0)
122 | 'America/Campo_Grande': new Date(2011, 9, 16, 5, 0, 0, 0)
123 | 'America/Montevideo': new Date(2011, 9, 2, 3, 0, 0, 0)
124 | 'America/Sao_Paolo': new Date(2011, 9, 16, 5, 0, 0, 0)
125 | 'America/Los_Angeles': new Date(2011, 2, 13, 8, 0, 0, 0)
126 | 'America/Santa_Isabel': new Date(2011, 3, 5, 8, 0, 0, 0)
127 | 'America/Havana': new Date(2011, 2, 13, 2, 0, 0, 0)
128 | 'America/New_York': new Date(2011, 2, 13, 7, 0, 0, 0)
129 | 'Asia/Gaza': new Date(2011, 2, 26, 23, 0, 0, 0)
130 | 'Asia/Beirut': new Date(2011, 2, 27, 1, 0, 0, 0)
131 | 'Europe/Minsk': new Date(2011, 2, 27, 3, 0, 0, 0)
132 | 'Europe/Istanbul': new Date(2011, 2, 27, 7, 0, 0, 0)
133 | 'Asia/Damascus': new Date(2011, 3, 1, 2, 0, 0, 0)
134 | 'Asia/Jerusalem': new Date(2011, 3, 1, 6, 0, 0, 0)
135 | 'Africa/Cairo': new Date(2011, 3, 29, 4, 0, 0, 0)
136 | 'Asia/Yerevan': new Date(2011, 2, 27, 4, 0, 0, 0)
137 | 'Asia/Baku': new Date(2011, 2, 27, 8, 0, 0, 0)
138 | 'Pacific/Auckland': new Date(2011, 8, 26, 7, 0, 0, 0)
139 | 'Pacific/Fiji': new Date(2010, 11, 29, 23, 0, 0, 0)
140 | 'America/Halifax': new Date(2011, 2, 13, 6, 0, 0, 0)
141 | 'America/Goose_Bay': new Date(2011, 2, 13, 2, 1, 0, 0)
142 | 'America/Miquelon': new Date(2011, 2, 13, 5, 0, 0, 0)
143 | 'America/Godthab': new Date(2011, 2, 27, 1, 0, 0, 0)
144 | TIMEZONES =
145 | '-720,0': {offset: -12, name: 'Etc/GMT+12'}
146 | '-660,0': {offset: -11, name: 'Pacific/Pago_Pago'}
147 | '-600,1': {offset: -11, name: 'America/Adak'}
148 | '-660,1,s': {offset: -11, name: 'Pacific/Apia'}
149 | '-600,0': {offset: -10, name: 'Pacific/Honolulu'}
150 | '-570,0': {offset: -10.5, name: 'Pacific/Marquesas'}
151 | '-540,0': {offset: -9, name: 'Pacific/Gambier'}
152 | '-540,1': {offset: -9, name: 'America/Anchorage'}
153 | '-480,1': {offset: -8, name: 'America/Los_Angeles'}
154 | '-480,0': {offset: -8, name: 'Pacific/Pitcairn'}
155 | '-420,0': {offset: -7, name: 'America/Phoenix'}
156 | '-420,1': {offset: -7, name: 'America/Denver'}
157 | '-360,0': {offset: -6, name: 'America/Guatemala'}
158 | '-360,1': {offset: -6, name: 'America/Chicago'}
159 | '-360,1,s': {offset: -6, name: 'Pacific/Easter'}
160 | '-300,0': {offset: -5, name: 'America/Bogota'}
161 | '-300,1': {offset: -5, name: 'America/New_York'}
162 | '-270,0': {offset: -4.5, name: 'America/Caracas'}
163 | '-240,1': {offset: -4, name: 'America/Halifax'}
164 | '-240,0': {offset: -4, name: 'America/Santo_Domingo'}
165 | '-240,1,s': {offset: -4, name: 'America/Asuncion'}
166 | '-210,1': {offset: -3.5, name: 'America/St_Johns'}
167 | '-180,1': {offset: -3, name: 'America/Godthab'}
168 | '-180,0': {offset: -3, name: 'America/Argentina/Buenos_Aires,'}
169 | '-180,1,s': {offset: -3, name: 'America/Montevideo'}
170 | '-120,0': {offset: -2, name: 'America/Noronha'}
171 | '-120,1': {offset: -2, name: 'Etc/GMT+2'}
172 | '-60,1': {offset: -1, name: 'Atlantic/Azores'}
173 | '-60,0': {offset: -1, name: 'Atlantic/Cape_Verde'}
174 | '0,0': {offset: 0, name: 'Africa/Casablanca'}
175 | '0,1': {offset: 0, name: 'Europe/London'}
176 | '60,1': {offset: 1, name: 'Europe/Berlin'}
177 | '60,0': {offset: 1, name: 'Africa/Lagos'}
178 | '60,1,s': {offset: 1, name: 'Africa/Windhoek'}
179 | '120,1': {offset: 2, name: 'Asia/Beirut'}
180 | '120,0': {offset: 2, name: 'Africa/Johannesburg'}
181 | '180,1': {offset: 3, name: 'Europe/Moscow'}
182 | '180,0': {offset: 3, name: 'Asia/Baghdad'}
183 | '210,1': {offset: 3.5, name: 'Asia/Tehran'}
184 | '240,0': {offset: 4, name: 'Asia/Dubai'}
185 | '240,1': {offset: 4, name: 'Asia/Yerevan'}
186 | '270,0': {offset: 4.5, name: 'Asia/Kabul'}
187 | '300,1': {offset: 5, name: 'Asia/Yekaterinburg'}
188 | '300,0': {offset: 5, name: 'Asia/Karachi'}
189 | '330,0': {offset: 5, name: 'Asia/Kolkata'}
190 | '345,0': {offset: 5.75, name: 'Asia/Kathmandu'}
191 | '360,0': {offset: 6, name: 'Asia/Dhaka'}
192 | '360,1': {offset: 6, name: 'Asia/Omsk'}
193 | '390,0': {offset: 6, name: 'Asia/Rangoon'}
194 | '420,1': {offset: 7, name: 'Asia/Krasnoyarsk'}
195 | '420,0': {offset: 7, name: 'Asia/Jakarta'}
196 | '480,0': {offset: 8, name: 'Asia/Shanghai'}
197 | '480,1': {offset: 8, name: 'Asia/Irkutsk'}
198 | '525,0': {offset: 8.75, name: 'Australia/Eucla'}
199 | '525,1,s': {offset: 8.75, name: 'Australia/Eucla'}
200 | '540,1': {offset: 9, name: 'Asia/Yakutsk'}
201 | '540,0': {offset: 9, name: 'Asia/Tokyo'}
202 | '570,0': {offset: 9.5, name: 'Australia/Darwin'}
203 | '570,1,s': {offset: 9.5, name: 'Australia/Adelaide'}
204 | '600,0': {offset: 10, name: 'Australia/Brisbane'}
205 | '600,1': {offset: 10, name: 'Asia/Vladivostok'}
206 | '600,1,s': {offset: 10, name: 'Australia/Sydney'}
207 | '630,1,s': {offset: 10.5, name: 'Australia/Lord_Howe'}
208 | '660,1': {offset: 11, name: 'Asia/Kamchatka'}
209 | '660,0': {offset: 11, name: 'Pacific/Noumea'}
210 | '690,0': {offset: 11.5, name: 'Pacific/Norfolk'}
211 | '720,1,s': {offset: 12, name: 'Pacific/Auckland'}
212 | '720,0': {offset: 12, name: 'Pacific/Tarawa'}
213 | '765,1,s': {offset: 12.75, name: 'Pacific/Chatham'}
214 | '780,0': {offset: 13, name: 'Pacific/Tongatapu'}
215 | '840,0': {offset: 14, name: 'Pacific/Kiritimati'}
216 |
--------------------------------------------------------------------------------
/distro/temporal.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var AMBIGIOUS_ZONES, DST_START_DATES, HEMISPHERE_NORTH, HEMISPHERE_SOUTH, HEMISPHERE_UNKNOWN, TIMEZONES, Temporal, TimeZone,
3 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
4 | __hasProp = {}.hasOwnProperty;
5 |
6 | Temporal = (function() {
7 | var jsonpCallback;
8 |
9 | jsonpCallback = "geoSuccessCallback" + (parseInt(Math.random() * 10000));
10 |
11 | Temporal.detect = function(username, callback) {
12 | if (username == null) {
13 | username = null;
14 | }
15 | if (callback == null) {
16 | callback = null;
17 | }
18 | return new Temporal(username, callback);
19 | };
20 |
21 | function Temporal(username, callback) {
22 | this.username = username != null ? username : null;
23 | this.callback = callback != null ? callback : null;
24 | this.parseGeoResponse = __bind(this.parseGeoResponse, this);
25 |
26 | this.geoSuccess = __bind(this.geoSuccess, this);
27 |
28 | this.detect();
29 | }
30 |
31 | Temporal.prototype.detect = function() {
32 | var timezone;
33 | timezone = this.detectLocally();
34 | if (this.username && navigator.geolocation && timezone.offset !== this.get().offset) {
35 | this.geoLocate();
36 | }
37 | return this.set(timezone);
38 | };
39 |
40 | Temporal.prototype.detectLocally = function() {
41 | var januaryOffset, juneOffset, key;
42 | januaryOffset = this.januaryOffset();
43 | juneOffset = this.juneOffset();
44 | key = {
45 | offset: januaryOffset,
46 | dst: 0,
47 | hemisphere: HEMISPHERE_UNKNOWN
48 | };
49 | if (januaryOffset - juneOffset < 0) {
50 | key = {
51 | offset: januaryOffset,
52 | dst: 1,
53 | hemisphere: HEMISPHERE_NORTH
54 | };
55 | } else if (januaryOffset - juneOffset > 0) {
56 | key = {
57 | offset: juneOffset,
58 | dst: 1,
59 | hemisphere: HEMISPHERE_SOUTH
60 | };
61 | }
62 | return new TimeZone("" + ([key.offset, key.dst].join(',')) + (key.hemisphere === HEMISPHERE_SOUTH ? ',s' : ''));
63 | };
64 |
65 | Temporal.prototype.geoLocate = function() {
66 | return navigator.geolocation.getCurrentPosition(this.geoSuccess, function() {});
67 | };
68 |
69 | Temporal.prototype.geoSuccess = function(position) {
70 | var script;
71 | window[jsonpCallback] = this.parseGeoResponse;
72 | script = document.createElement('script');
73 | script.setAttribute('src', "http://api.geonames.org/timezoneJSON?lat=" + position.coords.latitude + "&lng=" + position.coords.longitude + "&username=" + this.username + "&callback=" + jsonpCallback);
74 | return document.getElementsByTagName('head')[0].appendChild(script);
75 | };
76 |
77 | Temporal.prototype.parseGeoResponse = function(response) {
78 | delete window[jsonpCallback];
79 | if (response.timezoneId) {
80 | return this.set(new TimeZone({
81 | name: response.timezoneId,
82 | offset: response.rawOffset
83 | }));
84 | }
85 | };
86 |
87 | Temporal.prototype.set = function(timezone) {
88 | var expiration;
89 | this.timezone = timezone;
90 | window.timezone = this.timezone;
91 | expiration = new Date();
92 | expiration.setMonth(expiration.getMonth() + 1);
93 | document.cookie = "timezone=" + this.timezone.name + "; expires=" + (expiration.toGMTString());
94 | document.cookie = "timezone_offset=" + this.timezone.offset + "; expires=" + (expiration.toGMTString());
95 | return typeof this.callback === "function" ? this.callback(this.timezone) : void 0;
96 | };
97 |
98 | Temporal.prototype.get = function() {
99 | return {
100 | name: this.getCookie('timezone'),
101 | offset: parseFloat(this.getCookie('timezone_offset')) || 0
102 | };
103 | };
104 |
105 | Temporal.prototype.getCookie = function(name) {
106 | var match;
107 | match = document.cookie.match(new RegExp("(?:^|;)\\s?" + name + "=(.*?)(?:;|$)", 'i'));
108 | return match && unescape(match[1]);
109 | };
110 |
111 | Temporal.prototype.januaryOffset = function() {
112 | return this.dateOffset(new Date(2011, 0, 1, 0, 0, 0, 0));
113 | };
114 |
115 | Temporal.prototype.juneOffset = function() {
116 | return this.dateOffset(new Date(2011, 5, 1, 0, 0, 0, 0));
117 | };
118 |
119 | Temporal.prototype.dateOffset = function(date) {
120 | return -date.getTimezoneOffset();
121 | };
122 |
123 | return Temporal;
124 |
125 | }).call(this);
126 |
127 | TimeZone = (function() {
128 | var dateIsDst, resolveAmbiguity;
129 |
130 | dateIsDst = function(date) {
131 | return ((date.getMonth() > 5 ? this.juneOffset() : this.januaryOffset()) - this.dateOffset(date)) !== 0;
132 | };
133 |
134 | resolveAmbiguity = function() {
135 | var ambiguous, key, value;
136 | ambiguous = AMBIGIOUS_ZONES[this.name];
137 | if (typeof ambiguous === 'undefined') {
138 | return;
139 | }
140 | for (key in ambiguous) {
141 | value = ambiguous[key];
142 | if (dateIsDst(DST_START_DATES[value])) {
143 | this.name = value;
144 | return;
145 | }
146 | }
147 | };
148 |
149 | function TimeZone(keyOrProperties) {
150 | var property, value, zone;
151 | if (typeof keyOrProperties === 'string') {
152 | zone = TIMEZONES[keyOrProperties];
153 | for (property in zone) {
154 | if (!__hasProp.call(zone, property)) continue;
155 | value = zone[property];
156 | this[property] = value;
157 | }
158 | resolveAmbiguity();
159 | } else {
160 | for (property in keyOrProperties) {
161 | if (!__hasProp.call(keyOrProperties, property)) continue;
162 | value = keyOrProperties[property];
163 | this[property] = value;
164 | }
165 | }
166 | }
167 |
168 | return TimeZone;
169 |
170 | })();
171 |
172 | this.Temporal = {
173 | detect: Temporal.detect,
174 | reference: function() {
175 | return Temporal;
176 | }
177 | };
178 |
179 | HEMISPHERE_SOUTH = 'SOUTH';
180 |
181 | HEMISPHERE_NORTH = 'NORTH';
182 |
183 | HEMISPHERE_UNKNOWN = 'N/A';
184 |
185 | AMBIGIOUS_ZONES = {
186 | 'America/Denver': ['America/Denver', 'America/Mazatlan'],
187 | 'America/Chicago': ['America/Chicago', 'America/Mexico_City'],
188 | 'America/Asuncion': ['Atlantic/Stanley', 'America/Asuncion', 'America/Santiago', 'America/Campo_Grande'],
189 | 'America/Montevideo': ['America/Montevideo', 'America/Sao_Paolo'],
190 | 'Asia/Beirut': ['Asia/Gaza', 'Asia/Beirut', 'Europe/Minsk', 'Europe/Istanbul', 'Asia/Damascus', 'Asia/Jerusalem', 'Africa/Cairo'],
191 | 'Asia/Yerevan': ['Asia/Yerevan', 'Asia/Baku'],
192 | 'Pacific/Auckland': ['Pacific/Auckland', 'Pacific/Fiji'],
193 | 'America/Los_Angeles': ['America/Los_Angeles', 'America/Santa_Isabel'],
194 | 'America/New_York': ['America/Havana', 'America/New_York'],
195 | 'America/Halifax': ['America/Goose_Bay', 'America/Halifax'],
196 | 'America/Godthab': ['America/Miquelon', 'America/Godthab']
197 | };
198 |
199 | DST_START_DATES = {
200 | 'America/Denver': new Date(2011, 2, 13, 3, 0, 0, 0),
201 | 'America/Mazatlan': new Date(2011, 3, 3, 3, 0, 0, 0),
202 | 'America/Chicago': new Date(2011, 2, 13, 3, 0, 0, 0),
203 | 'America/Mexico_City': new Date(2011, 3, 3, 3, 0, 0, 0),
204 | 'Atlantic/Stanley': new Date(2011, 8, 4, 7, 0, 0, 0),
205 | 'America/Asuncion': new Date(2011, 9, 2, 3, 0, 0, 0),
206 | 'America/Santiago': new Date(2011, 9, 9, 3, 0, 0, 0),
207 | 'America/Campo_Grande': new Date(2011, 9, 16, 5, 0, 0, 0),
208 | 'America/Montevideo': new Date(2011, 9, 2, 3, 0, 0, 0),
209 | 'America/Sao_Paolo': new Date(2011, 9, 16, 5, 0, 0, 0),
210 | 'America/Los_Angeles': new Date(2011, 2, 13, 8, 0, 0, 0),
211 | 'America/Santa_Isabel': new Date(2011, 3, 5, 8, 0, 0, 0),
212 | 'America/Havana': new Date(2011, 2, 13, 2, 0, 0, 0),
213 | 'America/New_York': new Date(2011, 2, 13, 7, 0, 0, 0),
214 | 'Asia/Gaza': new Date(2011, 2, 26, 23, 0, 0, 0),
215 | 'Asia/Beirut': new Date(2011, 2, 27, 1, 0, 0, 0),
216 | 'Europe/Minsk': new Date(2011, 2, 27, 3, 0, 0, 0),
217 | 'Europe/Istanbul': new Date(2011, 2, 27, 7, 0, 0, 0),
218 | 'Asia/Damascus': new Date(2011, 3, 1, 2, 0, 0, 0),
219 | 'Asia/Jerusalem': new Date(2011, 3, 1, 6, 0, 0, 0),
220 | 'Africa/Cairo': new Date(2011, 3, 29, 4, 0, 0, 0),
221 | 'Asia/Yerevan': new Date(2011, 2, 27, 4, 0, 0, 0),
222 | 'Asia/Baku': new Date(2011, 2, 27, 8, 0, 0, 0),
223 | 'Pacific/Auckland': new Date(2011, 8, 26, 7, 0, 0, 0),
224 | 'Pacific/Fiji': new Date(2010, 11, 29, 23, 0, 0, 0),
225 | 'America/Halifax': new Date(2011, 2, 13, 6, 0, 0, 0),
226 | 'America/Goose_Bay': new Date(2011, 2, 13, 2, 1, 0, 0),
227 | 'America/Miquelon': new Date(2011, 2, 13, 5, 0, 0, 0),
228 | 'America/Godthab': new Date(2011, 2, 27, 1, 0, 0, 0)
229 | };
230 |
231 | TIMEZONES = {
232 | '-720,0': {
233 | offset: -12,
234 | name: 'Etc/GMT+12'
235 | },
236 | '-660,0': {
237 | offset: -11,
238 | name: 'Pacific/Pago_Pago'
239 | },
240 | '-600,1': {
241 | offset: -11,
242 | name: 'America/Adak'
243 | },
244 | '-660,1,s': {
245 | offset: -11,
246 | name: 'Pacific/Apia'
247 | },
248 | '-600,0': {
249 | offset: -10,
250 | name: 'Pacific/Honolulu'
251 | },
252 | '-570,0': {
253 | offset: -10.5,
254 | name: 'Pacific/Marquesas'
255 | },
256 | '-540,0': {
257 | offset: -9,
258 | name: 'Pacific/Gambier'
259 | },
260 | '-540,1': {
261 | offset: -9,
262 | name: 'America/Anchorage'
263 | },
264 | '-480,1': {
265 | offset: -8,
266 | name: 'America/Los_Angeles'
267 | },
268 | '-480,0': {
269 | offset: -8,
270 | name: 'Pacific/Pitcairn'
271 | },
272 | '-420,0': {
273 | offset: -7,
274 | name: 'America/Phoenix'
275 | },
276 | '-420,1': {
277 | offset: -7,
278 | name: 'America/Denver'
279 | },
280 | '-360,0': {
281 | offset: -6,
282 | name: 'America/Guatemala'
283 | },
284 | '-360,1': {
285 | offset: -6,
286 | name: 'America/Chicago'
287 | },
288 | '-360,1,s': {
289 | offset: -6,
290 | name: 'Pacific/Easter'
291 | },
292 | '-300,0': {
293 | offset: -5,
294 | name: 'America/Bogota'
295 | },
296 | '-300,1': {
297 | offset: -5,
298 | name: 'America/New_York'
299 | },
300 | '-270,0': {
301 | offset: -4.5,
302 | name: 'America/Caracas'
303 | },
304 | '-240,1': {
305 | offset: -4,
306 | name: 'America/Halifax'
307 | },
308 | '-240,0': {
309 | offset: -4,
310 | name: 'America/Santo_Domingo'
311 | },
312 | '-240,1,s': {
313 | offset: -4,
314 | name: 'America/Asuncion'
315 | },
316 | '-210,1': {
317 | offset: -3.5,
318 | name: 'America/St_Johns'
319 | },
320 | '-180,1': {
321 | offset: -3,
322 | name: 'America/Godthab'
323 | },
324 | '-180,0': {
325 | offset: -3,
326 | name: 'America/Argentina/Buenos_Aires'
327 | },
328 | '-180,1,s': {
329 | offset: -3,
330 | name: 'America/Montevideo'
331 | },
332 | '-120,0': {
333 | offset: -2,
334 | name: 'America/Noronha'
335 | },
336 | '-120,1': {
337 | offset: -2,
338 | name: 'Etc/GMT+2'
339 | },
340 | '-60,1': {
341 | offset: -1,
342 | name: 'Atlantic/Azores'
343 | },
344 | '-60,0': {
345 | offset: -1,
346 | name: 'Atlantic/Cape_Verde'
347 | },
348 | '0,0': {
349 | offset: 0,
350 | name: 'Africa/Casablanca'
351 | },
352 | '0,1': {
353 | offset: 0,
354 | name: 'Europe/London'
355 | },
356 | '60,1': {
357 | offset: 1,
358 | name: 'Europe/Berlin'
359 | },
360 | '60,0': {
361 | offset: 1,
362 | name: 'Africa/Lagos'
363 | },
364 | '60,1,s': {
365 | offset: 1,
366 | name: 'Africa/Windhoek'
367 | },
368 | '120,1': {
369 | offset: 2,
370 | name: 'Asia/Beirut'
371 | },
372 | '120,0': {
373 | offset: 2,
374 | name: 'Africa/Johannesburg'
375 | },
376 | '180,1': {
377 | offset: 3,
378 | name: 'Europe/Moscow'
379 | },
380 | '180,0': {
381 | offset: 3,
382 | name: 'Asia/Baghdad'
383 | },
384 | '210,1': {
385 | offset: 3.5,
386 | name: 'Asia/Tehran'
387 | },
388 | '240,0': {
389 | offset: 4,
390 | name: 'Asia/Dubai'
391 | },
392 | '240,1': {
393 | offset: 4,
394 | name: 'Asia/Yerevan'
395 | },
396 | '270,0': {
397 | offset: 4.5,
398 | name: 'Asia/Kabul'
399 | },
400 | '300,1': {
401 | offset: 5,
402 | name: 'Asia/Yekaterinburg'
403 | },
404 | '300,0': {
405 | offset: 5,
406 | name: 'Asia/Karachi'
407 | },
408 | '330,0': {
409 | offset: 5,
410 | name: 'Asia/Kolkata'
411 | },
412 | '345,0': {
413 | offset: 5.75,
414 | name: 'Asia/Kathmandu'
415 | },
416 | '360,0': {
417 | offset: 6,
418 | name: 'Asia/Dhaka'
419 | },
420 | '360,1': {
421 | offset: 6,
422 | name: 'Asia/Omsk'
423 | },
424 | '390,0': {
425 | offset: 6,
426 | name: 'Asia/Rangoon'
427 | },
428 | '420,1': {
429 | offset: 7,
430 | name: 'Asia/Krasnoyarsk'
431 | },
432 | '420,0': {
433 | offset: 7,
434 | name: 'Asia/Jakarta'
435 | },
436 | '480,0': {
437 | offset: 8,
438 | name: 'Asia/Shanghai'
439 | },
440 | '480,1': {
441 | offset: 8,
442 | name: 'Asia/Irkutsk'
443 | },
444 | '525,0': {
445 | offset: 8.75,
446 | name: 'Australia/Eucla'
447 | },
448 | '525,1,s': {
449 | offset: 8.75,
450 | name: 'Australia/Eucla'
451 | },
452 | '540,1': {
453 | offset: 9,
454 | name: 'Asia/Yakutsk'
455 | },
456 | '540,0': {
457 | offset: 9,
458 | name: 'Asia/Tokyo'
459 | },
460 | '570,0': {
461 | offset: 9.5,
462 | name: 'Australia/Darwin'
463 | },
464 | '570,1,s': {
465 | offset: 9.5,
466 | name: 'Australia/Adelaide'
467 | },
468 | '600,0': {
469 | offset: 10,
470 | name: 'Australia/Brisbane'
471 | },
472 | '600,1': {
473 | offset: 10,
474 | name: 'Asia/Vladivostok'
475 | },
476 | '600,1,s': {
477 | offset: 10,
478 | name: 'Australia/Sydney'
479 | },
480 | '630,1,s': {
481 | offset: 10.5,
482 | name: 'Australia/Lord_Howe'
483 | },
484 | '660,1': {
485 | offset: 11,
486 | name: 'Asia/Kamchatka'
487 | },
488 | '660,0': {
489 | offset: 11,
490 | name: 'Pacific/Noumea'
491 | },
492 | '690,0': {
493 | offset: 11.5,
494 | name: 'Pacific/Norfolk'
495 | },
496 | '720,1,s': {
497 | offset: 12,
498 | name: 'Pacific/Auckland'
499 | },
500 | '720,0': {
501 | offset: 12,
502 | name: 'Pacific/Tarawa'
503 | },
504 | '765,1,s': {
505 | offset: 12.75,
506 | name: 'Pacific/Chatham'
507 | },
508 | '780,0': {
509 | offset: 13,
510 | name: 'Pacific/Tongatapu'
511 | },
512 | '840,0': {
513 | offset: 14,
514 | name: 'Pacific/Kiritimati'
515 | }
516 | };
517 |
518 | }).call(this);
519 |
--------------------------------------------------------------------------------