require 'tzinfo' require 'delegate' module Barometer module Data class Zone < SimpleDelegator def initialize(zone) zone = if ZoneFull.detect?(zone) ZoneFull.new(zone) elsif ZoneOffset.detect?(zone) ZoneOffset.new(zone) elsif ZoneCode.detect?(zone) ZoneCode.new(zone) else raise(ArgumentError, "invalid time zone") end super(zone) end end class ZoneFull def self.detect?(zone) zone.respond_to?(:match) && !!zone.match(/^[A-Za-z]+\/[A-Za-z_]+$/) end def initialize(zone, time_class=::Time) @zone = zone @time_class = time_class @tz = TZInfo::Timezone.get(zone) end def code tz.period_for_utc(time_class.now.utc).zone_identifier.to_s end def offset tz.period_for_utc(time_class.now.utc).utc_total_offset end def now tz.utc_to_local(time_class.now.utc) end def to_s zone end def local_to_utc(local_time) tz.local_to_utc(local_time) end def utc_to_local(utc_time) tz.utc_to_local(utc_time) end private attr_reader :zone, :tz, :time_class end class ZoneOffset def self.detect?(zone) zone.respond_to?(:abs) && zone.abs <= 14 end def initialize(zone, time_class=::Time) @zone = zone @time_class = time_class end def code end def offset zone.to_f * 60 * 60 end def now time_class.now.utc + offset end def to_s zone.to_s end def local_to_utc(local_time) local_time - offset end def utc_to_local(utc_time) utc_time + offset end private attr_reader :zone, :time_class end class ZoneCode def self.detect?(zone) zone.respond_to?(:to_s) && Utils::ZoneCodeLookup.exists?(zone.to_s) end def initialize(zone, time_class=::Time) @zone = zone @time_class = time_class end def code zone end def offset Utils::ZoneCodeLookup.offset(zone) end def now time_class.now.utc + offset end def to_s zone end def local_to_utc(local_time) local_time - offset end def utc_to_local(utc_time) utc_time + offset end private attr_reader :zone, :time_class end end end