lib/timezone_parser/data/tzinfo.rb
# encoding: utf-8
require 'tzinfo'
require 'tzinfo/data/tzdataparser'
require 'uri'
require 'open-uri'
require 'rubygems/package'
require 'zlib'
require 'yaml'
require 'pathname'
require_relative '../data'
module TimezoneParser
# TZInfo module
module TZInfo
protected
@@Version = nil
@@Timezones = nil
@@TimezoneCountries = nil
public
# TZData source location
TZDataSource = 'ftp://ftp.iana.org/tz/tzdata-latest.tar.gz'
# Extracted TZData path
TZDataPath = TimezoneParser::Data::VendorDir + 'zoneinfo'
# TZInfo data path
TZInfoData = TimezoneParser::Data::VendorDir + 'tzinfo'
# Max Timestamp
LastTimestamp = 2147483647
def self.download(source = TZDataSource, location = TZDataPath, target = TZInfoData)
URI.parse(source).open do |tempfile|
FileUtils.mkdir_p(location)
tar = Gem::Package::TarReader.new(Zlib::GzipReader.open(tempfile.path))
tar.each do |entry|
path = location + entry.full_name
FileUtils.mkdir_p(path.dirname)
if entry.file?
File.open(path, 'wb') do |file|
file.write(entry.read)
end
end
end
tar.close
end
parser = ::TZInfo::Data::TZDataParser.new(location, target)
parser.execute
getVersion(location)
end
def self.getVersion(source = TZDataPath)
return @@Version if @@Version
@@Version = File.read(source + 'version', { :mode => 'r', :encoding => 'UTF-8:UTF-8' }).strip
@@Version
end
def self.init
::TZInfo::DataSource.set(:ruby, TZInfoData)
end
def self.getTimezones
unless @@Timezones
@@Timezones = ::TZInfo::Timezone.all_identifiers
end
@@Timezones
end
def self.getTimezoneCountries
unless @@TimezoneCountries
@@TimezoneCountries = {}
::TZInfo::Country.all.each do |countryData|
countryData.zone_identifiers.each do |timezone|
@@TimezoneCountries[timezone] ||= []
@@TimezoneCountries[timezone] << countryData.code
@@TimezoneCountries[timezone].uniq!
@@TimezoneCountries[timezone].sort!
end
end
::TZInfo::Timezone.all_linked_zones.each do |zone|
timezone = zone.identifier
actual_timezone = zone.canonical_zone.identifier
@@TimezoneCountries[timezone] = @@TimezoneCountries[actual_timezone].dup unless @@TimezoneCountries[actual_timezone].nil?
end
@@TimezoneCountries = Hash[@@TimezoneCountries.to_a.sort_by { |pair| pair.first }]
end
@@TimezoneCountries
end
def self.getAbbreviations
transitionData = {}
::TZInfo::Timezone.all_data_zone_identifiers.each do |name|
zone = ::TZInfo::Timezone.get(name)
zone_transitions = zone.transitions_up_to(Time.at(LastTimestamp))
zone_transitions.each_index do |i|
offset = zone_transitions[i].offset
next if offset.abbreviation == :LMT or offset.abbreviation == :zzz
abbr = offset.abbreviation.to_s
transitionData[abbr] = [] unless transitionData[abbr]
period = ::TZInfo::TimezonePeriod.new(zone_transitions[i], zone_transitions[i+1])
transitionData[abbr] << { :name => name, :period => period }
end
end
timezoneData = {}
transitionData.keys.sort.each do |name|
transitions = []
transitionData[name].each do |transition|
period_start = transition[:period].utc_start
period_start = period_start.to_s
period_end = transition[:period].utc_end
period_end = period_end.to_s if period_end
timezone = transition[:name]
countries = getTimezoneCountries[timezone]
countries = countries.dup if countries
data = { 'Offset' => transition[:period].utc_total_offset, 'Timezones' => [timezone], 'Countries' => countries, 'From' => period_start }
data['To'] = period_end if period_end
transitions << data
end
transitions.sort_by! { |data| [data['To'] ? data['To'] : 'zzzz', data['From']] }
abbreviationData = []
abbreviationData << transitions.shift
transitions.each do |data|
current = abbreviationData.last
if data['Offset'] == current['Offset'] and (current['Timezones'].sort == data['Timezones'].sort || (data['From'] == current['From'] and data['To'] == current['To']))
current['Timezones'] += data['Timezones']
current['Timezones'].uniq!
current['Timezones'].sort!
if data['Countries']
current['Countries'] = current['Countries'].to_a + data['Countries']
current['Countries'].uniq!
current['Countries'].sort!
end
current['To'] = data['To']
current.delete('To') unless current['To']
else
abbreviationData << data
end
end
previous = nil
abbreviationData.delete_if do |item|
if previous.nil?
previous = item
false
elsif previous['Offset'] == item['Offset'] and previous['Timezones'] == item['Timezones']
previous['To'] = item['To']
previous.delete('To') unless previous['To']
true
else
previous = item
false
end
end
timezoneData[name] = abbreviationData
end
timezoneData
end
end
end