Class: CATime
- Inherits:
-
Object
- Object
- CATime
- Defined in:
- lib/carray/time.rb,
lib/carray/time.rb,
lib/carray/time.rb
Overview
============================================================================ timestep system (P1/P2 integer path)
Projects an absolute time onto an integer "step index" -- the k-th
fixed-width bucket of step counted from origin -- and the inverse /
rounding / on-grid operators built on it. All storage-domain int64 and
vectorized; the crown jewel is that bucketing, matching, and positional
addressing across differently-scaled series all become integer arithmetic.
Integer path covers: fixed-length step on fixed-length storage, and Y/M step on Y/M storage (:M storage already encodes a linear month ordinal, so it floor-divides identically). The calendar path (Y/M step on sub-day storage, via civil-date algebra) is P3 and currently raises.
See devel/PROPOSAL_DATETIME64_STEP_SYSTEM.md.
Defined Under Namespace
Classes: Element, Resolution
Constant Summary collapse
- SU_LE_DAY =
Base units that are day-or-finer (a period head is representable exactly; the civil path targets these for a calendar bucket).
%i[D h m s ms us ns ps fs as].freeze
Reductions collapse
-
#mean(axis: nil, **opts) ⇒ Element, CATime
Returns the centroid time on
self's unit (§8), rounded to the nearest tick. -
#median(axis: nil, **opts) ⇒ Element, CATime
Returns the median time on
self's unit (§8). -
#minmax(*axes, **opts) ⇒ Array(Element, Element), Array(CATime, CATime)
Returns the earliest and latest time as a
[min, max]pair (§8). - #percentile(*p, axis: nil, **opts) ⇒ Element, ...
-
#quantile(axis: nil, **opts) ⇒ Array<Element>, Array<CATime>
Returns the five quartile instants
[p0, p25, p50, p75, p100]onself's unit (shorthand forpercentile(0, 25, 50, 75, 100)). -
#stddev(axis: nil, **opts) ⇒ CATimedelta::Element, CATimedelta
Returns the spread of the instants as a CATimedelta (a duration) on
self's unit. -
#stddevp(axis: nil, **opts) ⇒ CATimedelta::Element, CATimedelta
Returns the population spread as a CATimedelta, in the same shapes as #stddev.
-
#sum(*) ⇒ Object
Not supported; use #mean for a centroid.
-
#variance(*) ⇒ Object
Not supported: the variance of instants has squared-time units, which no type represents (ill-defined, like #sum).
-
#variancep(*) ⇒ Object
Not supported, for the same reason as #variance.
Field accessors collapse
-
#day ⇒ CArray
Day of the month, 1..31.
-
#hour ⇒ CArray
Hour of the day, 0..23; 0 when the storage unit is coarser than an hour.
-
#minute ⇒ CArray
Minute of the hour, 0..59; 0 when the storage unit is coarser than a minute.
-
#month ⇒ CArray
Calendar month, 1..12.
-
#second ⇒ CArray
Second of the minute, 0..59; 0 when the storage unit is coarser than a second.
-
#weekday ⇒ CArray
Day of the week, Sunday = 0 ..
-
#yday ⇒ CArray
Day of the year, 1..366.
-
#year ⇒ CArray
Each accessor returns an integer CArray with the requested calendar / clock field extracted from every element (UTC), computed by vectorized civil-date algebra directly on the int64 storage -- no per-cell Time.
Class Method Summary collapse
-
.from_timesteps(k, unit:, origin: nil) ⇒ Element, CATime
Inverse of #timesteps: returns the bucket-head time for timestep
k, stored on theunitgrid. -
.new(*shape, unit: :ns) ⇒ CATime
Allocates a new int64 storage CArray of the given
shapeand wraps it as a CATime Face with the givenunit. -
.wrap(raw, unit: :ns) ⇒ CATime
Zero-copy Face wrap of an existing int64 CArray.
Instance Method Summary collapse
-
#+(other) ⇒ CATime
Returns
self + otherfor a CATimedelta: the time is the anchor, so the result keepsself's unit and the duration is converted into it (a duration finer thanself's unit is truncated to it; a cross-group calendar duration raises). -
#-(other) ⇒ CATime, CATimedelta
Subtracting a CATimedelta yields a CATime at
self's unit (the duration is converted into it, truncated when finer); subtracting another CATime yields a CATimedelta at the finer of the two units (cross-group falls to the fixed unit). -
#ajd ⇒ CArray
Returns the Astronomical Julian Day (float, offset by half a day) for each element.
-
#ceil(unit:, origin: nil) ⇒ CATime
Returns each element raised to its bucket head at or after it (an element already on a boundary maps to itself), as a CATime.
-
#floor(unit:, origin: nil) ⇒ CATime
Returns each element floored to its bucket head (toward the past), as a CATime in the same storage resolution.
-
#is_leap ⇒ CArray
Returns a boolean CArray flagging elements that fall in a leap year (UTC).
-
#is_righttime(unit:, origin: nil) ⇒ CArray
Returns a boolean CArray flagging elements that land exactly on a bucket head.
-
#jd ⇒ CArray
Returns the Julian Day Number for each element.
-
#linear_fetch(addr, axis: nil) ⇒ Element, CATime
Returns the time at the fractional position
addron this array's grid, interpolating between the two bracketing instants. -
#round(unit:, origin: nil) ⇒ CATime
Returns each element rounded to its nearest bucket head (ties toward the future, matching
snap:round), as a CATime. -
#scalar_to_storage(surface) ⇒ Integer, Object
Write-direction counterpart of storage_to_scalar (the store hook fired from rb_ca_obj2ptr): brings a surface value object into this Face's int64 storage (count in self's unit since the Unix epoch) so a scalar store round-trips with a fetch.
-
#strftime(fmt) ⇒ CAString
Returns a CAString whose elements are the per-cell
Time#strftime(fmt)result (UTC). -
#ticks ⇒ CArray
Sanctioned external accessor that pairs with the existing
unitreader. -
#timesteps(unit: self.unit, origin: nil) ⇒ CArray
Returns the integer timestep of every element: the k-th
unit-wide bucket counted fromorigin(floor toward the past, so pre-originelements get a negative index -- a normal value, not masked). -
#to_comparable(operand) ⇒ CATime
Brings
operandintoself's unit space for a direct storage comparison (comparison operators / search family / linear_section). -
#to_date ⇒ CArray
Converts every element to a Ruby
Date(UTC), returned as an object CArray. -
#to_datetime ⇒ CArray
Converts every element to a Ruby
DateTime(UTC offset 0), returned as an object CArray. -
#to_time ⇒ CArray
Converts every element to a Ruby
Time(UTC), returned as an object CArray. -
#to_unit(unit) ⇒ CATime
Returns the same instants re-expressed on a finer grid: a new CATime whose storage is
self's ticks widened intounit.
Class Method Details
.from_timesteps(k, unit:, origin: nil) ⇒ Element, CATime
1938 1939 1940 1941 1942 1943 1944 |
# File 'lib/carray/time.rb', line 1938 def self.from_timesteps(k, unit:, origin: nil) res = Resolution.parse(unit) kk = k.is_a?(CArray) ? k.int64 : CArray.int64(1) { Integer(k) } _mul, step_ticks, o = _resolve_grid(res, res, origin) # step_ticks = 1 (same res) raw = o + kk * step_ticks k.is_a?(CArray) ? raw.time(unit: res) : Element.new(raw[0], res) end |
.new(*shape, unit: :ns) ⇒ CATime
288 289 290 291 |
# File 'lib/carray/time.rb', line 288 def self.new(*shape, unit: :ns) raw = CArray.int64(*shape) wrap(raw, unit: unit) end |
.wrap(raw, unit: :ns) ⇒ CATime
299 300 301 |
# File 'lib/carray/time.rb', line 299 def self.wrap(raw, unit: :ns) __wrap__(raw, unit) end |
Instance Method Details
#+(other) ⇒ CATime
696 697 698 699 700 701 702 703 704 705 |
# File 'lib/carray/time.rb', line 696 def +(other) case other when CATimedelta (parent + CATimeUnitAlgebra.convert_scale_trunc(other.parent, other.unit, unit)).time(unit: unit) when CATime raise TypeError, "CATime + CATime is ill-defined" else raise TypeError, "CATime + #{other.class} is not allowed (use CATimedelta)" end end |
#-(other) ⇒ CATime, CATimedelta
715 716 717 718 719 720 721 722 723 724 725 726 727 |
# File 'lib/carray/time.rb', line 715 def -(other) case other when CATimedelta (parent - CATimeUnitAlgebra.convert_scale_trunc(other.parent, other.unit, unit)).time(unit: unit) when CATime u = CATimeUnitAlgebra.diff_unit(unit, other.unit) a = CATimeUnitAlgebra.convert_instant!(parent, unit, u) b = CATimeUnitAlgebra.convert_instant!(other.parent, other.unit, u) (a - b).timedelta(unit: u) else raise TypeError, "CATime - #{other.class} is not allowed" end end |
#ajd ⇒ CArray
1095 1096 1097 1098 |
# File 'lib/carray/time.rb', line 1095 def ajd require 'date' to_time.convert(:double) {|t| t.to_datetime.ajd.to_f} end |
#ceil(unit:, origin: nil) ⇒ CATime
1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 |
# File 'lib/carray/time.rb', line 1879 def ceil(unit:, origin: nil) g = _step_grid(unit, origin) return _civil(:ceil, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g _guard_overflow(su, mul, o, step_ticks, :ceil) n = _num_ticks(mul) d = n - o q = d / step_ticks q = q - (d - q * step_ticks).lt(0) fl = o + q * step_ticks _head_time(fl + step_ticks * n.ne(fl), mul, su) end |
#day ⇒ CArray
Day of the month, 1..31. Every cell is 1 for :Y / :M storage, which
does not resolve days.
1048 1049 1050 1051 1052 1053 |
# File 'lib/carray/time.rb', line 1048 def day case unit.base when :Y, :M then parent * 0 + 1 else self.class.send(:_civil_from_days, _field_days)[2] end end |
#floor(unit:, origin: nil) ⇒ CATime
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 |
# File 'lib/carray/time.rb', line 1864 def floor(unit:, origin: nil) g = _step_grid(unit, origin) return _civil(:floor, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g _guard_overflow(su, mul, o, step_ticks, :floor) d = _num_ticks(mul) - o q = d / step_ticks q = q - (d - q * step_ticks).lt(0) _head_time(o + q * step_ticks, mul, su) end |
#hour ⇒ CArray
Hour of the day, 0..23; 0 when the storage unit is coarser than an hour.
1057 |
# File 'lib/carray/time.rb', line 1057 def hour; _clock_field(:h); end |
#is_leap ⇒ CArray
1104 1105 1106 1107 |
# File 'lib/carray/time.rb', line 1104 def is_leap y = year ((y % 4).eq(0) & (y % 100).ne(0)) | (y % 400).eq(0) end |
#is_righttime(unit:, origin: nil) ⇒ CArray
1919 1920 1921 1922 1923 1924 1925 1926 1927 |
# File 'lib/carray/time.rb', line 1919 def is_righttime(unit:, origin: nil) g = _step_grid(unit, origin) return _civil(:on, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g _guard_overflow(su, mul, o) d = _num_ticks(mul) - o q = d / step_ticks (d - q * step_ticks).eq(0) end |
#jd ⇒ CArray
1086 1087 1088 1089 |
# File 'lib/carray/time.rb', line 1086 def jd require 'date' to_time.convert(:int) {|t| t.to_date.jd} end |
#linear_fetch(addr, axis: nil) ⇒ Element, CATime
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 |
# File 'lib/carray/time.rb', line 957 def linear_fetch (addr, **opts) r = parent.float64.linear_fetch(addr, **opts) case r when CArray # mask_invalid before the int64 cast: the cast turns a NaN into 0, which # would read as the epoch instead of "no answer". r.mask_invalid.round.int64.time(unit: unit) when Numeric # Scalar query. Out of range -> nil, joining the nil the kernel already # returns for a masked scalar query: one "no answer" on this path. r.to_f.nan? ? nil : Element.new(r.round, unit) else r # nil (masked scalar query) end end |
#mean(axis: nil, **opts) ⇒ Element, CATime
780 781 782 |
# File 'lib/carray/time.rb', line 780 def mean(*args, **opts) _reduce_dt(:mean, :time, args, opts) end |
#median(axis: nil, **opts) ⇒ Element, CATime
789 790 791 |
# File 'lib/carray/time.rb', line 789 def median(*args, **opts) _reduce_dt(:median, :time, args, opts) end |
#minmax(*axes, **opts) ⇒ Array(Element, Element), Array(CATime, CATime)
755 756 757 758 |
# File 'lib/carray/time.rb', line 755 def minmax(*args, **opts) lo, hi = parent.minmax(*args, **opts) [_lift_extremum(lo), _lift_extremum(hi)] end |
#minute ⇒ CArray
Minute of the hour, 0..59; 0 when the storage unit is coarser than a minute.
1060 |
# File 'lib/carray/time.rb', line 1060 def minute; _clock_field(:m); end |
#month ⇒ CArray
Calendar month, 1..12. Every cell is 1 for :Y storage, which does not
resolve months.
1035 1036 1037 1038 1039 1040 1041 1042 1043 |
# File 'lib/carray/time.rb', line 1035 def month case unit.base when :Y then parent * 0 + 1 when :M mo = parent * unit.count + 1970 * 12 mo - self.class.send(:_floordiv_i, mo, 12) * 12 + 1 else self.class.send(:_civil_from_days, _field_days)[1] end end |
#percentile(*p, axis: nil, **opts) ⇒ Element, ...
799 800 801 |
# File 'lib/carray/time.rb', line 799 def percentile(*args, **opts) _reduce_dt(:percentile, :time, args, opts) end |
#quantile(axis: nil, **opts) ⇒ Array<Element>, Array<CATime>
807 808 809 |
# File 'lib/carray/time.rb', line 807 def quantile(*args, **opts) _reduce_dt(:quantile, :time, args, opts) end |
#round(unit:, origin: nil) ⇒ CATime
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 |
# File 'lib/carray/time.rb', line 1899 def round(unit:, origin: nil) g = _step_grid(unit, origin) return _civil(:round, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g _guard_overflow(su, mul, o, step_ticks, :round) d = _num_ticks(mul) - o q = d / step_ticks q = q - (d - q * step_ticks).lt(0) # floor bucket r = d - q * step_ticks # 0 <= r < step_ticks q = q + r.ge((step_ticks + 1) / 2) # ties -> future; no 2*d / 2*r _head_time(o + q * step_ticks, mul, su) end |
#scalar_to_storage(surface) ⇒ Integer, Object
917 918 919 920 921 922 923 924 |
# File 'lib/carray/time.rb', line 917 def scalar_to_storage (surface) case surface when Integer, String surface else to_comparable(surface).parent[0] end end |
#second ⇒ CArray
Second of the minute, 0..59; 0 when the storage unit is coarser than a second.
1063 |
# File 'lib/carray/time.rb', line 1063 def second; _clock_field(:s); end |
#stddev(axis: nil, **opts) ⇒ CATimedelta::Element, CATimedelta
817 818 819 |
# File 'lib/carray/time.rb', line 817 def stddev(*args, **opts) _reduce_dt(:stddev, :timedelta, args, opts) end |
#stddevp(axis: nil, **opts) ⇒ CATimedelta::Element, CATimedelta
825 826 827 |
# File 'lib/carray/time.rb', line 825 def stddevp(*args, **opts) _reduce_dt(:stddevp, :timedelta, args, opts) end |
#strftime(fmt) ⇒ CAString
1114 1115 1116 |
# File 'lib/carray/time.rb', line 1114 def strftime(fmt) CAString.wrap(to_time.convert(:object) {|t| t.strftime(fmt)}) end |
#sum(*) ⇒ Object
832 833 834 |
# File 'lib/carray/time.rb', line 832 def sum(*) raise TypeError, "CATime#sum is ill-defined; use mean for centroid" end |
#ticks ⇒ CArray
Sanctioned external accessor that pairs with the existing unit reader.
An interop bridge (carray-xarray, carray-pycall consumers, etc.) can
read out the int64 count + unit without reaching into parent (= an
internal-contract accessor). The reference epoch is the implicit Unix
epoch (1970-01-01 UTC) baked into the convention at the top of this
file and into the C layer's Time.at-based decoding; it is not a
per-instance state and therefore is not exposed as an accessor.
ca.ticks # => CArray (int64), tick counts since 1970-01-01 UTC ca.unit # => Symbol (:Y :M :W :D :h :m :s :ms :us :ns ...)
663 664 665 |
# File 'lib/carray/time.rb', line 663 def ticks parent end |
#timesteps(unit: self.unit, origin: nil) ⇒ CArray
1848 1849 1850 1851 1852 1853 1854 1855 1856 |
# File 'lib/carray/time.rb', line 1848 def timesteps(unit: self.unit, origin: nil) g = _step_grid(unit, origin) return _civil(:index, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g _guard_overflow(su, mul, o) d = _num_ticks(mul) - o q = d / step_ticks q - (d - q * step_ticks).lt(0) # floor-div correction (%-independent) end |
#to_comparable(operand) ⇒ CATime
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 |
# File 'lib/carray/time.rb', line 881 def to_comparable (operand) case operand when CATime return operand if operand.unit == unit CATimeUnitAlgebra.convert_instant!(operand.parent, operand.unit, unit) .time(unit: unit) when CATime::Element lifted = CATime.wrap(CA_INT64([operand.value]), unit: operand.unit) to_comparable(lifted) when Time # Reuse the single-literal builder; it yields a length-1 CATime # in the requested (= self's) resolution. CArray.time(operand, unit: unit) when defined?(DateTime) && DateTime to_comparable(operand.to_time.utc) else raise TypeError, "CATime cannot reconcile #{operand.class} " \ "(use ca.parent to compare the raw int64 storage directly)" end end |
#to_date ⇒ CArray
1001 1002 1003 1004 1005 |
# File 'lib/carray/time.rb', line 1001 def to_date require 'date' # 2440588 = JD of 1970-01-01; proleptic Gregorian to match to_time and the field accessors. _field_days.convert(:object) {|d| Date.jd(2440588 + d, Date::GREGORIAN)} end |
#to_datetime ⇒ CArray
1011 1012 1013 1014 |
# File 'lib/carray/time.rb', line 1011 def to_datetime require 'date' to_time.convert(:object) {|t| t.to_datetime} end |
#to_time ⇒ CArray
986 987 988 989 990 991 992 993 994 |
# File 'lib/carray/time.rb', line 986 def to_time require 'time' if CATimeUnitAlgebra::FIXED.key?(unit.base) f = unit.tick_ratio # exact seconds / tick (Rational) parent.convert(:object) {|v| Time.at(v * f, in: 'UTC')} else # calendar: exact granule midnight (_field_days * 86400).convert(:object) {|v| Time.at(v, in: 'UTC')} end end |
#to_unit(unit) ⇒ CATime
681 682 683 684 685 |
# File 'lib/carray/time.rb', line 681 def to_unit(unit) to = CATime::Resolution.parse(unit) CATimeUnitAlgebra.widen(parent, CATimeUnitAlgebra.multiple_factor(self.unit, to)).time(unit: to) end |
#variance(*) ⇒ Object
841 |
# File 'lib/carray/time.rb', line 841 def variance(*); raise TypeError, "CATime#variance is ill-defined (squared-time units); use stddev"; end |
#variancep(*) ⇒ Object
846 |
# File 'lib/carray/time.rb', line 846 def variancep(*); raise TypeError, "CATime#variancep is ill-defined (squared-time units); use stddevp"; end |
#weekday ⇒ CArray
Day of the week, Sunday = 0 .. Saturday = 6.
1067 1068 1069 1070 1071 |
# File 'lib/carray/time.rb', line 1067 def weekday # 1970-01-01 is a Thursday (wday 4); Sun=0..Sat=6. n = _field_days + 4 n - self.class.send(:_floordiv_i, n, 7) * 7 # floor-mod 7 end |
#yday ⇒ CArray
Day of the year, 1..366.
1075 1076 1077 1078 1079 1080 |
# File 'lib/carray/time.rb', line 1075 def yday d = _field_days y = self.class.send(:_civil_from_days, d)[0] ones = CArray.int64(*shape) { 1 } d - self.class.send(:_days_from_civil, y, ones, ones) + 1 end |
#year ⇒ CArray
Each accessor returns an integer CArray with the requested calendar / clock field extracted from every element (UTC), computed by vectorized civil-date algebra directly on the int64 storage -- no per-cell Time. Exact for every unit, including :M / :Y (where the old Time.at path drifted by using 30.5-day / 365.25-day approximations). Fields finer than the storage unit collapse to their zero point, and the input mask propagates.
1024 1025 1026 1027 1028 1029 1030 |
# File 'lib/carray/time.rb', line 1024 def year case unit.base when :Y then parent * unit.count + 1970 when :M then self.class.send(:_floordiv_i, parent * unit.count + 1970 * 12, 12) else self.class.send(:_civil_from_days, _field_days)[0] end end |