Class: CATime
- Inherits:
-
Object
- Object
- CATime
- Defined in:
- lib/carray/time.rb,
lib/carray/time.rb,
lib/carray/time.rb
Overview
============================================================================
Ruby surface operators (CATime)
Defined Under Namespace
Classes: Element, Grid, Resolution
Constant Summary collapse
- SNAP_DIRECTIONS =
Rounding rules #snap accepts, the same three CArray#snap takes.
%i[round floor ceil].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, ...
Returns the percentile instants on
self's unit, in the shapes the plain CArray#percentile uses: onepreduces to a single value (an Element, or a CATime withaxis:), two or morepgive an Array of those. -
#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. -
.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(grid = nil, unit:, origin: nil) ⇒ CATime
#snap with
direction: :ceil-- each element at the bucket head at or after it (an element already on a boundary maps to itself). - #floor(grid = nil, unit:, origin: nil) ⇒ CATime
-
#grid ⇒ Grid
The grid this array is stored on: its own tick, anchored at the epoch.
-
#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(grid = nil, unit:, origin: nil) ⇒ CATime
#snap with
direction: :round-- each element at its nearest bucket head, ties toward the future. -
#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.
- #snap(grid = nil, unit: nil, origin: nil, direction: :round) ⇒ Object
-
#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 the
unitgrid: a new CATime whose storage resolution isunit.
Class Method Details
.from_timesteps(k, unit:, origin: nil) ⇒ Element, CATime
2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 |
# File 'lib/carray/time.rb', line 2518 def self.from_timesteps(k, grid = nil, unit: nil, origin: nil) grid = unit if unit.is_a?(Grid) # A Grid answers on its own storage, so an origin off the unit grid # ("12 hours since ... 09:00") survives; the keyword form keeps the # narrower rule it always had, where the origin sits on the unit itself. return grid.at(k) if grid.is_a?(Grid) unit, origin = Grid.resolve(grid, unit, origin) res = Resolution.parse(unit) out = res.base == :W ? Resolution.new(1, :D) : res kk = k.is_a?(CArray) ? k.int64 : CArray.int64(1) { Integer(k) } _mul, step_ticks, o = CATimeGrid.resolve_integer_grid(res, out, origin) raw = o + kk * step_ticks k.is_a?(CArray) ? raw.time(unit: out) : Element.new(raw[0], out) end |
.new(*shape, unit: :ns) ⇒ CATime
380 381 382 383 |
# File 'lib/carray/time.rb', line 380 def self.new(*shape, unit: :ns) raw = CArray.int64(*shape) wrap(raw, unit: unit) end |
.wrap(raw, unit: :ns) ⇒ CATime
391 392 393 |
# File 'lib/carray/time.rb', line 391 def self.wrap(raw, unit: :ns) __wrap__(raw, unit) end |
Instance Method Details
#+(other) ⇒ CATime
804 805 806 807 808 809 810 811 812 813 |
# File 'lib/carray/time.rb', line 804 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
823 824 825 826 827 828 829 830 831 832 833 834 835 |
# File 'lib/carray/time.rb', line 823 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
1203 1204 1205 1206 |
# File 'lib/carray/time.rb', line 1203 def ajd require 'date' to_time.convert(:double) {|t| t.to_datetime.ajd.to_f} end |
#ceil(grid = nil, unit:, origin: nil) ⇒ CATime
2461 2462 2463 |
# File 'lib/carray/time.rb', line 2461 def ceil(grid = nil, unit: nil, origin: nil) snap(grid, unit: unit, origin: origin, direction: :ceil) end |
#day ⇒ CArray
Day of the month, 1..31. Every cell is 1 for :Y / :M storage, which
does not resolve days.
1156 1157 1158 1159 1160 1161 |
# File 'lib/carray/time.rb', line 1156 def day case unit.base when :Y, :M then parent * 0 + 1 else CATimeCivil.civil_from_days(days_since_epoch)[2] end end |
#floor(grid = nil, unit:, origin: nil) ⇒ CATime
2453 2454 2455 |
# File 'lib/carray/time.rb', line 2453 def floor(grid = nil, unit: nil, origin: nil) snap(grid, unit: unit, origin: origin, direction: :floor) end |
#grid ⇒ Grid
The grid this array is stored on: its own tick, anchored at the epoch.
t.timesteps is t.timesteps(t.grid), which is what the unit: default
has always meant.
2359 |
# File 'lib/carray/time.rb', line 2359 def grid = Grid.new(unit) |
#hour ⇒ CArray
Hour of the day, 0..23; 0 when the storage unit is coarser than an hour.
1165 |
# File 'lib/carray/time.rb', line 1165 def hour; clock_field(:h); end |
#is_leap ⇒ CArray
1212 1213 1214 1215 |
# File 'lib/carray/time.rb', line 1212 def is_leap y = year ((y % 4).eq(0) & (y % 100).ne(0)) | (y % 400).eq(0) end |
#is_righttime(unit:, origin: nil) ⇒ CArray
2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 |
# File 'lib/carray/time.rb', line 2487 def is_righttime(grid = nil, unit: nil, origin: nil) unit, origin = Grid.resolve(grid, unit, origin) g = resolve_timestep_grid(unit, origin) return calendar_bucket(:on, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g raise_if_int64_overflow(su, mul, o) d = storage_ticks_in_numerator_grid(mul) - o q = d / step_ticks (d - q * step_ticks).eq(0) end |
#jd ⇒ CArray
1194 1195 1196 1197 |
# File 'lib/carray/time.rb', line 1194 def jd require 'date' to_time.convert(:int) {|t| t.to_date.jd} end |
#linear_fetch(addr, axis: nil) ⇒ Element, CATime
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
# File 'lib/carray/time.rb', line 1065 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
888 889 890 |
# File 'lib/carray/time.rb', line 888 def mean(*args, **opts) reduce_on_own_grid(:mean, :time, args, opts) end |
#median(axis: nil, **opts) ⇒ Element, CATime
897 898 899 |
# File 'lib/carray/time.rb', line 897 def median(*args, **opts) reduce_on_own_grid(:median, :time, args, opts) end |
#minmax(*axes, **opts) ⇒ Array(Element, Element), Array(CATime, CATime)
863 864 865 866 |
# File 'lib/carray/time.rb', line 863 def minmax(*args, **opts) lo, hi = parent.minmax(*args, **opts) [relift_extremum(lo), relift_extremum(hi)] end |
#minute ⇒ CArray
Minute of the hour, 0..59; 0 when the storage unit is coarser than a minute.
1168 |
# File 'lib/carray/time.rb', line 1168 def minute; clock_field(:m); end |
#month ⇒ CArray
Calendar month, 1..12. Every cell is 1 for :Y storage, which does not
resolve months.
1143 1144 1145 1146 1147 1148 1149 1150 1151 |
# File 'lib/carray/time.rb', line 1143 def month case unit.base when :Y then parent * 0 + 1 when :M mo = parent * unit.count + 1970 * 12 mo % 12 + 1 else CATimeCivil.civil_from_days(days_since_epoch)[1] end end |
#percentile(*p, axis: nil, **opts) ⇒ Element, ...
907 908 909 |
# File 'lib/carray/time.rb', line 907 def percentile(*args, **opts) reduce_on_own_grid(:percentile, :time, args, opts) end |
#quantile(axis: nil, **opts) ⇒ Array<Element>, Array<CATime>
915 916 917 |
# File 'lib/carray/time.rb', line 915 def quantile(*args, **opts) reduce_on_own_grid(:quantile, :time, args, opts) end |
#round(grid = nil, unit:, origin: nil) ⇒ CATime
2471 2472 2473 |
# File 'lib/carray/time.rb', line 2471 def round(grid = nil, unit: nil, origin: nil) snap(grid, unit: unit, origin: origin, direction: :round) end |
#scalar_to_storage(surface) ⇒ Integer, Object
1025 1026 1027 1028 1029 1030 1031 1032 |
# File 'lib/carray/time.rb', line 1025 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.
1171 |
# File 'lib/carray/time.rb', line 1171 def second; clock_field(:s); end |
#floor(unit:, origin: nil) ⇒ CATime #snap(grid = nil, unit:, origin: nil, direction: :round) ⇒ CATime
2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 |
# File 'lib/carray/time.rb', line 2423 def snap(grid = nil, unit: nil, origin: nil, direction: :round) unless SNAP_DIRECTIONS.include?(direction) raise ArgumentError, "snap: direction must be :round / :floor / :ceil " \ "(got #{direction.inspect})" end unit, origin = Grid.resolve(grid, unit, origin) g = resolve_timestep_grid(unit, origin) return calendar_bucket(direction, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g raise_if_int64_overflow(su, mul, o, step_ticks, direction) n = storage_ticks_in_numerator_grid(mul) d = n - o q = d / step_ticks q = q - (d - q * step_ticks).lt(0) # floor bucket case direction when :ceil fl = o + q * step_ticks return bucket_head_as_time(fl + step_ticks * n.ne(fl), mul, su) when :round r = d - q * step_ticks # 0 <= r < step_ticks q = q + r.ge((step_ticks + 1) / 2) # ties -> future; no 2*d / 2*r end bucket_head_as_time(o + q * step_ticks, mul, su) end |
#stddev(axis: nil, **opts) ⇒ CATimedelta::Element, CATimedelta
925 926 927 |
# File 'lib/carray/time.rb', line 925 def stddev(*args, **opts) reduce_on_own_grid(:stddev, :timedelta, args, opts) end |
#stddevp(axis: nil, **opts) ⇒ CATimedelta::Element, CATimedelta
933 934 935 |
# File 'lib/carray/time.rb', line 933 def stddevp(*args, **opts) reduce_on_own_grid(:stddevp, :timedelta, args, opts) end |
#strftime(fmt) ⇒ CAString
1222 1223 1224 |
# File 'lib/carray/time.rb', line 1222 def strftime(fmt) CAString.wrap(to_time.convert(:object) {|t| t.strftime(fmt)}) end |
#sum(*) ⇒ Object
940 941 942 |
# File 'lib/carray/time.rb', line 940 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 ...)
761 762 763 |
# File 'lib/carray/time.rb', line 761 def ticks parent end |
#timesteps(unit: self.unit, origin: nil) ⇒ CArray
2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 |
# File 'lib/carray/time.rb', line 2380 def timesteps(grid = nil, unit: nil, origin: nil) unit, origin = Grid.resolve(grid, unit, origin) unit = self.unit if unit.nil? g = resolve_timestep_grid(unit, origin) return calendar_bucket(:index, g[1], origin) if g[0] == :civil su, mul, step_ticks, o = g raise_if_int64_overflow(su, mul, o) d = storage_ticks_in_numerator_grid(mul) - o q = d / step_ticks q - (d - q * step_ticks).lt(0) # floor-div correction (%-independent) end |
#to_comparable(operand) ⇒ CATime
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 |
# File 'lib/carray/time.rb', line 989 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
1109 1110 1111 1112 1113 |
# File 'lib/carray/time.rb', line 1109 def to_date require 'date' # 2440588 = JD of 1970-01-01; proleptic Gregorian to match to_time and the field accessors. days_since_epoch.convert(:object) {|d| Date.jd(2440588 + d, Date::GREGORIAN)} end |
#to_datetime ⇒ CArray
1119 1120 1121 1122 |
# File 'lib/carray/time.rb', line 1119 def to_datetime require 'date' to_time.convert(:object) {|t| t.to_datetime} end |
#to_time ⇒ CArray
1094 1095 1096 1097 1098 1099 1100 1101 1102 |
# File 'lib/carray/time.rb', line 1094 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 (days_since_epoch * 86400).convert(:object) {|v| Time.at(v, in: 'UTC')} end end |
#to_unit(unit) ⇒ CATime
790 791 792 793 |
# File 'lib/carray/time.rb', line 790 def to_unit(unit) to = CATime::Resolution.parse(unit) CATimeUnitAlgebra.convert_instant_floor(parent, self.unit, to).time(unit: to) end |
#variance(*) ⇒ Object
949 |
# File 'lib/carray/time.rb', line 949 def variance(*); raise TypeError, "CATime#variance is ill-defined (squared-time units); use stddev"; end |
#variancep(*) ⇒ Object
954 |
# File 'lib/carray/time.rb', line 954 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.
1175 1176 1177 1178 1179 |
# File 'lib/carray/time.rb', line 1175 def weekday # 1970-01-01 is a Thursday (wday 4); Sun=0..Sat=6. n = days_since_epoch + 4 n % 7 end |
#yday ⇒ CArray
Day of the year, 1..366.
1183 1184 1185 1186 1187 1188 |
# File 'lib/carray/time.rb', line 1183 def yday d = days_since_epoch y = CATimeCivil.civil_from_days(d)[0] ones = CArray.int64(*shape) { 1 } d - CATimeCivil.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.
1132 1133 1134 1135 1136 1137 1138 |
# File 'lib/carray/time.rb', line 1132 def year case unit.base when :Y then parent * unit.count + 1970 when :M then (parent * unit.count + 1970 * 12) / 12 else CATimeCivil.civil_from_days(days_since_epoch)[0] end end |