Class: CATime::Element

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/carray/time.rb

Overview

Element-return class: a single time element carries both its int64 count and its resolution, so scalar access (ca) round-trips through this rather than a bare Integer.

Constant Summary collapse

EPOCH_JD =

storage + value/unit accessors + initialize are C-implemented in ext/ca_obj_datetime.c (= TypedData_Make_Struct fast path). Methods below are Ruby-side; they call value/unit which are C accessors.

Calendar breakdown is delegated to Ruby Date / Time (the scalar pays one object's cost, unlike the vectorized array accessors), but exactly: a :M / :Y value decodes through Date#next_month / #next_year (NOT the 30.5-day UNIT_TO_SECONDS approximation, which drifts), and a fixed-unit value through an exact Rational-second Time.at. All UTC.

2440588

Instance Method Summary collapse

Instance Method Details

#+(td) ⇒ CATime::Element

Returns this instant advanced by a CATimedelta::Element. The time is the anchor: the result keeps self's unit and the duration is converted into it, truncated toward zero when finer (a :D time + a 5 h duration is + 0 days; + 30 h is + 1 day) -- the same rule as the array CATime#+. Only a duration in the SAME group (both calendar or both fixed) is accepted -- a cross-group step (a :s time + a :M duration) is calendar arithmetic; use to_date + Date#next_month / #next_year for that.

Parameters:

Returns:

Raises:

  • (TypeError, ArgumentError)

    on a non-timedelta / cross-group operand.



515
516
517
# File 'lib/carray/time.rb', line 515

def +(td)
  shifted_by_duration(td, 1)
end

#-(other) ⇒ CATime::Element, CATimedelta::Element

Subtracting a CATimedelta::Element yields a CATime::Element (same rule as +); subtracting another CATime::Element yields the elapsed duration as a CATimedelta::Element, in the diff_unit of the two (same group -> finer unit; cross-group -> the fixed unit).



526
527
528
529
530
531
532
533
534
535
536
# File 'lib/carray/time.rb', line 526

def -(other)
  case other
  when Element
    u = CATimeUnitAlgebra.diff_unit(unit, other.unit)
    CATimedelta::Element.new(instant_in(u) - other.instant_in(u), u)
  when CATimedelta::Element
    shifted_by_duration(other, -1)
  else
    raise TypeError, "CATime::Element - #{other.class} is not allowed"
  end
end

#<=>(other) ⇒ Object

Three-way compare by INSTANT, reconciling a different unit (both are brought to a common unit both reach exactly, via diff_unit), so two datetimes are always ordered regardless of unit. Returns nil for a non-time operand (Time / DateTime are accepted); per the Comparable contract that makes == false and < raise, so no explicit == is defined (Comparable derives it: same instant -> equal, even cross-unit).



477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/carray/time.rb', line 477

def <=>(other)
  case other
  when Element
    u = CATimeUnitAlgebra.diff_unit(unit, other.unit)
    instant_in(u) <=> other.instant_in(u)
  when Time
    to_time <=> other.getutc
  when (defined?(DateTime) ? DateTime : nil)
    to_time <=> other.to_time.getutc
  end
rescue ArgumentError
  nil
end

#eql?(other) ⇒ Boolean

Hash-key identity is unit-strict (mirrors Ruby: 1 == 1.0 but not 1.eql?(1.0)), so a :s and a :ms scalar at the same instant compare == yet key a Hash separately.

Returns:

  • (Boolean)


494
495
496
# File 'lib/carray/time.rb', line 494

def eql?(other)
  other.is_a?(Element) && other.unit == unit && other.value == value
end

#hashInteger

Returns a hash consistent with #eql? (unit-strict).

Returns:

  • (Integer)

    a hash consistent with #eql? (unit-strict).



499
500
501
# File 'lib/carray/time.rb', line 499

def hash
  [value, unit].hash
end

#inspectString

Returns the instant plus its storage unit.

Returns:

  • (String)

    the instant plus its storage unit.



466
467
468
469
# File 'lib/carray/time.rb', line 466

def inspect
  tag = unit.count == 1 ? "#{value}#{unit.base}" : "#{value} @ #{unit}"
  "#<CATime::Element #{to_s} (#{tag})>"
end

#to_dateDate

Returns the date this scalar denotes (UTC). A sub-day unit is floored to its day.

Returns:

  • (Date)

    the date this scalar denotes (UTC). A sub-day unit is floored to its day.



436
437
438
439
440
441
442
443
444
445
# File 'lib/carray/time.rb', line 436

def to_date
  require 'date'
  case unit.base
  when :Y then epoch_date.next_year(value * unit.count)
  when :M then epoch_date.next_month(value * unit.count)
  when :W then Date.jd(EPOCH_JD + value * unit.count * 7, Date::GREGORIAN)
  when :D then Date.jd(EPOCH_JD + value * unit.count, Date::GREGORIAN)
  else         Date.jd(EPOCH_JD + floor_to_days_since_epoch, Date::GREGORIAN)  # sub-day: floor to day
  end
end

#to_datetimeDateTime

Returns the instant this scalar denotes (UTC).

Returns:

  • (DateTime)

    the instant this scalar denotes (UTC).



448
449
450
# File 'lib/carray/time.rb', line 448

def to_datetime
  to_time.to_datetime
end

#to_sString

Unit-aware string: coarse units print at their own granularity so the (value, unit) pair stays recoverable (a :M scalar is "2024-03", not "2024-03-01T00:00:00Z", which is indistinguishable from a :D).

Returns:

  • (String)


456
457
458
459
460
461
462
463
# File 'lib/carray/time.rb', line 456

def to_s
  case unit.base
  when :Y      then format("%04d", to_date.year)
  when :M      then to_date.strftime("%Y-%m")
  when :W, :D  then to_date.strftime("%Y-%m-%d")
  else              to_time.iso8601(fractional_second_digits)  # :h .. :as (time shown)
  end
end

#to_timeTime

Returns the instant this scalar denotes (UTC). For a calendar unit this is the first instant (midnight of day 1) of the granule.

Returns:

  • (Time)

    the instant this scalar denotes (UTC). For a calendar unit this is the first instant (midnight of day 1) of the granule.



423
424
425
426
427
428
429
430
431
432
# File 'lib/carray/time.rb', line 423

def to_time
  require 'time'
  require 'date'
  case unit.base
  when :Y then d = epoch_date.next_year(value * unit.count);  Time.utc(d.year, d.month, d.day)
  when :M then d = epoch_date.next_month(value * unit.count); Time.utc(d.year, d.month, d.day)
  else
    Time.at(Rational(value) * unit.tick_ratio, in: 'UTC')  # exact seconds
  end
end