timezone 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +1 -1
- data/lib/timezone.rb +2 -104
- data/lib/timezone/configure.rb +24 -0
- data/lib/timezone/error.rb +16 -0
- data/lib/timezone/version.rb +1 -1
- data/lib/timezone/zone.rb +74 -0
- data/test/timezone_test.rb +11 -8
- metadata +6 -3
data/README.markdown
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Timezone
|
2
2
|
|
3
|
-
A simple way to get accurate current and historical timezone information based on zone or latitude and longitude coordinates. This gem uses the tz database
|
3
|
+
A simple way to get accurate current and historical timezone information based on zone or latitude and longitude coordinates. This gem uses the [tz database](http://www.twinsun.com/tz/tz-link.htm) for historical timezone information. It also uses the [geonames API](http://www.geonames.org/export/web-services.html) for timezone latitude and longitude lookup.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
data/lib/timezone.rb
CHANGED
@@ -1,105 +1,3 @@
|
|
1
|
-
require '
|
2
|
-
require 'date'
|
3
|
-
require 'time'
|
4
|
-
require 'net/http'
|
1
|
+
require File.expand_path('../timezone/zone', __FILE__)
|
5
2
|
|
6
|
-
|
7
|
-
attr_accessor :rules, :zone
|
8
|
-
|
9
|
-
# Configuration class for the Timezone gem.
|
10
|
-
#
|
11
|
-
# Timezone::Configure.begin do |c| ... end
|
12
|
-
# c.username = username - the geonames username you use to access the geonames timezone API.
|
13
|
-
#
|
14
|
-
# Signup for a geonames username at http://www.geonames.org/login. Use that username to configure
|
15
|
-
# your application for latitude and longitude based timezone searches. If you aren't going to
|
16
|
-
# initialize timezone objects based on latitude and longitude then this configuration is not necessary.
|
17
|
-
class Configure
|
18
|
-
def self.username
|
19
|
-
@@username
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.username= username
|
23
|
-
@@username = username
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.begin
|
27
|
-
yield self
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Error messages that can be raised by this gem. To catch any related error message, simply use Error::Base.
|
32
|
-
#
|
33
|
-
# begin
|
34
|
-
# ...
|
35
|
-
# rescue Timezone::Error::Base => e
|
36
|
-
# puts "Timezone Error: #{e.message}"
|
37
|
-
# end
|
38
|
-
module Error
|
39
|
-
class Base < StandardError; end
|
40
|
-
class InvalidZone < Base; end
|
41
|
-
class NilZone < Base; end
|
42
|
-
class GeoNames < Base; end
|
43
|
-
class ParseTime < Base; end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Create a new Timezone object.
|
47
|
-
#
|
48
|
-
# Timezone.new(options)
|
49
|
-
# :zone - The actual name of the zone. For example, Australia/Sydney or Americas/Los_Angeles.
|
50
|
-
# :lat, :lon - The latitude and longitude of the location.
|
51
|
-
# :latlon - The array of latitude and longitude of the location.
|
52
|
-
#
|
53
|
-
# If a latitude and longitude is passed in, the Timezone object will do a lookup for the actual zone
|
54
|
-
# name and then use that as a reference. It will then load the appropriate json timezone information
|
55
|
-
# for that zone, and compile a list of the timezone rules.
|
56
|
-
def initialize options
|
57
|
-
if options.has_key?(:lat) && options.has_key?(:lon)
|
58
|
-
options[:zone] = timezone_id options[:lat], options[:lon]
|
59
|
-
elsif options.has_key?(:latlon)
|
60
|
-
options[:zone] = timezone_id *options[:latlon]
|
61
|
-
end
|
62
|
-
|
63
|
-
raise Error::NilZone, 'No zone was found. Please specify a zone.' if options[:zone].nil?
|
64
|
-
|
65
|
-
file = File.join File.expand_path(File.dirname(__FILE__)+'/../data'), "#{options[:zone]}.json"
|
66
|
-
raise Error::InvalidZone, "'#{options[:zone]}' is not a valid zone." unless File.exists?(file)
|
67
|
-
|
68
|
-
data = JSON.parse(open(file).read)
|
69
|
-
@rules = data['zone']
|
70
|
-
@zone = data['_zone'] || options[:zone]
|
71
|
-
end
|
72
|
-
|
73
|
-
# Determine the time in the timezone.
|
74
|
-
#
|
75
|
-
# timezone.time(reference)
|
76
|
-
# reference - The Time you want to convert.
|
77
|
-
#
|
78
|
-
# The reference is converted to a UTC equivalent. That UTC equivalent is then used to lookup the appropriate
|
79
|
-
# offset in the timezone rules. Once the offset has been found that offset is added to the reference UTC time
|
80
|
-
# to calculate the reference time in the timezone.
|
81
|
-
def time reference
|
82
|
-
reference = reference.utc
|
83
|
-
rule = rules.detect{ |rule| _parsetime(rule['_from']) <= reference && _parsetime(rule['_to']) >= reference }
|
84
|
-
reference + rule['offset']
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def timezone_id lat, lon #:nodoc:
|
90
|
-
begin
|
91
|
-
response = Net::HTTP.get('ws.geonames.org', "/timezoneJSON?lat=#{lat}&lng=#{lon}&username=#{Configure.username}")
|
92
|
-
JSON.parse(response)['timezoneId']
|
93
|
-
rescue Exception => e
|
94
|
-
raise Error::GeoNames, e.message
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def _parsetime time #:nodoc:
|
99
|
-
begin
|
100
|
-
Time.strptime(time, "%Y-%m-%dT%H:%M:%SZ")
|
101
|
-
rescue Exception => e
|
102
|
-
raise Error::ParseTime, e.message
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
3
|
+
module Timezone; end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Timezone
|
2
|
+
# Configuration class for the Timezone gem.
|
3
|
+
#
|
4
|
+
# Timezone::Configure.begin do |c| ... end
|
5
|
+
#
|
6
|
+
# c.username = username - the geonames username you use to access the geonames timezone API.
|
7
|
+
#
|
8
|
+
# Signup for a geonames username at http://www.geonames.org/login. Use that username to configure
|
9
|
+
# your application for latitude and longitude based timezone searches. If you aren't going to
|
10
|
+
# initialize timezone objects based on latitude and longitude then this configuration is not necessary.
|
11
|
+
class Configure
|
12
|
+
def self.username
|
13
|
+
@@username
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.username= username
|
17
|
+
@@username = username
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.begin
|
21
|
+
yield self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Timezone
|
2
|
+
# Error messages that can be raised by this gem. To catch any related error message, simply use Error::Base.
|
3
|
+
#
|
4
|
+
# begin
|
5
|
+
# ...
|
6
|
+
# rescue Timezone::Error::Base => e
|
7
|
+
# puts "Timezone Error: #{e.message}"
|
8
|
+
# end
|
9
|
+
module Error
|
10
|
+
class Base < StandardError; end
|
11
|
+
class InvalidZone < Base; end
|
12
|
+
class NilZone < Base; end
|
13
|
+
class GeoNames < Base; end
|
14
|
+
class ParseTime < Base; end
|
15
|
+
end
|
16
|
+
end
|
data/lib/timezone/version.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'date'
|
3
|
+
require 'time'
|
4
|
+
require 'net/http'
|
5
|
+
require 'timezone/error'
|
6
|
+
require 'timezone/configure'
|
7
|
+
|
8
|
+
module Timezone
|
9
|
+
class Zone
|
10
|
+
attr_accessor :rules, :zone
|
11
|
+
|
12
|
+
# Create a new Timezone object.
|
13
|
+
#
|
14
|
+
# Timezone.new(options)
|
15
|
+
#
|
16
|
+
# :zone - The actual name of the zone. For example, Australia/Sydney or Americas/Los_Angeles.
|
17
|
+
# :lat, :lon - The latitude and longitude of the location.
|
18
|
+
# :latlon - The array of latitude and longitude of the location.
|
19
|
+
#
|
20
|
+
# If a latitude and longitude is passed in, the Timezone object will do a lookup for the actual zone
|
21
|
+
# name and then use that as a reference. It will then load the appropriate json timezone information
|
22
|
+
# for that zone, and compile a list of the timezone rules.
|
23
|
+
def initialize options
|
24
|
+
if options.has_key?(:lat) && options.has_key?(:lon)
|
25
|
+
options[:zone] = timezone_id options[:lat], options[:lon]
|
26
|
+
elsif options.has_key?(:latlon)
|
27
|
+
options[:zone] = timezone_id *options[:latlon]
|
28
|
+
end
|
29
|
+
|
30
|
+
raise Timezone::Error::NilZone, 'No zone was found. Please specify a zone.' if options[:zone].nil?
|
31
|
+
|
32
|
+
file = File.join File.expand_path(File.dirname(__FILE__)+'/../../data'), "#{options[:zone]}.json"
|
33
|
+
raise Timezone::Error::InvalidZone, "'#{options[:zone]}' is not a valid zone." unless File.exists?(file)
|
34
|
+
|
35
|
+
data = JSON.parse(open(file).read)
|
36
|
+
@rules = data['zone']
|
37
|
+
@zone = data['_zone'] || options[:zone]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Determine the time in the timezone.
|
41
|
+
#
|
42
|
+
# timezone.time(reference)
|
43
|
+
#
|
44
|
+
# reference - The Time you want to convert.
|
45
|
+
#
|
46
|
+
# The reference is converted to a UTC equivalent. That UTC equivalent is then used to lookup the appropriate
|
47
|
+
# offset in the timezone rules. Once the offset has been found that offset is added to the reference UTC time
|
48
|
+
# to calculate the reference time in the timezone.
|
49
|
+
def time reference
|
50
|
+
reference = reference.utc
|
51
|
+
rule = rules.detect{ |rule| _parsetime(rule['_from']) <= reference && _parsetime(rule['_to']) >= reference }
|
52
|
+
reference + rule['offset']
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def timezone_id lat, lon #:nodoc:
|
58
|
+
begin
|
59
|
+
response = Net::HTTP.get('ws.geonames.org', "/timezoneJSON?lat=#{lat}&lng=#{lon}&username=#{Timezone::Configure.username}")
|
60
|
+
JSON.parse(response)['timezoneId']
|
61
|
+
rescue Exception => e
|
62
|
+
raise Timezone::Error::GeoNames, e.message
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def _parsetime time #:nodoc:
|
67
|
+
begin
|
68
|
+
Time.strptime(time, "%Y-%m-%dT%H:%M:%SZ")
|
69
|
+
rescue Exception => e
|
70
|
+
raise Timezone::Error::ParseTime, e.message
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/test/timezone_test.rb
CHANGED
@@ -1,40 +1,43 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/timezone')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/timezone/zone')
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/timezone/error')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/timezone/configure')
|
2
5
|
require 'test/unit'
|
3
6
|
|
4
7
|
class TimezoneTest < Test::Unit::TestCase
|
5
8
|
|
6
9
|
def test_valid_timezone
|
7
10
|
assert_nothing_raised do
|
8
|
-
Timezone.new :zone => 'Australia/Sydney'
|
11
|
+
Timezone::Zone.new :zone => 'Australia/Sydney'
|
9
12
|
end
|
10
13
|
end
|
11
14
|
|
12
15
|
def test_nil_timezone
|
13
16
|
assert_raise Timezone::Error::NilZone do
|
14
|
-
Timezone.new :zone => nil
|
17
|
+
Timezone::Zone.new :zone => nil
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
18
21
|
def test_invalid_timezone
|
19
22
|
assert_raise Timezone::Error::InvalidZone do
|
20
|
-
Timezone.new :zone => 'Foo/Bar'
|
23
|
+
Timezone::Zone.new :zone => 'Foo/Bar'
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
24
27
|
def test_loading_GMT_timezone
|
25
|
-
timezone = Timezone.new :zone => 'GMT'
|
28
|
+
timezone = Timezone::Zone.new :zone => 'GMT'
|
26
29
|
assert_equal Time.now.utc.to_i, timezone.time(Time.now).to_i
|
27
30
|
end
|
28
31
|
|
29
32
|
def test_loading_historical_time
|
30
|
-
timezone = Timezone.new :zone => 'America/Los_Angeles'
|
33
|
+
timezone = Timezone::Zone.new :zone => 'America/Los_Angeles'
|
31
34
|
local = Time.strptime('2011-02-11T13:20:00Z', '%Y-%m-%dT%H:%M:%SZ')
|
32
35
|
utc = Time.strptime('2011-02-11T21:20:00Z', '%Y-%m-%dT%H:%M:%SZ')
|
33
36
|
assert_equal local.to_i, timezone.time(utc).to_i
|
34
37
|
end
|
35
38
|
|
36
39
|
def test_loading_half_hour_timezone
|
37
|
-
timezone = Timezone.new :zone => 'Asia/Kathmandu'
|
40
|
+
timezone = Timezone::Zone.new :zone => 'Asia/Kathmandu'
|
38
41
|
utc = Time.utc(2011, 1, 4, 3, 51, 29)
|
39
42
|
local = Time.utc(2011, 1, 4, 9, 36, 29)
|
40
43
|
assert_equal local.to_i, timezone.time(utc).to_i
|
@@ -42,12 +45,12 @@ class TimezoneTest < Test::Unit::TestCase
|
|
42
45
|
|
43
46
|
def test_using_lat_lon_coordinates
|
44
47
|
Timezone::Configure.begin { |c| c.username = 'timezone' }
|
45
|
-
timezone = Timezone.new :latlon => [-34.92771808058, 138.477041423321]
|
48
|
+
timezone = Timezone::Zone.new :latlon => [-34.92771808058, 138.477041423321]
|
46
49
|
assert_equal 'Australia/Adelaide', timezone.zone
|
47
50
|
end
|
48
51
|
|
49
52
|
def test_australian_timezone_with_dst
|
50
|
-
timezone = Timezone.new :zone => 'Australia/Adelaide'
|
53
|
+
timezone = Timezone::Zone.new :zone => 'Australia/Adelaide'
|
51
54
|
utc = Time.utc(2010, 12, 23, 19, 37, 15)
|
52
55
|
local = Time.utc(2010, 12, 24, 6, 7, 15)
|
53
56
|
assert_equal local.to_i, timezone.time(utc).to_i
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: timezone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0
|
5
|
+
version: 0.1.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Pan Thomakos
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-02-
|
13
|
+
date: 2011-02-12 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -490,7 +490,10 @@ files:
|
|
490
490
|
- data/Pacific/Wallis.json
|
491
491
|
- data/WET.json
|
492
492
|
- lib/timezone.rb
|
493
|
+
- lib/timezone/configure.rb
|
494
|
+
- lib/timezone/error.rb
|
493
495
|
- lib/timezone/version.rb
|
496
|
+
- lib/timezone/zone.rb
|
494
497
|
- test/timezone_test.rb
|
495
498
|
- timezone.gemspec
|
496
499
|
has_rdoc: true
|
@@ -520,6 +523,6 @@ rubyforge_project: timezone
|
|
520
523
|
rubygems_version: 1.5.0
|
521
524
|
signing_key:
|
522
525
|
specification_version: 3
|
523
|
-
summary: timezone-0.0
|
526
|
+
summary: timezone-0.1.0
|
524
527
|
test_files:
|
525
528
|
- test/timezone_test.rb
|