Module: CATimeLiteral

Defined in:
lib/carray/time.rb

Overview

Reads a time literal (Time / DateTime / Integer unix-seconds / String) into the pieces the CATime constructors need. All parsing is UTC (an explicit offset is honoured; otherwise UTC) and DateTime-independent (Date._parse + a civil-date kernel). Internal to the time surface: nothing here is part of the public API.

Class Method Summary collapse

Class Method Details

.epoch_seconds(spec, format = nil) ⇒ Object

Exact Rational seconds since the Unix epoch for a start literal (Time / DateTime / Integer unix-seconds / String). UTC default.



1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
# File 'lib/carray/time.rb', line 1637

def epoch_seconds(spec, format = nil)
  require 'date'
  require 'time'
  case spec
  when Time     then spec.to_r
  when Integer  then Rational(spec)
  when CATime::Element
    # A time element is already an instant, so it needs no parsing: a
    # fixed unit counts seconds directly, and a calendar one names the
    # first midnight of its granule (the same instant convert_instant!
    # gives it).  This is what lets floor / ceil / to_unit output feed
    # straight back into time_range / time_series / .time.
    if CATimeUnitAlgebra::FIXED.key?(spec.unit.base)
      Rational(spec.value) * spec.unit.tick_ratio
    else
      Rational(CATimeUnitAlgebra.calendar_days_since_epoch(CA_INT64([spec.value]),
                                           spec.unit)[0] * 86400)
    end
  when String
    h = parse_date_fields(spec, format)
    days = CATimeCivil.days_from_civil(CA_INT64([h[:year]]), CA_INT64([h[:mon] || 1]),
                             CA_INT64([h[:mday] || 1]))[0]
    sec  = Rational(days * 86400 + (h[:hour] || 0) * 3600 +
                    (h[:min] || 0) * 60 + (h[:sec] || 0))
    sec += h[:sec_fraction] if h[:sec_fraction]
    sec -= h[:offset]       if h[:offset]      # east-of-UTC offset -> UTC
    sec
  else
    if defined?(DateTime) && spec.is_a?(DateTime)
      spec.to_time.to_r
    else
      raise ArgumentError, "cannot parse time #{spec.class}"
    end
  end
end

.tick_index(spec, res, format = nil) ⇒ Object

Tick index of spec's instant on the res grid (floor toward the past).



1704
1705
1706
1707
1708
1709
1710
1711
1712
# File 'lib/carray/time.rb', line 1704

def tick_index(spec, res, format = nil)
  if CATimeUnitAlgebra::FIXED.key?(res.base)
    (epoch_seconds(spec, format) / res.tick_ratio).floor
  else
    y, m    = year_month(spec, format)
    months  = (y - 1970) * 12 + (m - 1)
    (Rational(months) / res.tick_ratio).floor
  end
end

.to_time_array(literal, res, format, on_error) ⇒ Object

Single-literal build for time: a 1-element CATime, honouring the on_error policy (raise, or a masked cell).



1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
# File 'lib/carray/time.rb', line 1716

def to_time_array(literal, res, format, on_error)
  raw = CArray.int64(1)
  begin
    raw[0] = tick_index(literal, res, format)
  rescue ArgumentError, TypeError
    raise if on_error == :raise
    raw[0] = UNDEF
  end
  raw.time(unit: res)
end

.year_month(spec, format = nil) ⇒ Object

[year, month] (UTC) of a start literal, for a calendar-resolution grid.



1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
# File 'lib/carray/time.rb', line 1674

def year_month(spec, format = nil)
  require 'date'
  require 'time'
  case spec
  when Time    then t = spec.utc; [t.year, t.month]
  when Integer then t = Time.at(spec, in: 'UTC'); [t.year, t.month]
  when CATime::Element
    # A calendar element is a month ordinal already, so read it as one
    # rather than going out through Time (which a :Y / :M element would
    # have to re-derive).  Integer / and % floor, so a pre-epoch value
    # lands in the right month.
    case spec.unit.base
    when :M then mo = spec.value * spec.unit.count + 1970 * 12
                 [mo / 12, mo % 12 + 1]
    when :Y then [spec.value * spec.unit.count + 1970, 1]
    else         t = spec.to_time.utc; [t.year, t.month]
    end
  when String
    h = parse_date_fields(spec, format)
    [h[:year], h[:mon] || 1]
  else
    if defined?(DateTime) && spec.is_a?(DateTime)
      t = spec.to_time.utc; [t.year, t.month]
    else
      raise ArgumentError, "cannot parse time #{spec.class}"
    end
  end
end