tzdetect 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|