tzdetect 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +94 -0
- data/Rakefile +16 -0
- data/TZDetect.gemspec +22 -0
- data/lib/TZDetect/configuration.rb +56 -0
- data/lib/TZDetect/error.rb +9 -0
- data/lib/TZDetect/geocoder.rb +65 -0
- data/lib/TZDetect/geocoders/geoname.rb +47 -0
- data/lib/TZDetect/geocoders/google.rb +49 -0
- data/lib/TZDetect/parser.rb +41 -0
- data/lib/TZDetect/parsers/geoname.rb +31 -0
- data/lib/TZDetect/parsers/google.rb +36 -0
- data/lib/TZDetect/version.rb +3 -0
- data/lib/TZDetect/zone.rb +172 -0
- data/lib/TZDetect.rb +5 -0
- data/test/geoname_geocoder_test.rb +37 -0
- data/test/geoname_parser_test.rb +37 -0
- data/test/google_geocoder_test.rb +26 -0
- data/test/google_parser_test.rb +52 -0
- data/test/helper.rb +1 -0
- data/test/main_functionality_test.rb +89 -0
- metadata +97 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Bojan Milosavljevic
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# TZDetect
|
2
|
+
|
3
|
+
Detects timezone by address or location, using google or geoname web services.
|
4
|
+
It will utilaze web services only if can't detect timezone by country code (when country has only one timezone)
|
5
|
+
|
6
|
+
Procedure for detecting time zone:
|
7
|
+
|
8
|
+
1. Looks tah given country has only one time zone
|
9
|
+
2. Fetching time zone data through web services if position (latitude and longitude) is given
|
10
|
+
3. First detects position for given country, city and region and then fetch timezone data from web
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
gem 'tzdetect'
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install tzdetect
|
25
|
+
|
26
|
+
## Configuration
|
27
|
+
|
28
|
+
Can be set through block:
|
29
|
+
|
30
|
+
TZDetect::Configuration.begin do |c|
|
31
|
+
c.username = 'your_geonames_username'
|
32
|
+
end
|
33
|
+
|
34
|
+
or
|
35
|
+
TZDetect::Configuration.service = :geoname
|
36
|
+
TZDetect::Configuration.username = "username"
|
37
|
+
|
38
|
+
Configuration params:
|
39
|
+
|
40
|
+
* service - service name can be :google or :geoname - default google
|
41
|
+
* username - username for geoname service
|
42
|
+
* google_client_key = google api key (not tested )
|
43
|
+
* google_signature = google api signature (not tested )
|
44
|
+
|
45
|
+
If you use geoname service, make sure you have a geonames username. It's free and easy to setup, you can do so [here](http://www.geonames.org/login).
|
46
|
+
|
47
|
+
## Usage
|
48
|
+
|
49
|
+
Country with many timezones without position data: System will find location for Miami and then find timezone
|
50
|
+
|
51
|
+
tz = TimeZone.new("US", "Miami", "Florida")
|
52
|
+
tz.timezone.name # "America/New_York"
|
53
|
+
tz.latitude # 25.774265
|
54
|
+
tz.longitude # -80.193658
|
55
|
+
|
56
|
+
Country with many time zones with position data: System will directly find timezone with position data
|
57
|
+
|
58
|
+
tz = TimeZone.new("US", "Miami", "Florida", 25.774265, -80.193658)
|
59
|
+
tz.timezone.name # "America/New_York"
|
60
|
+
|
61
|
+
Country with one time zone will not return location data
|
62
|
+
|
63
|
+
tz = TimeZone.new("RS", "Novi Sad")
|
64
|
+
tz.timezone.name # "Europe/Belgrade"
|
65
|
+
tz.latitude # nil
|
66
|
+
tz.longitude # nil
|
67
|
+
|
68
|
+
|
69
|
+
## Class methods
|
70
|
+
|
71
|
+
### Get timezone by location
|
72
|
+
|
73
|
+
Return TZInfo object
|
74
|
+
|
75
|
+
tz = TimeZone.get!("RS", "Novi Sad")
|
76
|
+
tz.name # "Europe/Belgrade"
|
77
|
+
|
78
|
+
tz = TimeZone.new("US", "Miami", "Florida", 25.774265, -80.193658)
|
79
|
+
tz.name # "America/New_York"
|
80
|
+
|
81
|
+
### Getting the timezone for a specific latitude and longitude
|
82
|
+
|
83
|
+
You can use class method by_location
|
84
|
+
|
85
|
+
TZDetect::TimeZone.by_location! 44.21, 21.0
|
86
|
+
|
87
|
+
|
88
|
+
## Contributing
|
89
|
+
|
90
|
+
1. Fork it
|
91
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
92
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
93
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
94
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'rdoc/task'
|
5
|
+
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
Rake::TestTask.new(:test) do |t|
|
9
|
+
t.libs << 'lib'
|
10
|
+
t.libs << 'test'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = false
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => [:test]
|
16
|
+
|
data/TZDetect.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tzdetect/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "tzdetect"
|
8
|
+
gem.version = TZDetect::VERSION
|
9
|
+
gem.authors = ["Bojan Milosavljevic "]
|
10
|
+
gem.email = ["milboj@gmail.com"]
|
11
|
+
gem.description = %q{Detects timezone by address or location using google or geoname web service}
|
12
|
+
gem.summary = %q{Easy way to detect time zone by address or location }
|
13
|
+
gem.homepage = ""
|
14
|
+
gem.required_ruby_version = ">= 1.9.2"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.add_dependency 'tzinfo', '>= 0.3.33'
|
21
|
+
gem.add_dependency 'json', '>= 1.7'
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module TZDetect
|
2
|
+
class Configuration
|
3
|
+
ALLOWED_TYPES= [:google, :geoname]
|
4
|
+
|
5
|
+
@@google_signature = nil
|
6
|
+
@@google_client_key = nil
|
7
|
+
|
8
|
+
def self.service= type
|
9
|
+
raise ArgumentError, "wrong service type" unless ALLOWED_TYPES.include?(type.to_sym)
|
10
|
+
@@service= type
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.service
|
14
|
+
@@service ||= :google
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.google_client_key
|
18
|
+
@@google_client_key
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.google_client_key= google_key
|
22
|
+
@@google_client_key= google_key
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.google_signature
|
26
|
+
@@google_signature
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.google_signature= google_signature
|
30
|
+
@@google_key= google_signature
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def self.username= username
|
35
|
+
@@username = username
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.username
|
39
|
+
@@username
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.begin
|
43
|
+
yield self
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
# Merge this params Set params https://developers.google.com/maps/documentation/business/webservices
|
48
|
+
def self.google_params
|
49
|
+
params = {}
|
50
|
+
params[:client] = @@google_client_key unless @@google_client_key.nil?
|
51
|
+
params[:signature] = @@google_signature unless @@google_signature.nil?
|
52
|
+
return params
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module TZDetect
|
2
|
+
Position = Struct.new(:latitude, :longitude, :timezone)
|
3
|
+
module Geocoder
|
4
|
+
attr_accessor :city, :country, :region
|
5
|
+
attr_reader :latitude, :longitude, :timezone
|
6
|
+
|
7
|
+
# Constructor initalize country city and region
|
8
|
+
def initialize country, city, region=""
|
9
|
+
@country = country
|
10
|
+
@city = city
|
11
|
+
@region = region
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
|
21
|
+
# Returns location position hash
|
22
|
+
#
|
23
|
+
# *latitude
|
24
|
+
# *longitude
|
25
|
+
# *timezone - can be nil
|
26
|
+
#
|
27
|
+
def self.get country, city, region=""
|
28
|
+
location = self.new country, city, region
|
29
|
+
location.position
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns location position hash (raise exception)
|
33
|
+
#
|
34
|
+
# *latitude
|
35
|
+
# *longitude
|
36
|
+
# *timezone - can be nil
|
37
|
+
#
|
38
|
+
def self.get! country, city, region=""
|
39
|
+
location = self.new country, city, region
|
40
|
+
return location.position!
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
# Returns position
|
47
|
+
def postion
|
48
|
+
begin
|
49
|
+
self.postion!
|
50
|
+
rescue
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns position raise excpetion
|
56
|
+
def position!
|
57
|
+
raise TZDetect::Error::Geocoder, "not implemented method position!"
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetched_data
|
61
|
+
Position.new(@latitude, @longitude, @timezone)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../geocoder')
|
4
|
+
|
5
|
+
module TZDetect
|
6
|
+
class GeonameGeocode
|
7
|
+
include Geocoder
|
8
|
+
GEONAME_GEOCODE_API = "http://api.geonames.org/searchJSON"
|
9
|
+
|
10
|
+
# Returns position
|
11
|
+
def position!
|
12
|
+
begin
|
13
|
+
|
14
|
+
url =URI(GEONAME_GEOCODE_API)
|
15
|
+
url.query = URI.encode_www_form(geoname_params)
|
16
|
+
|
17
|
+
Net::HTTP.start(url.host, url.port, :use_ssl => false) do |http|
|
18
|
+
request = Net::HTTP::Get.new(url.request_uri)
|
19
|
+
response = http.request request # Net::HTTPResponse object
|
20
|
+
json_result =JSON.parse(response.read_body)
|
21
|
+
|
22
|
+
unless json_result["geonames"].empty?
|
23
|
+
geoname = json_result["geonames"][0]
|
24
|
+
@latitude = geoname["lat"]
|
25
|
+
@longitude = geoname["lng"]
|
26
|
+
@timezone = geoname["timezone"]["timeZoneId"]
|
27
|
+
return fetched_data
|
28
|
+
else
|
29
|
+
raise "no results"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue Exception => e
|
33
|
+
raise TZDetect::Error::GeoCoder, e.message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def geoname_params
|
39
|
+
username = Configuration.username
|
40
|
+
raise "no geoname username configured" if username.nil?
|
41
|
+
address = @region.nil? ? @city : "#{@city},#{@region}"
|
42
|
+
return {:q=> address, :country=>@country, :formated => "true",:maxRows => 5, :username => username, :style => "FULL" }
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../geocoder')
|
4
|
+
|
5
|
+
module TZDetect
|
6
|
+
class GoogleGeocode
|
7
|
+
include Geocoder
|
8
|
+
GOOGLE_GEOCODE_API = "https://maps.google.com/maps/api/geocode/json"
|
9
|
+
|
10
|
+
|
11
|
+
# Returns position
|
12
|
+
def position!
|
13
|
+
other = @region.nil? ? @country : "#{@region},#{@country}"
|
14
|
+
address = "#{@city},#{other}"
|
15
|
+
position = self.class.fetch_by_address!(address)
|
16
|
+
@latitude = position["lat"]
|
17
|
+
@longitude = position["lng"]
|
18
|
+
return fetched_data
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Find position by addresss
|
23
|
+
def self.fetch_by_address! address
|
24
|
+
begin
|
25
|
+
url =URI(GOOGLE_GEOCODE_API)
|
26
|
+
url.query = URI.encode_www_form(self.params(address))
|
27
|
+
Net::HTTP.start(url.host, url.port, :use_ssl => true) do |http|
|
28
|
+
request = Net::HTTP::Get.new(url.request_uri)
|
29
|
+
response = http.request request # Net::HTTPResponse object
|
30
|
+
json_result =JSON.parse(response.read_body)
|
31
|
+
if json_result["status"] == "OK"
|
32
|
+
return json_result["results"][0]["geometry"]["location"]
|
33
|
+
else
|
34
|
+
raise "no results"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
rescue Exception => e
|
38
|
+
raise TZDetect::Error::GeoCoder, e.message
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Set params https://developers.google.com/maps/documentation/business/webservices
|
43
|
+
def self.params address
|
44
|
+
{:address=> address, :sensor=>"false"}.merge(Configuration.google_params)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module TZDetect
|
2
|
+
module Parser
|
3
|
+
attr_accessor :latitude, :longitude
|
4
|
+
def initialize lat, lon
|
5
|
+
lat = lat.to_f if lat.kind_of? Integer
|
6
|
+
lon = lon.to_f if lon.kind_of? Integer
|
7
|
+
valid_latitude_and_longitude lat, lon
|
8
|
+
raise TZDetect::Error::Parser, "lat and long must be float" if !lat.kind_of?(Float) || !lon.kind_of?(Float)
|
9
|
+
@latitude = lat
|
10
|
+
@longitude = lon
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def timezone! lat, lon
|
19
|
+
t = self.new lat, lon
|
20
|
+
t.timezone
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def timezone
|
25
|
+
begin
|
26
|
+
m = self.timezone!
|
27
|
+
rescue
|
28
|
+
m = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def valid_latitude_and_longitude lat, lon
|
34
|
+
raise TZDetect::Error::Parser, "latitude and longitude must be number" if !lat.kind_of?(Float) || !lon.kind_of?(Float)
|
35
|
+
raise TZDetect::Error::Parser, "latitude must be beetween -90 and 90 degrees" if lat < -90 || lat > 90
|
36
|
+
raise TZDetect::Error::Parser, "longitude must be beetween -180 and 180 degrees" if lon < -180 || lon > 180
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../parser')
|
4
|
+
|
5
|
+
module TZDetect
|
6
|
+
class GeonameParser
|
7
|
+
include Parser
|
8
|
+
|
9
|
+
GEONAME_GEOCODE_TIMEZONE_API = "http://api.geonames.org"
|
10
|
+
def timezone!
|
11
|
+
begin
|
12
|
+
|
13
|
+
url =URI("#{GEONAME_GEOCODE_TIMEZONE_API}#{geoname_params}")
|
14
|
+
Net::HTTP.start(url.host, url.port, :use_ssl => false) do |http|
|
15
|
+
request = Net::HTTP::Get.new(url.request_uri)
|
16
|
+
response = http.request request # Net::HTTPResponse object
|
17
|
+
json_result =JSON.parse(response.read_body)["timezoneId"]
|
18
|
+
raise "No result" if json_result.nil?
|
19
|
+
return json_result
|
20
|
+
end
|
21
|
+
rescue Exception => e
|
22
|
+
raise TZDetect::Error::Parser, e.message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
def geoname_params
|
26
|
+
username = Configuration.username
|
27
|
+
raise "No geoname useraname" if username.nil?
|
28
|
+
"/timezoneJSON?lat=#{@latitude}&lng=#{@longitude}&username=#{username}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../parser')
|
4
|
+
|
5
|
+
module TZDetect
|
6
|
+
class GoogleParser
|
7
|
+
include Parser
|
8
|
+
GOOGLE_GEOCODE_TIMEZONE_API = "https://maps.googleapis.com/maps/api/timezone/json"
|
9
|
+
|
10
|
+
def timezone!
|
11
|
+
begin
|
12
|
+
url =URI(GOOGLE_GEOCODE_TIMEZONE_API)
|
13
|
+
url.query = URI.encode_www_form(self.params)
|
14
|
+
Net::HTTP.start(url.host, url.port, :use_ssl => true) do |http|
|
15
|
+
request = Net::HTTP::Get.new(url.request_uri)
|
16
|
+
response = http.request request # Net::HTTPResponse object
|
17
|
+
json_result =JSON.parse(response.read_body)
|
18
|
+
if json_result["status"] == "OK"
|
19
|
+
json_result["timeZoneId"]
|
20
|
+
else
|
21
|
+
raise "No result"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
rescue Exception => e
|
25
|
+
raise TZDetect::Error::Parser, e.message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def params
|
31
|
+
timestamp = Time.now.to_i
|
32
|
+
{:location =>"#{@latitude},#{@longitude}", :timestamp=>"#{timestamp}", :sensor=>"false"}.merge(Configuration.google_params)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require "tzinfo"
|
2
|
+
require "tzdetect/error"
|
3
|
+
require "tzdetect/configuration"
|
4
|
+
|
5
|
+
require "tzdetect/parsers/geoname"
|
6
|
+
require "tzdetect/parsers/google"
|
7
|
+
|
8
|
+
require "tzdetect/geocoders/geoname"
|
9
|
+
require "tzdetect/geocoders/google"
|
10
|
+
|
11
|
+
module TZDetect
|
12
|
+
class TimeZone
|
13
|
+
attr_reader :country_code, :city, :region, :latitude, :longitude, :timezone
|
14
|
+
|
15
|
+
# Constructor
|
16
|
+
#
|
17
|
+
# * country_code - two character ISO 3166-1 alpha 2 code http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
|
18
|
+
# * city
|
19
|
+
# * region - use region
|
20
|
+
# * latitude
|
21
|
+
# * longitude
|
22
|
+
#
|
23
|
+
# == Example
|
24
|
+
#
|
25
|
+
# === Country with many timezones without position data. System will find location for Miami and then find timezone
|
26
|
+
#
|
27
|
+
# tz = TimeZone.new("US", "Miami", "Florida")
|
28
|
+
# tz.timezone.name # "America/New_York"
|
29
|
+
# tz.latitude # 25.774265
|
30
|
+
# tz.longitude # -80.193658
|
31
|
+
#
|
32
|
+
# === Country with many timezones with position data. System will directly find timezone with position data
|
33
|
+
#
|
34
|
+
# tz = TimeZone.new("US", "Miami", "Florida", 25.774265, -80.193658)
|
35
|
+
# tz.timezone.name # "America/New_York"
|
36
|
+
#
|
37
|
+
# === Country with one timezone will not return location data
|
38
|
+
#
|
39
|
+
# tz = TimeZone.new("RS", "Novi Sad")
|
40
|
+
# tz.timezone.name # "Europe/Belgrade"
|
41
|
+
# tz.latitude # nil
|
42
|
+
# tz.longitude # nil
|
43
|
+
#
|
44
|
+
#
|
45
|
+
#
|
46
|
+
def initialize country_code, city, region=nil, latitude=nil, longitude=nil
|
47
|
+
@country_code = country_code.upcase
|
48
|
+
@city = city
|
49
|
+
@region = region
|
50
|
+
@latitude = latitude
|
51
|
+
@longitude = longitude
|
52
|
+
timezone!
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
class << self
|
59
|
+
|
60
|
+
# Returns countries codes for countries with more then one timezone
|
61
|
+
def countries_with_many_tz
|
62
|
+
TZInfo::Country.all.select{|m| m.zones.count > 1}.map{|m| m.code}
|
63
|
+
end
|
64
|
+
|
65
|
+
# Detect timezone by country code city region and position data
|
66
|
+
# Returns TZInfo object and raise errors
|
67
|
+
def get! country_code, city, region="", latitude=nil, longitude=nil
|
68
|
+
m = self.new country_code, city, region, latitude, longitude
|
69
|
+
m.timezone!
|
70
|
+
end
|
71
|
+
|
72
|
+
# Detect timezone by country code city region and position data
|
73
|
+
# Returns TZInfo object and raise errors
|
74
|
+
def get country_code, city, region="", latitude=nil, longitude=nil
|
75
|
+
begin
|
76
|
+
m = self.new country_code, city, region, latitude, longitude
|
77
|
+
m.timezone
|
78
|
+
rescue Exception
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Detect timezone by latitude and longitude
|
85
|
+
# Returns TZInfo raise errors
|
86
|
+
def by_location! latitude, longitude
|
87
|
+
type = TZDetect::Configuration.service
|
88
|
+
case type
|
89
|
+
when :google
|
90
|
+
geocoder = GoogleParser.new(latitude, longitude)
|
91
|
+
when :geoname
|
92
|
+
geocoder = GeonameParser.new(latitude, longitude)
|
93
|
+
else
|
94
|
+
raise TZDetect::Error::Configuration, "wrong configuration for field type"
|
95
|
+
end
|
96
|
+
TZInfo::Timezone.get geocoder.timezone!
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# Detect timezone by latitude and longitude
|
101
|
+
# Returns TZInfo object, if can't find timezone returns nil
|
102
|
+
def by_location latitude, longitude
|
103
|
+
begin
|
104
|
+
self.by_location! latitude, longitude
|
105
|
+
rescue Exception
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# Detect timezone by latitude and longitude
|
113
|
+
# Returns TZInfo object, if can't find timezone returns nil
|
114
|
+
def get_location!(country_code, city, region=nil)
|
115
|
+
type = TZDetect::Configuration.service
|
116
|
+
case type
|
117
|
+
when :google
|
118
|
+
geocoder = GoogleGeocode.new(country_code, city, region)
|
119
|
+
when :geoname
|
120
|
+
geocoder = GeonameGeocode.new(country_code, city, region)
|
121
|
+
else
|
122
|
+
raise TZDetect::Error::Configuration, "wrong configuration for field type"
|
123
|
+
end
|
124
|
+
geocoder.position!
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def tz_name!
|
132
|
+
@timezone = self.class.by_location! @latitude, @longitude
|
133
|
+
end
|
134
|
+
|
135
|
+
def location_fetch!
|
136
|
+
location = self.class.get_location!(@country_code, @city, @region)
|
137
|
+
@latitude = location.latitude
|
138
|
+
@longitude = location.longitude
|
139
|
+
@timezone = TZInfo::Timezone.get(location.timezone) unless location.timezone.nil?
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def get_country!
|
144
|
+
begin
|
145
|
+
return TZInfo::Country.get(@country_code)
|
146
|
+
rescue Exception => e
|
147
|
+
raise TZDetect::Error::Base, e
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns TZInfo object with detected timezone information
|
152
|
+
# can rise errors
|
153
|
+
def timezone!
|
154
|
+
if @timezone.nil?
|
155
|
+
|
156
|
+
tz = get_country!
|
157
|
+
# if country have only one zone then return that zone
|
158
|
+
if tz.zones.count == 1
|
159
|
+
@timezone = TZInfo::Timezone.get(tz.zones[0].name)
|
160
|
+
else
|
161
|
+
location_fetch! if @latitude.nil? or @longitude.nil?
|
162
|
+
# some services returns timezone when fetch location
|
163
|
+
tz_name! if @timezone.nil?
|
164
|
+
end
|
165
|
+
end
|
166
|
+
return @timezone
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
data/lib/TZDetect.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'tzdetect'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require 'helper'
|
5
|
+
|
6
|
+
class TZDetect::ParserTest < Test::Unit::TestCase
|
7
|
+
include TZDetect
|
8
|
+
def test_no_configured_user
|
9
|
+
assert_raises Error::GeoCoder do
|
10
|
+
geocoder = GeonameGeocode.new("LL", "GDSAFDSAF")
|
11
|
+
geocoder.position!
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
def test_geoname_get_location
|
17
|
+
|
18
|
+
assert_nothing_raised do
|
19
|
+
TZDetect::Configuration.username = GEONAME_USERNAME
|
20
|
+
geocoder = GeonameGeocode.new("RS", "Novi Sad")
|
21
|
+
geocoder.position!
|
22
|
+
assert_instance_of Float, geocoder.latitude
|
23
|
+
assert_instance_of Float, geocoder.longitude
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_geoname_wrong_location
|
28
|
+
|
29
|
+
assert_raises Error::GeoCoder do
|
30
|
+
TZDetect::Configuration.username = GEONAME_USERNAME
|
31
|
+
geocoder = GeonameGeocode.new("LL", "tghyjukl")
|
32
|
+
geocoder.position!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'tzdetect'
|
2
|
+
require 'test/unit'
|
3
|
+
class TZDetect::ParserTest < Test::Unit::TestCase
|
4
|
+
include TZDetect
|
5
|
+
def test_geoname_parser_not_configured_username
|
6
|
+
assert_raises Error::Parser do
|
7
|
+
TZDetect::Configuration.username = nil
|
8
|
+
ge = TZDetect::GeonameParser.new(44.2, 22.0)
|
9
|
+
ge.timezone!
|
10
|
+
end
|
11
|
+
end
|
12
|
+
def test_class_method
|
13
|
+
assert_nothing_raised do
|
14
|
+
TZDetect::Configuration.username = GEONAME_USERNAME
|
15
|
+
ge = TZDetect::GeonameParser.timezone!(12.2, 25.0)
|
16
|
+
ge.timezone!
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def test_geoname_parser_bad_input
|
21
|
+
assert_raises Error::Parser do
|
22
|
+
ge = TZDetect::GeonameParser.new(44.2, "gthyju")
|
23
|
+
ge.timezone!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
def test_zones
|
27
|
+
TZDetect::Configuration.username = GEONAME_USERNAME
|
28
|
+
zones = {
|
29
|
+
"Europe/Belgrade"=> [44.2,22.0],
|
30
|
+
"Africa/Tripoli" => [27.2,22.0],
|
31
|
+
"Africa/Johannesburg" => [-27.2,22.0]}
|
32
|
+
zones.each {|key, value|
|
33
|
+
assert_equal(key, TZDetect::GeonamParser.timezone!(value[0], value[1]))
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'tzdetect'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'helper'
|
4
|
+
class TZDetect::ParserTest < Test::Unit::TestCase
|
5
|
+
include TZDetect
|
6
|
+
def test_get_location
|
7
|
+
|
8
|
+
assert_nothing_raised do
|
9
|
+
geocoder = GoogleGeocode.new("RS", "Novi Sad")
|
10
|
+
geocoder.position!
|
11
|
+
assert_instance_of Float, geocoder.latitude
|
12
|
+
assert_instance_of Float, geocoder.longitude
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_wrong_location
|
17
|
+
|
18
|
+
assert_raises Error::GeoCoder do
|
19
|
+
geocoder = GoogleGeocode.new("LL", "rfgthjklfads f")
|
20
|
+
geocoder.position!
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'tzdetect'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require 'helper'
|
5
|
+
class TZDetect::ParserTest < Test::Unit::TestCase
|
6
|
+
include TZDetect
|
7
|
+
|
8
|
+
def test_google_parser
|
9
|
+
assert_nothing_raised do
|
10
|
+
parsed = TZDetect::GoogleParser.new(12.2, 25.0)
|
11
|
+
|
12
|
+
parsed.timezone!
|
13
|
+
parsed = TZDetect::GoogleParser.new(12, 25)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def test_bad_inputs
|
17
|
+
assert_raises Error::Parser do
|
18
|
+
TZDetect::GoogleParser.new(12.2, "dd")
|
19
|
+
end
|
20
|
+
assert_raises Error::Parser do
|
21
|
+
TZDetect::GoogleParser.new("dd", "tghyj")
|
22
|
+
end
|
23
|
+
assert_raises Error::Parser do
|
24
|
+
TZDetect::GoogleParser.new(91, 1)
|
25
|
+
end
|
26
|
+
assert_raises Error::Parser do
|
27
|
+
TZDetect::GoogleParser.new(89.99, 181.1)
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_raises Error::Parser do
|
31
|
+
TZDetect::GoogleParser.new(91.1, 181.1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_class_method
|
36
|
+
assert_nothing_raised do
|
37
|
+
TZDetect::GoogleParser.timezone!(12.2, 25.0)
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
def test_zones
|
42
|
+
zones = {
|
43
|
+
"Europe/Belgrade"=> [44.2,22.0],
|
44
|
+
"Africa/Tripoli" => [27.2,22.0],
|
45
|
+
"Africa/Johannesburg" => [-27.2,22.0]}
|
46
|
+
zones.each {|key, value|
|
47
|
+
assert_equal(key, TZDetect::GoogleParser.timezone!(value[0], value[1]))
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
GEONAME_USERNAME= "geoname"
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'tzdetect'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'helper'
|
4
|
+
class TZDetect::MainTest < Test::Unit::TestCase
|
5
|
+
include TZDetect
|
6
|
+
def test_not_all_properties
|
7
|
+
assert_raises ArgumentError do
|
8
|
+
TimeZone.new()
|
9
|
+
end
|
10
|
+
assert_raises ArgumentError do
|
11
|
+
TZDetect::Configuration.service = :geo
|
12
|
+
end
|
13
|
+
end
|
14
|
+
def test_geoname_get_location
|
15
|
+
assert_nothing_raised do
|
16
|
+
TZDetect::Configuration.username = GEONAME_USERNAME
|
17
|
+
TZDetect::Configuration.service = :geoname
|
18
|
+
tz = TimeZone.new("RS", "Novi Sad")
|
19
|
+
m = tz.timezone
|
20
|
+
assert_kind_of TZInfo::DataTimezone, m
|
21
|
+
assert_equal m.name, "Europe/Belgrade"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
def test_google_get_location
|
25
|
+
assert_nothing_raised do
|
26
|
+
TZDetect::Configuration.service = :google
|
27
|
+
tz = TimeZone.new("RS", "Novi Sad")
|
28
|
+
m = tz.timezone
|
29
|
+
assert_kind_of TZInfo::DataTimezone, m
|
30
|
+
assert_equal m.name, "Europe/Belgrade"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
def test_google_get_location_small_countyr
|
34
|
+
TZDetect::Configuration.service = :google
|
35
|
+
tz = TimeZone.new("rs", "Novi Sad")
|
36
|
+
m = tz.timezone
|
37
|
+
assert_kind_of TZInfo::DataTimezone, m
|
38
|
+
assert_equal m.name, "Europe/Belgrade"
|
39
|
+
end
|
40
|
+
def test_google_get_location_us_static
|
41
|
+
TZDetect::Configuration.service = :google
|
42
|
+
tz = TimeZone.get_location!("US", "Miami", "Florida")
|
43
|
+
assert_kind_of TZDetect::Position, tz
|
44
|
+
end
|
45
|
+
def test_google_get_location_us
|
46
|
+
TZDetect::Configuration.service = :google
|
47
|
+
tz = TimeZone.new("US", "Miami", "Florida")
|
48
|
+
m = tz.timezone
|
49
|
+
assert_kind_of TZInfo::DataTimezone, m
|
50
|
+
assert_equal m.name, "America/New_York"
|
51
|
+
end
|
52
|
+
def test_geoname_get_location_us
|
53
|
+
TZDetect::Configuration.service = :geoname
|
54
|
+
tz = TimeZone.new("US", "Miami", "Florida")
|
55
|
+
m = tz.timezone
|
56
|
+
assert_kind_of TZInfo::DataTimezone, m
|
57
|
+
assert_equal m.name, "America/New_York"
|
58
|
+
end
|
59
|
+
def test_wrong_country
|
60
|
+
assert_raises Error::Base do
|
61
|
+
TZDetect::Configuration.service = :google
|
62
|
+
tz = TimeZone.new("ll", "burutifdas")
|
63
|
+
m = tz.timezone
|
64
|
+
assert_kind_of tzinfo::datatimezone, m
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def test_wrong_country_code_google
|
68
|
+
assert_raises Error::Base do
|
69
|
+
TZDetect::Configuration.service = :geoname
|
70
|
+
tz = TimeZone.new("LL", "buruti")
|
71
|
+
tz.timezone
|
72
|
+
end
|
73
|
+
end
|
74
|
+
def test_static_by_location
|
75
|
+
tz = TimeZone.by_location(44.21, 21.0)
|
76
|
+
assert_kind_of TZInfo::DataTimezone, tz
|
77
|
+
assert_equal tz.name, "Europe/Belgrade"
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_static
|
81
|
+
m = TimeZone.get("rs", "Novi Sad")
|
82
|
+
assert_kind_of TZInfo::DataTimezone, m
|
83
|
+
assert_equal m.name, "Europe/Belgrade"
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tzdetect
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ! 'Bojan Milosavljevic '
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: tzinfo
|
16
|
+
requirement: &70295667282600 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.3.33
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70295667282600
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
requirement: &70295667282140 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.7'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70295667282140
|
36
|
+
description: Detects timezone by address or location using google or geoname web
|
37
|
+
service
|
38
|
+
email:
|
39
|
+
- milboj@gmail.com
|
40
|
+
executables: []
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files: []
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- Gemfile
|
46
|
+
- LICENSE.txt
|
47
|
+
- README.md
|
48
|
+
- Rakefile
|
49
|
+
- TZDetect.gemspec
|
50
|
+
- lib/TZDetect.rb
|
51
|
+
- lib/TZDetect/configuration.rb
|
52
|
+
- lib/TZDetect/error.rb
|
53
|
+
- lib/TZDetect/geocoder.rb
|
54
|
+
- lib/TZDetect/geocoders/geoname.rb
|
55
|
+
- lib/TZDetect/geocoders/google.rb
|
56
|
+
- lib/TZDetect/parser.rb
|
57
|
+
- lib/TZDetect/parsers/geoname.rb
|
58
|
+
- lib/TZDetect/parsers/google.rb
|
59
|
+
- lib/TZDetect/version.rb
|
60
|
+
- lib/TZDetect/zone.rb
|
61
|
+
- test/geoname_geocoder_test.rb
|
62
|
+
- test/geoname_parser_test.rb
|
63
|
+
- test/google_geocoder_test.rb
|
64
|
+
- test/google_parser_test.rb
|
65
|
+
- test/helper.rb
|
66
|
+
- test/main_functionality_test.rb
|
67
|
+
homepage: ''
|
68
|
+
licenses: []
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 1.9.2
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.8.15
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Easy way to detect time zone by address or location
|
91
|
+
test_files:
|
92
|
+
- test/geoname_geocoder_test.rb
|
93
|
+
- test/geoname_parser_test.rb
|
94
|
+
- test/google_geocoder_test.rb
|
95
|
+
- test/google_parser_test.rb
|
96
|
+
- test/helper.rb
|
97
|
+
- test/main_functionality_test.rb
|