module EtOrbi

Constants

VERSION
ZONE_ALIASES

Attributes

_os_zone[RW]

Public Class Methods

_make_info() click to toggle source

For `make info`

# File lib/et-orbi.rb, line 207
def _make_info

  puts render_nozone_time(Time.now.to_f)
  puts platform_info
end
determine_local_tzone() click to toggle source
# File lib/et-orbi.rb, line 678
def determine_local_tzone

  etz = ENV['TZ']

  tz = etz && (::TZInfo::Timezone.get(etz) rescue nil)
  return tz if tz

  if Time.respond_to?(:zone) && Time.zone.respond_to?(:tzinfo)
    tz = Time.zone.tzinfo
    return tz if tz
  end

  tz = ::TZInfo::Timezone.get(os_tz) rescue nil
  return tz if tz

  tzs = determine_local_tzones
  tz = (etz && tzs.find { |z| z.name == etz }) || tzs.first
  return tz if tz

  n = Time.now

  get_tzone(n.zone) ||
  get_tzone(n.strftime('%Z%z'))
end
find_olson_zone(str) click to toggle source
# File lib/et-orbi.rb, line 672
def find_olson_zone(str)

  list_olson_zones(str).each { |s| z = get_tzone(s); return z if z }
  nil
end
get_tzone(o) click to toggle source
# File lib/et-orbi.rb, line 144
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) ||
  (::TZInfo::Timezone.get(s) rescue nil)
end
list_iso8601_zones(s) click to toggle source

en.wikipedia.org/wiki/ISO_8601 Postel's law applies

# File lib/et-orbi.rb, line 644
def list_iso8601_zones(s)

  s.scan(
    %r{
      (?<=:\d\d)
      \s*
      (?:
        [-+]
        (?:[0-1][0-9]|2[0-4])
        (?:(?::)?(?:[0-5][0-9]|60))?
        (?![-+])
        |
        Z
      )
    }x
    ).collect(&:strip)
end
list_olson_zones(s) click to toggle source
# File lib/et-orbi.rb, line 662
def list_olson_zones(s)

  s.scan(
    %r{
      (?<=\s|\A)
      (?:[A-Za-z][A-Za-z0-9+_-]+)
      (?:\/(?:[A-Za-z][A-Za-z0-9+_-]+)){0,2}
    }x)
end
make(*a)
Alias for: make_time
make_from_array(a, zone) click to toggle source
# File lib/et-orbi.rb, line 120
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
make_from_date(d, zone) click to toggle source
# File lib/et-orbi.rb, line 111
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
make_from_eotime(eot, zone) click to toggle source
# File lib/et-orbi.rb, line 138
def make_from_eotime(eot, zone)

  return eot if zone == nil || zone == eot.zone
  EoTime.new(eot.to_f, zone)
end
make_from_numeric(f, zone) click to toggle source
# File lib/et-orbi.rb, line 133
def make_from_numeric(f, zone)

  EoTime.new(Time.now.to_f + f, zone)
end
make_from_string(s, zone) click to toggle source
# File lib/et-orbi.rb, line 128
def make_from_string(s, zone)

  parse(s, zone: zone)
end
make_from_time(t, zone) click to toggle source
# File lib/et-orbi.rb, line 96
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 resulting the error message

  EoTime.new(t, z)
end
make_time(*a) click to toggle source
# File lib/et-orbi.rb, line 74
    def make_time(*a)

#p [ :mt, a ]
      zone = a.length > 1 ? get_tzone(a.last) : nil
      a.pop if zone
#p [ :mt, zone ]

      o = a.length > 1 ? a : a.first
#p [ :mt, :o, o ]

      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
Also aliased as: make
now(zone=nil) click to toggle source
# File lib/et-orbi.rb, line 19
def now(zone=nil)

  EoTime.new(Time.now.to_f, zone)
end
os_tz() click to toggle source
# File lib/et-orbi.rb, line 705
def os_tz

  return (@_os_zone == '' ? nil : @_os_zone)          if defined?(@_os_zone) && @_os_zone

  @os_tz ||= (debian_tz || centos_tz || osx_tz)
end
parse(str, opts={}) click to toggle source
# File lib/et-orbi.rb, line 24
    def parse(str, opts={})

      if defined?(::Chronic) && t = ::Chronic.parse(str, opts)
        return EoTime.new(t, nil)
      end

      #rold = RUBY_VERSION < '1.9.0'
      #rold = RUBY_VERSION < '2.0.0'
      begin
        DateTime.parse(str)
      rescue
        fail ArgumentError, "No time information in #{str.inspect}"
      end #if rold
        #
        # is necessary since Time.parse('xxx') in Ruby < 1.9 yields `now`

      str_zone = get_tzone(list_iso8601_zones(str).last)
#p [ :parse, str, str_zone ]
#p ENV['TZ']

#p [ :parse, :oz, opts[:zone] ]
#p [ :parse, :sz, str_zone ]
#p [ :parse, :foz, find_olson_zone(str) ]
#p [ :parse, :ltz, local_tzone ]
      zone =
        opts[:zone] ||
        str_zone ||
        find_olson_zone(str) ||
        determine_local_tzone
#p [ :parse, :zone, zone ]

      str = str.sub(zone.name, '') unless zone.name.match(/\A[-+]/)
        #
        # for 'Sun Nov 18 16:01:00 Asia/Singapore 2012',
        # although where does rufus-scheduler have it from?

      local = Time.parse(str)
#p [ :parse, :local, local, local.zone ]

      secs =
        if str_zone
          local.to_f
        else
          zone.local_to_utc(local).to_f
        end
#p [ :parse, :secs, secs ]

      EoTime.new(secs, zone)
    end
platform_info() click to toggle source
# File lib/et-orbi.rb, line 177
def platform_info

  etos = Proc.new { |k, v| "#{k}:#{v.inspect}" }

  h = {
    'etz' => ENV['TZ'],
    'tnz' => Time.now.zone,
    'tzid' => defined?(TZInfo::Data),
    '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['eotlzn'] = ltz.name
  end

  "(#{h.map(&etos).join(',')},#{gather_tzs.map(&etos).join(',')})"
end
render_nozone_time(seconds) click to toggle source
# File lib/et-orbi.rb, line 162
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
to_windows_tz(zone_name, time=Time.now) click to toggle source
# File lib/et-orbi.rb, line 713
def to_windows_tz(zone_name, time=Time.now)

  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

  [ abbs[0], tzop, tzoh, tzos, abbs[1] ].compact.join
end
unalias(name) click to toggle source
# File lib/et-orbi/zone_aliases.rb, line 4
def self.unalias(name)

  ZONE_ALIASES[name.sub(/ Daylight /, ' Standard ')] ||
  unzz(name) ||
  name
end
unzz(name) click to toggle source
# File lib/et-orbi/zone_aliases.rb, line 11
def self.unzz(name)

  m = name.match(/\A([A-Z]{3,4})([+-])(\d{1,2}):?(\d{2})?\z/)
  return nil unless m

  abbs = [ m[1] ]; a = m[1]
  abbs << "#{a}T" if a.size < 4

  off = (m[2] == '+' ? 1 : -1) * (m[3].to_i * 3600 + (m[4] || '0').to_i * 60)

  t = Time.now
  twin = Time.utc(t.year, 1, 1) # winter
  tsum = Time.utc(t.year, 7, 1) # summer

  (@tz_all ||= ::TZInfo::Timezone.all)
    .each { |tz|
      abbs.each { |abb|
        per = tz.period_for_utc(twin)
        return tz.name              if per.abbreviation.to_s == abb && per.utc_total_offset == off
        per = tz.period_for_utc(tsum)
        return tz.name              if per.abbreviation.to_s == abb && per.utc_total_offset == off } }

  nil
end

Protected Class Methods

centos_tz() click to toggle source
# File lib/et-orbi.rb, line 833
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
create_offset_tzone(utc_off, id) click to toggle source

TZInfo >= 2.0.0

# File lib/et-orbi.rb, line 774
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
debian_tz() click to toggle source

system tz determination

# File lib/et-orbi.rb, line 826
def debian_tz

  path = '/etc/timezone'

  File.exist?(path) ? File.read(path).strip : nil
rescue; nil; end
determine_local_tzones() click to toggle source
# File lib/et-orbi.rb, line 792
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_all ||= ::TZInfo::Timezone.all
  @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
gather_tzs() click to toggle source
# File lib/et-orbi.rb, line 855
def gather_tzs

  { :debian => debian_tz, :centos => centos_tz, :osx => osx_tz }
end
get_as_tzone(t) click to toggle source
# File lib/et-orbi.rb, line 235
def get_as_tzone(t)

  t.respond_to?(:time_zone) ? t.time_zone : nil
end
get_local_tzone(t) click to toggle source
# File lib/et-orbi.rb, line 215
def get_local_tzone(t)

  #lt = local_tzone
  #lp = lt.period_for_local(t)
  #ab = lp.abbreviation.to_s
  #
  #return lt \
  #  if ab == t.zone
  #return lt \
  #  if ab.match(/\A[-+]\d{2}(:?\d{2})?\z/) && lp.utc_offset == t.utc_offset
  #
  #nil
    #
    # keep that in the fridge for now

  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
get_offset_tzone(str) click to toggle source
# File lib/et-orbi.rb, line 749
def get_offset_tzone(str)

  # custom timezones, no DST, just an offset, like "+08:00" or "-01:30"

  m = str.match(/\A([+-][0-1][0-9]):?([0-5][0-9])?\z/)
  return nil unless m

  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

  return (
    (@custom_tz_cache ||= {})[str] =
      create_offset_tzone(hr * 3600 + mn * 60, str)
  ) if hr

  nil
end
osx_tz() click to toggle source
# File lib/et-orbi.rb, line 846
def osx_tz

  path = '/etc/localtime'

  File.symlink?(path) ?
    File.readlink(path).split('/')[4..-1].join('/') :
    nil
rescue; nil; end
to_offset(n) click to toggle source

protected module methods

# File lib/et-orbi.rb, line 738
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