lib/et-orbi.rb in et-orbi-1.1.7 vs lib/et-orbi.rb in et-orbi-1.1.8

- old
+ new

@@ -2,512 +2,17 @@ require 'date' if RUBY_VERSION < '1.9.0' require 'time' require 'tzinfo' -require 'et-orbi/eo_time' -require 'et-orbi/zone_aliases' +require 'et-orbi/info' +require 'et-orbi/make' +require 'et-orbi/time' +require 'et-orbi/zones' +require 'et-orbi/zone' module EtOrbi - VERSION = '1.1.7' - - # - # module methods - - class << self - - def now(zone=nil) - - EoTime.new(Time.now.to_f, zone) - end - - def parse(str, opts={}) - - str, str_zone = extract_zone(str) - - if defined?(::Chronic) && t = ::Chronic.parse(str, opts) - - str = [ t.strftime('%F %T'), str_zone ].compact.join(' ') - end - - begin - DateTime.parse(str) - rescue - fail ArgumentError, "No time information in #{str.inspect}" - end - #end if RUBY_VERSION < '1.9.0' - #end if RUBY_VERSION < '2.0.0' - # - # is necessary since Time.parse('xxx') in Ruby < 1.9 yields `now` - - zone = - opts[:zone] || - get_tzone(str_zone) || - determine_local_tzone - - local = Time.parse(str) - secs = zone.local_to_utc(local).to_f - - EoTime.new(secs, zone) - end - - def make_time(*a) - - zone = a.length > 1 ? get_tzone(a.last) : nil - a.pop if zone - - o = a.length > 1 ? a : a.first - - case o - when Time then make_from_time(o, zone) - when Date then make_from_date(o, zone) - when Array then make_from_array(o, zone) - when String then make_from_string(o, zone) - when Numeric then make_from_numeric(o, zone) - when ::EtOrbi::EoTime then make_from_eotime(o, zone) - else fail ArgumentError.new( - "Cannot turn #{o.inspect} to a ::EtOrbi::EoTime instance") - end - end - alias make make_time - - def make_from_time(t, zone) - - z = - zone || - get_as_tzone(t) || - get_tzone(t.zone) || - get_local_tzone(t) - - z ||= t.zone - # pass the abbreviation anyway, - # it will be used in the resulting error message - - EoTime.new(t, z) - end - - def make_from_date(d, zone) - - make_from_time( - d.respond_to?(:to_time) ? - d.to_time : - Time.parse(d.strftime('%Y-%m-%d %H:%M:%S')), - zone) - end - - def make_from_array(a, zone) - - t = Time.utc(*a) - s = t.strftime("%Y-%m-%d %H:%M:%S.#{'%06d' % t.usec}") - - make_from_string(s, zone) - end - - def make_from_string(s, zone) - - parse(s, zone: zone) - end - - def make_from_numeric(f, zone) - - EoTime.new(Time.now.to_f + f, zone) - end - - def make_from_eotime(eot, zone) - - return eot if zone == nil || zone == eot.zone - EoTime.new(eot.to_f, zone) - end - - def get_tzone(o) - - return o if o.is_a?(::TZInfo::Timezone) - return nil if o == nil - return determine_local_tzone if o == :local - return ::TZInfo::Timezone.get('Zulu') if o == 'Z' - return o.tzinfo if o.respond_to?(:tzinfo) - - o = to_offset(o) if o.is_a?(Numeric) - - return nil unless o.is_a?(String) - - s = unalias(o) - - get_offset_tzone(s) || - get_x_offset_tzone(s) || - (::TZInfo::Timezone.get(s) rescue nil) - end - - def render_nozone_time(seconds) - - t = - Time.utc(1970) + seconds - ts = - t.strftime('%Y-%m-%d %H:%M:%S') + - ".#{(seconds % 1).to_s.split('.').last}" - tz = - EtOrbi.determine_local_tzone - z = - tz ? tz.period_for_local(t).abbreviation.to_s : nil - - "(secs:#{seconds},utc~:#{ts.inspect},ltz~:#{z.inspect})" - end - - def tzinfo_version - - #TZInfo::VERSION - Gem.loaded_specs['tzinfo'].version.to_s - rescue => err - err.inspect - end - - def tzinfo_data_version - - #TZInfo::Data::VERSION rescue nil - Gem.loaded_specs['tzinfo-data'].version.to_s rescue nil - end - - def platform_info - - etos = Proc.new { |k, v| "#{k}:#{v.inspect}" } - - h = { - 'etz' => ENV['TZ'], - 'tnz' => Time.now.zone, - 'tziv' => tzinfo_version, - 'tzidv' => tzinfo_data_version, - 'rv' => RUBY_VERSION, - 'rp' => RUBY_PLATFORM, - 'win' => Gem.win_platform?, - 'rorv' => (Rails::VERSION::STRING rescue nil), - 'astz' => ([ Time.zone.class, Time.zone.tzinfo.name ] rescue nil), - 'eov' => EtOrbi::VERSION, - 'eotnz' => '???', - 'eotnfz' => '???', - 'eotlzn' => '???' } - if ltz = EtOrbi::EoTime.local_tzone - h['eotnz'] = EtOrbi::EoTime.now.zone - h['eotnfz'] = EtOrbi::EoTime.now.strftime('%z') - h['eotnfZ'] = EtOrbi::EoTime.now.strftime('%Z') - h['eotlzn'] = ltz.name - end - - "(#{h.map(&etos).join(',')},#{gather_tzs.map(&etos).join(',')})" - end - - # For `make info` - # - def _make_info - - puts render_nozone_time(Time.now.to_f) - puts platform_info - end - - ZONES_ISO8601 = - %r{ - (?<=:\d\d)\s* - (?: - [-+] - (?:[0-1][0-9]|2[0-4]) - (?:(?::)?(?:[0-5][0-9]|60))? - (?![-+]) - |Z - ) - }x - - # https://en.wikipedia.org/wiki/ISO_8601 - # Postel's law applies - # - def list_iso8601_zones(s) - - s.scan(ZONES_ISO8601).collect(&:strip) - end - - ZONES_OLSON = ( - TZInfo::Timezone.all.collect { |z| z.name }.sort + - (0..12).collect { |i| [ "UTC-#{i}", "UTC+#{i}" ] }) - .flatten - .sort_by(&:size) - .reverse - - def list_olson_zones(s) - - s = s.dup - - ZONES_OLSON - .inject([]) { |a, z| - i = s.index(z); next a unless i - s[i, z.length] = '' - a << z - a } - end - - def find_olson_zone(str) - - list_olson_zones(str).each { |s| z = get_tzone(s); return z if z } - nil - end - - def extract_zone(str) - - s = str.dup - - zs = ZONES_OLSON - .inject([]) { |a, z| - i = s.index(z); next a unless i - a << z - s[i, z.length] = '' - a } - - s.gsub!(ZONES_ISO8601) { |m| zs << m.strip; '' } #if zs.empty? - - zs = zs.sort_by { |z| str.index(z) } - - [ s.strip, zs.last ] - end - - def determine_local_tzone - - # ENV has the priority - - etz = ENV['TZ'] - - tz = etz && get_tzone(etz) - return tz if tz - - # then Rails/ActiveSupport has the priority - - if Time.respond_to?(:zone) && Time.zone.respond_to?(:tzinfo) - tz = Time.zone.tzinfo - return tz if tz - end - - # then the operating system is queried - - tz = ::TZInfo::Timezone.get(os_tz) rescue nil - return tz if tz - - # then Ruby's time zone abbs are looked at CST, JST, CEST, ... :-( - - tzs = determine_local_tzones - tz = (etz && tzs.find { |z| z.name == etz }) || tzs.first - return tz if tz - - # then, fall back to GMT offest :-( - - n = Time.now - - get_tzone(n.zone) || - get_tzone(n.strftime('%Z%z')) - end - alias zone determine_local_tzone - - attr_accessor :_os_zone # test tool - - def os_tz - - return (@_os_zone == '' ? nil : @_os_zone) \ - if defined?(@_os_zone) && @_os_zone - - @os_tz ||= (debian_tz || centos_tz || osx_tz) - end - - # Semi-helpful, since it requires the current time - # - def windows_zone_name(zone_name, time) - - twin = Time.utc(time.year, 1, 1) # winter - tsum = Time.utc(time.year, 7, 1) # summer - - tz = ::TZInfo::Timezone.get(zone_name) - tzo = tz.period_for_local(time).utc_total_offset - tzop = tzo < 0 ? nil : '-'; tzo = tzo.abs - tzoh = tzo / 3600 - tzos = tzo % 3600 - tzos = tzos == 0 ? nil : ':%02d' % (tzos / 60) - - abbs = [ - tz.period_for_utc(twin).abbreviation.to_s, - tz.period_for_utc(tsum).abbreviation.to_s ] - .uniq - - if abbs[0].match(/\A[A-Z]/) - [ abbs[0], tzop, tzoh, tzos, abbs[1] ] - .compact.join - else - [ windows_zone_code_x(zone_name), tzop, tzoh, tzos || ':00', zone_name ] - .collect(&:to_s).join - end - end - - # - # protected module methods - - protected - - def windows_zone_code_x(zone_name) - - a = [ '_' ] - a.concat(zone_name.split('/')[0, 2].collect { |s| s[0, 1].upcase }) - a << '_' if a.size < 3 - - a.join - end - - def get_local_tzone(t) - - l = Time.local(t.year, t.month, t.day, t.hour, t.min, t.sec, t.usec) - - (t.zone == l.zone) ? determine_local_tzone : nil - end - - # https://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html - # - # If it responds to #time_zone, then return that time zone. - # - def get_as_tzone(t) - - t.respond_to?(:time_zone) ? t.time_zone : nil - end - - def to_offset(n) - - i = n.to_i - sn = i < 0 ? '-' : '+'; i = i.abs - hr = i / 3600; mn = i % 3600; sc = i % 60 - - sc > 0 ? - '%s%02d:%02d:%02d' % [ sn, hr, mn, sc ] : - '%s%02d:%02d' % [ sn, hr, mn ] - end - - # custom timezones, no DST, just an offset, like "+08:00" or "-01:30" - # - def get_offset_tzone(str) - - m = str.match(/\A([+-][0-1]?[0-9]):?([0-5][0-9])?\z/) rescue nil - # - # On Windows, the real encoding could be something other than UTF-8, - # and make the match fail - # - return nil unless m - - tz = custom_tzs[str] - return tz if tz - - hr = m[1].to_i - mn = m[2].to_i - - hr = nil if hr.abs > 11 - hr = nil if mn > 59 - mn = -mn if hr && hr < 0 - - hr ? - custom_tzs[str] = create_offset_tzone(hr * 3600 + mn * 60, str) : - nil - end - - if defined?(TZInfo::DataSources::ConstantOffsetDataTimezoneInfo) - # TZInfo >= 2.0.0 - - def create_offset_tzone(utc_off, id) - - off = TZInfo::TimezoneOffset.new(utc_off, 0, id) - tzi = TZInfo::DataSources::ConstantOffsetDataTimezoneInfo.new(id, off) - tzi.create_timezone - end - - else - # TZInfo < 2.0.0 - - def create_offset_tzone(utc_off, id) - - tzi = TZInfo::TransitionDataTimezoneInfo.new(id) - tzi.offset(id, utc_off, 0, id) - tzi.create_timezone - end - end - - def get_x_offset_tzone(str) - - m = str.match(/\A_..-?[0-1]?\d:?(?:[0-5]\d)?(.+)\z/) rescue nil - # - # On Windows, the real encoding could be something other than UTF-8, - # and make the match fail (as in .get_offset_tzone above) - - m ? ::TZInfo::Timezone.get(m[1]) : nil - end - - def determine_local_tzones - - tabbs = (-6..5) - .collect { |i| - t = Time.now + i * 30 * 24 * 3600 - "#{t.zone}_#{t.utc_offset}" } - .uniq - .sort - .join('|') - - t = Time.now - #tu = t.dup.utc # /!\ dup is necessary, #utc modifies its target - - twin = Time.local(t.year, 1, 1) # winter - tsum = Time.local(t.year, 7, 1) # summer - - @tz_winter_summer ||= {} - - @tz_winter_summer[tabbs] ||= tz_all - .select { |tz| - pw = tz.period_for_local(twin) - ps = tz.period_for_local(tsum) - tabbs == - [ "#{pw.abbreviation}_#{pw.utc_total_offset}", - "#{ps.abbreviation}_#{ps.utc_total_offset}" ] - .uniq.sort.join('|') } - - @tz_winter_summer[tabbs] - end - - def custom_tzs; @custom_tzs ||= {}; end - def tz_all; @tz_all ||= ::TZInfo::Timezone.all; end - - # - # system tz determination - - def debian_tz - - path = '/etc/timezone' - - File.exist?(path) ? File.read(path).strip : nil - rescue; nil; end - - def centos_tz - - path = '/etc/sysconfig/clock' - - File.open(path, 'rb') do |f| - until f.eof? - if m = f.readline.match(/ZONE="([^"]+)"/); return m[1]; end - end - end if File.exist?(path) - - nil - rescue; nil; end - - def osx_tz - - path = '/etc/localtime' - - File.symlink?(path) ? - File.readlink(path).split('/')[4..-1].join('/') : - nil - rescue; nil; end - - def gather_tzs - - { :debian => debian_tz, :centos => centos_tz, :osx => osx_tz } - end - end + VERSION = '1.1.8' end