carray 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +304 -0
- data/README.md +3 -4
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +225 -0
- data/ext/ca_axis_group.c +177 -99
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +66 -71
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +663 -214
- data/ext/ca_kernel_iterator.h +231 -167
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +132 -22
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +1 -0
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +96 -25
- data/ext/ca_obj_face.c +406 -64
- data/ext/ca_obj_face.h +92 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +21 -21
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +189 -107
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +543 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +47 -49
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -118
- data/ext/ca_obj_stride.c +195 -34
- data/ext/ca_obj_string.c +17 -18
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +149 -27
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +102 -79
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +463 -0
- data/ext/carray.h +70 -27
- data/ext/carray_access.c +455 -211
- data/ext/carray_address_basis.c +177 -36
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +46 -14
- data/ext/carray_call_cfunc.c +0 -28
- data/ext/carray_cast.c +1444 -209
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +32 -11
- data/ext/carray_core.c +514 -122
- data/ext/carray_count.c +140 -87
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +569 -277
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +385 -3
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +682 -316
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +221 -76
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1089 -359
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +273 -239
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +170 -70
- data/ext/carray_sort.c +215 -218
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +0 -2
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1076 -846
- data/ext/ruby_carray.c +7 -1
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +19 -0
- data/lib/carray/autoload_method_extension.rb +6 -0
- data/lib/carray/axis_group.rb +174 -26
- data/lib/carray/basics.rb +91 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +118 -48
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +94 -26
- data/lib/carray/categorical_iterator.rb +333 -826
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +46 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +589 -82
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +124 -25
- data/lib/carray/frame/group.rb +189 -63
- data/lib/carray/frame/io.rb +412 -72
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +37 -16
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +715 -48
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +29 -11
- data/lib/carray/iterator.rb +14 -2
- data/lib/carray/lazy.rb +54 -30
- data/lib/carray/mask_gap_fill.rb +66 -52
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +117 -4
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +177 -36
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +51 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +4 -3
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +517 -103
- data/lib/carray/window_iterator.rb +127 -72
- data/lib/carray.rb +2 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +1 -2
- data/yard-stubs/carray_access.rb +5 -4
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +25 -13
- data/yard-stubs/carray_median_percentile.rb +10 -6
- data/yard-stubs/carray_order.rb +34 -13
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +2 -1
- data/yard-stubs/carray_sort.rb +43 -3
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +11 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -26005
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14862
- data/ext/carray_kernels_reduce_extreme.c +0 -17494
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32218
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
data/lib/carray/time.rb
CHANGED
|
@@ -93,6 +93,19 @@ module CATimeUnitAlgebra
|
|
|
93
93
|
|
|
94
94
|
module_function
|
|
95
95
|
|
|
96
|
+
# An operand of a time or duration operator, taken through its to_ca when
|
|
97
|
+
# it exports MemoryView and defines one (an Apache Arrow duration array, given
|
|
98
|
+
# a bridge that adds to_ca), as the core binary operators take it; other
|
|
99
|
+
# objects are returned as they are, for the operator to accept or refuse.
|
|
100
|
+
def operand(other)
|
|
101
|
+
if !other.is_a?(CArray) && other.respond_to?(:to_ca) &&
|
|
102
|
+
CArray.memory_view_available?(other)
|
|
103
|
+
other.to_ca
|
|
104
|
+
else
|
|
105
|
+
other
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
96
109
|
# seconds- (fixed) or months- (calendar) per base tick.
|
|
97
110
|
def base_ratio(base)
|
|
98
111
|
FIXED[base] || CALENDAR[base]
|
|
@@ -208,7 +221,7 @@ module CATimeUnitAlgebra
|
|
|
208
221
|
# fine->coarse divides only when every value is exact; cross-group ALWAYS
|
|
209
222
|
# raises -- a :M / :Y duration has no fixed ratio to days (a month is
|
|
210
223
|
# calendar-variable), so it genuinely cannot scale to seconds.
|
|
211
|
-
def convert_scale!(storage, from, to)
|
|
224
|
+
def convert_scale!(storage, from, to, instant: false)
|
|
212
225
|
a = res(from); b = res(to)
|
|
213
226
|
return storage if a == b
|
|
214
227
|
r = ratio(a, b)
|
|
@@ -221,6 +234,11 @@ module CATimeUnitAlgebra
|
|
|
221
234
|
scaled = widen(storage, r.numerator) # coarse -> fine: lossless multiply
|
|
222
235
|
return scaled if r.denominator == 1
|
|
223
236
|
unless (scaled % r.denominator).eq(0).all
|
|
237
|
+
if instant
|
|
238
|
+
raise ArgumentError,
|
|
239
|
+
"cannot convert times in #{a} to #{b} without loss: " \
|
|
240
|
+
"some are not on the #{b} grid (they would be moved to it)"
|
|
241
|
+
end
|
|
224
242
|
raise ArgumentError,
|
|
225
243
|
"cannot scale duration #{a} to #{b} without loss: " \
|
|
226
244
|
"some values are not a whole multiple of #{b} " \
|
|
@@ -271,7 +289,7 @@ module CATimeUnitAlgebra
|
|
|
271
289
|
def convert_instant!(storage, from, to)
|
|
272
290
|
a = res(from); b = res(to)
|
|
273
291
|
return storage if a == b
|
|
274
|
-
return convert_scale!(storage, a, b) if ratio(a, b) # same group
|
|
292
|
+
return convert_scale!(storage, a, b, instant: true) if ratio(a, b) # same group
|
|
275
293
|
if CALENDAR.key?(a.base)
|
|
276
294
|
convert_instant_calendar_to_fixed(storage, a, b) # widen
|
|
277
295
|
else
|
|
@@ -385,7 +403,48 @@ module CATimeUnitAlgebra
|
|
|
385
403
|
end
|
|
386
404
|
end
|
|
387
405
|
|
|
406
|
+
# A reduction of a time Face takes `fill_value:` as a value of the Face (an
|
|
407
|
+
# Element, or what an assignment to the Face accepts), the same as
|
|
408
|
+
# `strip_mask` and `[]=` read it. The reduction runs on the storage, so the
|
|
409
|
+
# fill goes in after the result is a Face again: a whole reduction keeps its
|
|
410
|
+
# axes for that, so the fill lands in a typed cell, and is then taken out.
|
|
411
|
+
module CATimeSurfaceFill
|
|
412
|
+
|
|
413
|
+
private
|
|
414
|
+
|
|
415
|
+
def with_surface_fill (opts)
|
|
416
|
+
fill = opts[:fill_value]
|
|
417
|
+
return yield(opts) if fill.nil? || fill.equal?(UNDEF)
|
|
418
|
+
o = opts.reject { |k, _| k == :fill_value }
|
|
419
|
+
o[:keep_axis] = true
|
|
420
|
+
r = yield(o)
|
|
421
|
+
kept = nil
|
|
422
|
+
unless opts[:keep_axis]
|
|
423
|
+
axes = opts[:axis].nil? ? (0...ndim).to_a :
|
|
424
|
+
Array(opts[:axis]).map { |a| a < 0 ? a + ndim : a }
|
|
425
|
+
kept = shape.each_with_index.reject { |_, i| axes.include?(i) }.map(&:first)
|
|
426
|
+
end
|
|
427
|
+
surface_fill(r, fill, kept)
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# `kept` is the shape to return to, or nil to keep the reduced axes.
|
|
431
|
+
def surface_fill (r, fill, kept)
|
|
432
|
+
case r
|
|
433
|
+
when Array then r.map { |x| surface_fill(x, fill, kept) }
|
|
434
|
+
when CArray
|
|
435
|
+
r = r.strip_mask(fill) if r.has_mask?
|
|
436
|
+
if kept.nil? then r
|
|
437
|
+
elsif kept.empty? then r[*Array.new(r.ndim, 0)]
|
|
438
|
+
else r.reshape(*kept)
|
|
439
|
+
end
|
|
440
|
+
else r
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
end
|
|
445
|
+
|
|
388
446
|
class CATime
|
|
447
|
+
include CATimeSurfaceFill
|
|
389
448
|
# @overload new(*shape, unit: :ns)
|
|
390
449
|
# Allocates a new int64 storage CArray of the given `shape` and
|
|
391
450
|
# wraps it as a CATime Face with the given `unit`. The
|
|
@@ -472,7 +531,7 @@ class CATime
|
|
|
472
531
|
# @return [String]
|
|
473
532
|
def to_s
|
|
474
533
|
case unit.base
|
|
475
|
-
when :Y then
|
|
534
|
+
when :Y then to_date.strftime("%Y")
|
|
476
535
|
when :M then to_date.strftime("%Y-%m")
|
|
477
536
|
when :W, :D then to_date.strftime("%Y-%m-%d")
|
|
478
537
|
else to_time.iso8601(fractional_second_digits) # :h .. :as (time shown)
|
|
@@ -602,6 +661,7 @@ class CATime
|
|
|
602
661
|
end
|
|
603
662
|
|
|
604
663
|
class CATimedelta
|
|
664
|
+
include CATimeSurfaceFill
|
|
605
665
|
# @overload new(*shape, unit: :ns)
|
|
606
666
|
# Allocates a new int64 storage CArray of the given `shape` and
|
|
607
667
|
# wraps it as a CATimedelta Face with the given `unit`.
|
|
@@ -816,16 +876,19 @@ class CATime
|
|
|
816
876
|
# cross-group calendar duration raises). Adding two datetimes is
|
|
817
877
|
# ill-defined.
|
|
818
878
|
# @param other [CATimedelta]
|
|
879
|
+
# (or a MemoryView producer whose +to_ca+ gives one, such as an Arrow
|
|
880
|
+
# duration array)
|
|
819
881
|
# @return [CATime]
|
|
820
882
|
# @raise [TypeError] on a non-timedelta operand.
|
|
821
883
|
def +(other)
|
|
884
|
+
given, other = other, CATimeUnitAlgebra.operand(other)
|
|
822
885
|
case other
|
|
823
886
|
when CATimedelta
|
|
824
887
|
(parent + CATimeUnitAlgebra.convert_scale_trunc(other.parent, other.unit, unit)).time(unit: unit)
|
|
825
888
|
when CATime
|
|
826
889
|
raise TypeError, "CATime + CATime is ill-defined"
|
|
827
890
|
else
|
|
828
|
-
raise TypeError, "CATime + #{
|
|
891
|
+
raise TypeError, "CATime + #{given.class} is not allowed (use CATimedelta)"
|
|
829
892
|
end
|
|
830
893
|
end
|
|
831
894
|
|
|
@@ -835,9 +898,12 @@ class CATime
|
|
|
835
898
|
# another {CATime} yields a {CATimedelta} at the finer of the two
|
|
836
899
|
# units (cross-group falls to the fixed unit).
|
|
837
900
|
# @param other [CATimedelta, CATime]
|
|
901
|
+
# (or a MemoryView producer whose +to_ca+ gives one, such as an Arrow
|
|
902
|
+
# duration or timestamp array)
|
|
838
903
|
# @return [CATime, CATimedelta]
|
|
839
904
|
# @raise [TypeError] on an unsupported operand.
|
|
840
905
|
def -(other)
|
|
906
|
+
given, other = other, CATimeUnitAlgebra.operand(other)
|
|
841
907
|
case other
|
|
842
908
|
when CATimedelta
|
|
843
909
|
(parent - CATimeUnitAlgebra.convert_scale_trunc(other.parent, other.unit, unit)).time(unit: unit)
|
|
@@ -847,7 +913,7 @@ class CATime
|
|
|
847
913
|
b = CATimeUnitAlgebra.convert_instant!(other.parent, other.unit, u)
|
|
848
914
|
(a - b).timedelta(unit: u)
|
|
849
915
|
else
|
|
850
|
-
raise TypeError, "CATime - #{
|
|
916
|
+
raise TypeError, "CATime - #{given.class} is not allowed"
|
|
851
917
|
end
|
|
852
918
|
end
|
|
853
919
|
|
|
@@ -858,38 +924,19 @@ class CATime
|
|
|
858
924
|
# base CArray#<=> composed from the (gated) > and <.
|
|
859
925
|
|
|
860
926
|
# Element decode convention via the C-layer macro
|
|
861
|
-
# `
|
|
927
|
+
# `CA_FACE_STORAGE_TO_ELEMENT_IF_FACE`: no Ruby `[]` override is needed, a
|
|
862
928
|
# scalar auto-decodes on all scalar-return paths such as `ca[i,j,k]` /
|
|
863
|
-
# `ca[k]`.
|
|
929
|
+
# `ca[k]`. storage_to_element itself lives in C (ext/ca_obj_datetime.c)
|
|
864
930
|
# so the hot path stays off Ruby method dispatch. Its write counterpart
|
|
865
|
-
# is
|
|
931
|
+
# is element_to_storage (below, surface -> storage on store).
|
|
866
932
|
|
|
867
933
|
# @!group Reductions
|
|
868
934
|
|
|
869
|
-
# min / max are order-structure reductions: they ride the core
|
|
870
|
-
# Face gate (ORDERABLE storage descent + output re-lift, see
|
|
871
|
-
# ext/mkkernel.rb face_gate: :relift), so
|
|
872
|
-
#
|
|
873
|
-
#
|
|
874
|
-
# re-lift both extremes here. They are actual elements, so they keep the
|
|
875
|
-
# storage unit (no rounding, unlike mean).
|
|
876
|
-
|
|
877
|
-
# @overload minmax(*axes, **opts)
|
|
878
|
-
# Returns the earliest and latest time as a `[min, max]` pair (§8).
|
|
879
|
-
# @return [Array(Element, Element), Array(CATime, CATime)]
|
|
880
|
-
def minmax(*args, **opts)
|
|
881
|
-
lo, hi = parent.minmax(*args, **opts)
|
|
882
|
-
[relift_extremum(lo), relift_extremum(hi)]
|
|
883
|
-
end
|
|
884
|
-
|
|
885
|
-
def relift_extremum(r)
|
|
886
|
-
case r
|
|
887
|
-
when Integer then Element.new(r, unit)
|
|
888
|
-
when CArray then r.time(unit: unit)
|
|
889
|
-
else r
|
|
890
|
-
end
|
|
891
|
-
end
|
|
892
|
-
private :relift_extremum
|
|
935
|
+
# min / max / minmax are order-structure reductions: they ride the core
|
|
936
|
+
# reduce Face gate (ORDERABLE storage descent + output re-lift, see
|
|
937
|
+
# ext/mkkernel.rb face_gate: :relift), so the inherited methods return the
|
|
938
|
+
# Element / CATime shapes directly. The extremes are actual elements, so
|
|
939
|
+
# they keep the storage unit (no rounding, unlike mean).
|
|
893
940
|
|
|
894
941
|
# The centroid / spread / order reductions report on the array's own grid:
|
|
895
942
|
# they run on the storage ticks and round the result back to the nearest
|
|
@@ -953,22 +1000,43 @@ class CATime
|
|
|
953
1000
|
|
|
954
1001
|
# @overload sum(*)
|
|
955
1002
|
# Not supported; use {#mean} for a centroid.
|
|
956
|
-
# @raise [
|
|
1003
|
+
# @raise [CArray::DataTypeError] always.
|
|
957
1004
|
def sum(*)
|
|
958
|
-
raise
|
|
1005
|
+
raise CArray::DataTypeError, "CATime#sum is ill-defined; use mean for centroid"
|
|
959
1006
|
end
|
|
960
1007
|
|
|
1008
|
+
# @overload accumulate(*)
|
|
1009
|
+
# Not supported, for the same reason as {#sum}.
|
|
1010
|
+
# @raise [CArray::DataTypeError] always.
|
|
1011
|
+
def accumulate(*); raise CArray::DataTypeError, "CATime#accumulate is ill-defined (a sum of instants)"; end
|
|
1012
|
+
|
|
1013
|
+
# @overload cumsum(*)
|
|
1014
|
+
# Not supported, for the same reason as {#sum}. The running position
|
|
1015
|
+
# relative to the first instant is `(t - t[0])` cumulated as durations.
|
|
1016
|
+
# @raise [CArray::DataTypeError] always.
|
|
1017
|
+
def cumsum(*); raise CArray::DataTypeError, "CATime#cumsum is ill-defined (a sum of instants)"; end
|
|
1018
|
+
|
|
1019
|
+
# @overload prod(*)
|
|
1020
|
+
# Not supported: a product of instants has no meaning.
|
|
1021
|
+
# @raise [CArray::DataTypeError] always.
|
|
1022
|
+
def prod(*); raise CArray::DataTypeError, "CATime#prod is ill-defined (a product of instants)"; end
|
|
1023
|
+
|
|
1024
|
+
# @overload cumprod(*)
|
|
1025
|
+
# Not supported, for the same reason as {#prod}.
|
|
1026
|
+
# @raise [CArray::DataTypeError] always.
|
|
1027
|
+
def cumprod(*); raise CArray::DataTypeError, "CATime#cumprod is ill-defined (a product of instants)"; end
|
|
1028
|
+
|
|
961
1029
|
# @overload variance(*)
|
|
962
1030
|
# Not supported: the variance of instants has squared-time units, which
|
|
963
1031
|
# no type represents (ill-defined, like {#sum}). Use {#stddev} for the
|
|
964
1032
|
# spread as a duration.
|
|
965
|
-
# @raise [
|
|
966
|
-
def variance(*); raise
|
|
1033
|
+
# @raise [CArray::DataTypeError] always.
|
|
1034
|
+
def variance(*); raise CArray::DataTypeError, "CATime#variance is ill-defined (squared-time units); use stddev"; end
|
|
967
1035
|
|
|
968
1036
|
# @overload variancep(*)
|
|
969
1037
|
# Not supported, for the same reason as {#variance}. Use {#stddevp}.
|
|
970
|
-
# @raise [
|
|
971
|
-
def variancep(*); raise
|
|
1038
|
+
# @raise [CArray::DataTypeError] always.
|
|
1039
|
+
def variancep(*); raise CArray::DataTypeError, "CATime#variancep is ill-defined (squared-time units); use stddevp"; end
|
|
972
1040
|
# @!endgroup
|
|
973
1041
|
|
|
974
1042
|
# sort / partition ride the core sort Face gate (ORDERABLE storage
|
|
@@ -986,11 +1054,15 @@ class CATime
|
|
|
986
1054
|
# different unit or shape), so the gate routes the operand through here.
|
|
987
1055
|
#
|
|
988
1056
|
# Accepted operands: another {CATime} (unit-rescaled to self),
|
|
989
|
-
# a {Element} (lifted to a length-1 CATime),
|
|
990
|
-
# Ruby `
|
|
991
|
-
#
|
|
992
|
-
#
|
|
993
|
-
#
|
|
1057
|
+
# a {Element} (lifted to a length-1 CATime), and an instant written
|
|
1058
|
+
# outside CArray -- a Ruby `Time`, `Date`, `DateTime`, or a `String` in
|
|
1059
|
+
# the grammar {CArray.time} reads (UTC unless it carries an offset).
|
|
1060
|
+
# Those are read at the precision they carry, not at self's unit, and
|
|
1061
|
+
# then go through the same conversion as a CATime: "2024-01-02 09:00"
|
|
1062
|
+
# against a day-unit array raises rather than matching 2024-01-02. A
|
|
1063
|
+
# bare Integer / other type raises; descend to `ca.parent` to compare
|
|
1064
|
+
# the hidden storage directly. An Array of any of these is a 1-D set
|
|
1065
|
+
# of instants (the value set of {#is_in}).
|
|
994
1066
|
#
|
|
995
1067
|
# The rescale is an INSTANT conversion (convert_instant!), lossless: a
|
|
996
1068
|
# coarser->finer unit always converts; a finer->coarser unit converts only
|
|
@@ -1000,10 +1072,24 @@ class CATime
|
|
|
1000
1072
|
# :D and finer, and a fixed operand coarsens to :M/:Y only when it sits on
|
|
1001
1073
|
# the calendar boundary (:W is the one exception -- month/year starts are
|
|
1002
1074
|
# not week-aligned, so :Y/:M <-> :W raises).
|
|
1003
|
-
# @param operand [CATime, CATime::Element, Time, DateTime]
|
|
1075
|
+
# @param operand [CATime, CATime::Element, Time, Date, DateTime, String, Array]
|
|
1004
1076
|
# @return [CATime] in self's unit.
|
|
1005
1077
|
# @raise [TypeError, ArgumentError] on an unreconcilable operand / unit.
|
|
1078
|
+
# Returns `[self, other]` in one unit that holds every value of both
|
|
1079
|
+
# exactly: the finer grid of two same-group units, or the fixed-length one
|
|
1080
|
+
# of a calendar / fixed pair (a month begins on a day). A lookup between
|
|
1081
|
+
# two arrays of different units (is_in, the set operations, locate_addr and
|
|
1082
|
+
# the joins on them) meets there, so it does not depend on which array is
|
|
1083
|
+
# the receiver.
|
|
1084
|
+
# @api private
|
|
1085
|
+
def in_common_unit_with (other)
|
|
1086
|
+
return [self, other] if other.unit == unit
|
|
1087
|
+
u = CATimeUnitAlgebra.diff_unit(unit, other.unit)
|
|
1088
|
+
[to_unit(u), other.to_unit(u)]
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1006
1091
|
def to_comparable (operand)
|
|
1092
|
+
require 'date' # Date is one of the operand kinds below
|
|
1007
1093
|
case operand
|
|
1008
1094
|
when CATime
|
|
1009
1095
|
return operand if operand.unit == unit
|
|
@@ -1012,12 +1098,19 @@ class CATime
|
|
|
1012
1098
|
when CATime::Element
|
|
1013
1099
|
lifted = CATime.wrap(CA_INT64([operand.value]), unit: operand.unit)
|
|
1014
1100
|
to_comparable(lifted)
|
|
1015
|
-
when Time
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1101
|
+
when Time, String, Date
|
|
1102
|
+
to_comparable(CATimeLiteral.exact_instant(operand))
|
|
1103
|
+
when Array
|
|
1104
|
+
# A set of instants written one by one (is_in(["2024-01-02", ...])).
|
|
1105
|
+
CATime.wrap(CA_INT64(operand.map { |v| to_comparable(v).parent[0] }),
|
|
1106
|
+
unit: unit)
|
|
1107
|
+
when CArray
|
|
1108
|
+
# An array that is not a time array: each value read as an instant (an
|
|
1109
|
+
# object column of Time values, a string column), a masked cell kept
|
|
1110
|
+
# masked.
|
|
1111
|
+
require "carray/methods/is_in"
|
|
1112
|
+
send(:values_in_face, self, operand) ||
|
|
1113
|
+
CArray.int64(*operand.shape).fill(UNDEF).time(unit: unit)
|
|
1021
1114
|
else
|
|
1022
1115
|
raise TypeError,
|
|
1023
1116
|
"CATime cannot reconcile #{operand.class} " \
|
|
@@ -1025,23 +1118,22 @@ class CATime
|
|
|
1025
1118
|
end
|
|
1026
1119
|
end
|
|
1027
1120
|
|
|
1028
|
-
# @overload
|
|
1029
|
-
# Write-direction counterpart of
|
|
1121
|
+
# @overload element_to_storage(surface)
|
|
1122
|
+
# Write-direction counterpart of storage_to_element (the store hook fired
|
|
1030
1123
|
# from rb_ca_obj2ptr): brings a surface value object into this Face's
|
|
1031
1124
|
# int64 storage (count in self's unit since the Unix epoch) so a scalar
|
|
1032
|
-
# store round-trips with a fetch. A {Element} / `Time` / `
|
|
1033
|
-
# reconciled to self's unit via {#to_comparable}
|
|
1034
|
-
# discipline: a cross-group unit or a non-exact
|
|
1035
|
-
# raises). A bare Integer (the documented `.parent`
|
|
1036
|
-
#
|
|
1037
|
-
#
|
|
1038
|
-
# @param surface [Element, Time, DateTime, Integer, String]
|
|
1125
|
+
# store round-trips with a fetch. A {Element} / `Time` / `Date` /
|
|
1126
|
+
# `DateTime` / `String` is reconciled to self's unit via {#to_comparable}
|
|
1127
|
+
# (same lossless discipline: a cross-group unit or a non-exact
|
|
1128
|
+
# finer->coarser cast raises). A bare Integer (the documented `.parent`
|
|
1129
|
+
# raw-storage escape) passes through unchanged to the storage cast.
|
|
1130
|
+
# @param surface [Element, Time, Date, DateTime, Integer, String]
|
|
1039
1131
|
# @return [Integer, Object] the storage-domain value, or `surface`
|
|
1040
1132
|
# unchanged for a pass-through type.
|
|
1041
1133
|
# @raise [TypeError, ArgumentError] on an unreconcilable surface / unit.
|
|
1042
|
-
def
|
|
1134
|
+
def element_to_storage (surface)
|
|
1043
1135
|
case surface
|
|
1044
|
-
when Integer
|
|
1136
|
+
when Integer
|
|
1045
1137
|
surface
|
|
1046
1138
|
else
|
|
1047
1139
|
to_comparable(surface).parent[0]
|
|
@@ -1248,7 +1340,9 @@ class CATime
|
|
|
1248
1340
|
# the centroid of month numbers (a caller wanting the day-space answer
|
|
1249
1341
|
# writes to_unit(:D) first) -- and the result is rounded back to a tick.
|
|
1250
1342
|
def reduce_on_own_grid(op, kind, args, opts)
|
|
1251
|
-
|
|
1343
|
+
with_surface_fill(opts) do |o|
|
|
1344
|
+
round_and_relift(parent.public_send(op, *args, **o), kind)
|
|
1345
|
+
end
|
|
1252
1346
|
end
|
|
1253
1347
|
|
|
1254
1348
|
# Round-to-nearest onto the storage grid, then wear the Face again. An
|
|
@@ -1273,6 +1367,41 @@ class CATime
|
|
|
1273
1367
|
end
|
|
1274
1368
|
end
|
|
1275
1369
|
|
|
1370
|
+
# inspect's name for the array: the class and the tick, which the storage
|
|
1371
|
+
# type name (fixlen[8]) does not tell.
|
|
1372
|
+
private def inspect_label
|
|
1373
|
+
"#{self.class}[#{unit}]"
|
|
1374
|
+
end
|
|
1375
|
+
|
|
1376
|
+
# inspect's cell: the instant as Element#to_s writes it, but with the
|
|
1377
|
+
# digits of a second that some cell of the array needs, the same in every
|
|
1378
|
+
# cell so that the cells line up; the unit is in the label.
|
|
1379
|
+
private def inspect_cell_formatter
|
|
1380
|
+
digits = inspect_second_digits
|
|
1381
|
+
lambda do |e|
|
|
1382
|
+
case e.unit.base
|
|
1383
|
+
when :Y, :M, :W, :D then e.to_s
|
|
1384
|
+
else e.to_time.iso8601(digits)
|
|
1385
|
+
end
|
|
1386
|
+
end
|
|
1387
|
+
end
|
|
1388
|
+
|
|
1389
|
+
# The digits of a second inspect writes: 0, 3, 6 or 9, the fewest that
|
|
1390
|
+
# show every present cell exactly (9 at most, as Time holds).
|
|
1391
|
+
private def inspect_second_digits
|
|
1392
|
+
base_digits = { ms: 3, us: 6, ns: 9, ps: 12, fs: 15, as: 18 }[unit.base]
|
|
1393
|
+
return 0 unless base_digits
|
|
1394
|
+
count = unit.count
|
|
1395
|
+
[0, 3, 6, 9].each do |d|
|
|
1396
|
+
break if d >= base_digits
|
|
1397
|
+
m = 10**(base_digits - d)
|
|
1398
|
+
# ticks * count is a multiple of m when ticks is one of m / gcd(m, count),
|
|
1399
|
+
# which does not multiply out of int64.
|
|
1400
|
+
return d if (ticks % (m / m.gcd(count))).eq(0).all
|
|
1401
|
+
end
|
|
1402
|
+
[base_digits, 9].min
|
|
1403
|
+
end
|
|
1404
|
+
|
|
1276
1405
|
end
|
|
1277
1406
|
|
|
1278
1407
|
# ============================================================================
|
|
@@ -1319,9 +1448,12 @@ class CATimedelta
|
|
|
1319
1448
|
# {CATimedelta}; a {CATime} operand delegates to
|
|
1320
1449
|
# {CATime#+} (commutative).
|
|
1321
1450
|
# @param other [CATimedelta, CATime]
|
|
1451
|
+
# (or a MemoryView producer whose +to_ca+ gives one, such as an Arrow
|
|
1452
|
+
# duration or timestamp array)
|
|
1322
1453
|
# @return [CATimedelta, CATime]
|
|
1323
1454
|
# @raise [TypeError] on incompatible operands.
|
|
1324
1455
|
def +(other)
|
|
1456
|
+
given, other = other, CATimeUnitAlgebra.operand(other)
|
|
1325
1457
|
case other
|
|
1326
1458
|
when CATimedelta
|
|
1327
1459
|
u = common_duration_unit(other.unit)
|
|
@@ -1331,7 +1463,7 @@ class CATimedelta
|
|
|
1331
1463
|
when CATime
|
|
1332
1464
|
other + self # commutative -> time's unit
|
|
1333
1465
|
else
|
|
1334
|
-
raise TypeError, "CATimedelta + #{
|
|
1466
|
+
raise TypeError, "CATimedelta + #{given.class} is not allowed"
|
|
1335
1467
|
end
|
|
1336
1468
|
end
|
|
1337
1469
|
|
|
@@ -1339,9 +1471,12 @@ class CATimedelta
|
|
|
1339
1471
|
# Returns the difference of two {CATimedelta} at the finer of the two
|
|
1340
1472
|
# units (a cross-group pair raises).
|
|
1341
1473
|
# @param other [CATimedelta]
|
|
1474
|
+
# (or a MemoryView producer whose +to_ca+ gives one, such as an Arrow
|
|
1475
|
+
# duration array)
|
|
1342
1476
|
# @return [CATimedelta]
|
|
1343
1477
|
# @raise [TypeError] on a non-timedelta operand.
|
|
1344
1478
|
def -(other)
|
|
1479
|
+
given, other = other, CATimeUnitAlgebra.operand(other)
|
|
1345
1480
|
case other
|
|
1346
1481
|
when CATimedelta
|
|
1347
1482
|
u = common_duration_unit(other.unit)
|
|
@@ -1349,7 +1484,7 @@ class CATimedelta
|
|
|
1349
1484
|
b = CATimeUnitAlgebra.convert_scale!(other.parent, other.unit, u)
|
|
1350
1485
|
(a - b).timedelta(unit: u)
|
|
1351
1486
|
else
|
|
1352
|
-
raise TypeError, "CATimedelta - #{
|
|
1487
|
+
raise TypeError, "CATimedelta - #{given.class} is not allowed"
|
|
1353
1488
|
end
|
|
1354
1489
|
end
|
|
1355
1490
|
|
|
@@ -1385,9 +1520,12 @@ class CATimedelta
|
|
|
1385
1520
|
# {CATimedelta}, by another {CATimedelta} with matching `unit`
|
|
1386
1521
|
# yields a dimensionless CArray.
|
|
1387
1522
|
# @param other [Integer, CATimedelta]
|
|
1523
|
+
# (or a MemoryView producer whose +to_ca+ gives one, such as an Arrow
|
|
1524
|
+
# duration array)
|
|
1388
1525
|
# @return [CATimedelta, CArray]
|
|
1389
1526
|
# @raise [TypeError] on incompatible operands.
|
|
1390
1527
|
def /(other)
|
|
1528
|
+
given, other = other, CATimeUnitAlgebra.operand(other)
|
|
1391
1529
|
case other
|
|
1392
1530
|
when Integer
|
|
1393
1531
|
CATimeUnitAlgebra.trunc_divide(parent, other).timedelta(unit: unit)
|
|
@@ -1396,7 +1534,7 @@ class CATimedelta
|
|
|
1396
1534
|
CATimeUnitAlgebra.trunc_divide(
|
|
1397
1535
|
CATimeUnitAlgebra.convert_scale!(parent, unit, u),
|
|
1398
1536
|
CATimeUnitAlgebra.convert_scale!(other.parent, other.unit, u))
|
|
1399
|
-
else raise TypeError, "CATimedelta / #{
|
|
1537
|
+
else raise TypeError, "CATimedelta / #{given.class} is not allowed"
|
|
1400
1538
|
end
|
|
1401
1539
|
end
|
|
1402
1540
|
|
|
@@ -1412,14 +1550,41 @@ class CATimedelta
|
|
|
1412
1550
|
# or as a {CATimedelta} view for per-axis reduction.
|
|
1413
1551
|
# @return [Element, CATimedelta]
|
|
1414
1552
|
def sum(*args, **opts)
|
|
1415
|
-
round_and_relift(parent.sum(*args, **
|
|
1553
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.sum(*args, **o)) }
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
# @overload accumulate(*args, **opts)
|
|
1557
|
+
# Returns the sum of durations in the storage's own integer type, as
|
|
1558
|
+
# {#sum} does in float64.
|
|
1559
|
+
# @return [Element, CATimedelta]
|
|
1560
|
+
def accumulate(*args, **opts)
|
|
1561
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.accumulate(*args, **o)) }
|
|
1562
|
+
end
|
|
1563
|
+
|
|
1564
|
+
# @overload cumsum(axis: nil)
|
|
1565
|
+
# Returns the running sum of durations as a {CATimedelta} on `self`'s
|
|
1566
|
+
# unit. A masked cell holds the running sum, as {CArray#cumsum} does.
|
|
1567
|
+
# @return [CATimedelta]
|
|
1568
|
+
def cumsum(*args, **opts)
|
|
1569
|
+
round_and_relift(parent.cumsum(*args, **opts))
|
|
1416
1570
|
end
|
|
1417
1571
|
|
|
1572
|
+
# @overload prod(*)
|
|
1573
|
+
# Not supported: a product of durations has the units of a power of
|
|
1574
|
+
# time, which no type represents.
|
|
1575
|
+
# @raise [CArray::DataTypeError] always.
|
|
1576
|
+
def prod(*); raise CArray::DataTypeError, "CATimedelta#prod is ill-defined (powers of time units)"; end
|
|
1577
|
+
|
|
1578
|
+
# @overload cumprod(*)
|
|
1579
|
+
# Not supported, for the same reason as {#prod}.
|
|
1580
|
+
# @raise [CArray::DataTypeError] always.
|
|
1581
|
+
def cumprod(*); raise CArray::DataTypeError, "CATimedelta#cumprod is ill-defined (powers of time units)"; end
|
|
1582
|
+
|
|
1418
1583
|
# @overload mean(axis: nil, **opts)
|
|
1419
1584
|
# Returns the mean duration rounded to the nearest unit count.
|
|
1420
1585
|
# @return [Element, CATimedelta]
|
|
1421
1586
|
def mean(*args, **opts)
|
|
1422
|
-
round_and_relift(parent.mean(*args, **
|
|
1587
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.mean(*args, **o)) }
|
|
1423
1588
|
end
|
|
1424
1589
|
|
|
1425
1590
|
# median / percentile / quantile / stddev / stddevp report on this array's
|
|
@@ -1431,7 +1596,7 @@ class CATimedelta
|
|
|
1431
1596
|
# Returns the median duration on `self`'s unit.
|
|
1432
1597
|
# @return [Element, CATimedelta]
|
|
1433
1598
|
def median(*args, **opts)
|
|
1434
|
-
round_and_relift(parent.median(*args, **
|
|
1599
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.median(*args, **o)) }
|
|
1435
1600
|
end
|
|
1436
1601
|
|
|
1437
1602
|
# @overload percentile(*p, axis: nil, **opts)
|
|
@@ -1440,7 +1605,7 @@ class CATimedelta
|
|
|
1440
1605
|
# two or more give an Array).
|
|
1441
1606
|
# @return [Element, CATimedelta, Array<Element>, Array<CATimedelta>]
|
|
1442
1607
|
def percentile(*args, **opts)
|
|
1443
|
-
round_and_relift(parent.percentile(*args, **
|
|
1608
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.percentile(*args, **o)) }
|
|
1444
1609
|
end
|
|
1445
1610
|
|
|
1446
1611
|
# @overload quantile(axis: nil, **opts)
|
|
@@ -1448,7 +1613,7 @@ class CATimedelta
|
|
|
1448
1613
|
# `self`'s unit.
|
|
1449
1614
|
# @return [Array<Element>, Array<CATimedelta>]
|
|
1450
1615
|
def quantile(*args, **opts)
|
|
1451
|
-
round_and_relift(parent.quantile(*args, **
|
|
1616
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.quantile(*args, **o)) }
|
|
1452
1617
|
end
|
|
1453
1618
|
|
|
1454
1619
|
# @overload stddev(axis: nil, **opts)
|
|
@@ -1457,7 +1622,7 @@ class CATimedelta
|
|
|
1457
1622
|
# the precision matters.
|
|
1458
1623
|
# @return [Element, CATimedelta]
|
|
1459
1624
|
def stddev(*args, **opts)
|
|
1460
|
-
round_and_relift(parent.stddev(*args, **
|
|
1625
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.stddev(*args, **o)) }
|
|
1461
1626
|
end
|
|
1462
1627
|
|
|
1463
1628
|
# @overload stddevp(axis: nil, **opts)
|
|
@@ -1465,7 +1630,7 @@ class CATimedelta
|
|
|
1465
1630
|
# {#stddev}.
|
|
1466
1631
|
# @return [Element, CATimedelta]
|
|
1467
1632
|
def stddevp(*args, **opts)
|
|
1468
|
-
round_and_relift(parent.stddevp(*args, **
|
|
1633
|
+
with_surface_fill(opts) { |o| round_and_relift(parent.stddevp(*args, **o)) }
|
|
1469
1634
|
end
|
|
1470
1635
|
|
|
1471
1636
|
def round_and_relift(r)
|
|
@@ -1482,39 +1647,21 @@ class CATimedelta
|
|
|
1482
1647
|
# Not supported: the variance of durations has squared-time units, which
|
|
1483
1648
|
# no type represents -- the same reason {CATime#variance} refuses. Use
|
|
1484
1649
|
# {#stddev} for the spread as a duration.
|
|
1485
|
-
# @raise [
|
|
1650
|
+
# @raise [CArray::DataTypeError] always.
|
|
1486
1651
|
def variance(*)
|
|
1487
|
-
raise
|
|
1652
|
+
raise CArray::DataTypeError, "CATimedelta#variance is ill-defined (squared-time units); use stddev"
|
|
1488
1653
|
end
|
|
1489
1654
|
|
|
1490
1655
|
# @overload variancep(*)
|
|
1491
1656
|
# Not supported, for the same reason as {#variance}. Use {#stddevp}.
|
|
1492
|
-
# @raise [
|
|
1657
|
+
# @raise [CArray::DataTypeError] always.
|
|
1493
1658
|
def variancep(*)
|
|
1494
|
-
raise
|
|
1659
|
+
raise CArray::DataTypeError, "CATimedelta#variancep is ill-defined (squared-time units); use stddevp"
|
|
1495
1660
|
end
|
|
1496
1661
|
|
|
1497
|
-
# min / max ride the core reduce Face gate (ORDERABLE storage
|
|
1498
|
-
# output re-lift
|
|
1499
|
-
#
|
|
1500
|
-
# minmax has no core gate, so re-lift both extremes here.
|
|
1501
|
-
|
|
1502
|
-
# @overload minmax(*axes, **opts)
|
|
1503
|
-
# Returns the shortest and longest duration as a `[min, max]` pair.
|
|
1504
|
-
# @return [Array(Element, Element), Array(CATimedelta, CATimedelta)]
|
|
1505
|
-
def minmax(*args, **opts)
|
|
1506
|
-
lo, hi = parent.minmax(*args, **opts)
|
|
1507
|
-
[relift_extremum(lo), relift_extremum(hi)]
|
|
1508
|
-
end
|
|
1509
|
-
|
|
1510
|
-
def relift_extremum(r)
|
|
1511
|
-
case r
|
|
1512
|
-
when Integer then Element.new(r, unit)
|
|
1513
|
-
when CArray then r.timedelta(unit: unit)
|
|
1514
|
-
else r
|
|
1515
|
-
end
|
|
1516
|
-
end
|
|
1517
|
-
private :relift_extremum
|
|
1662
|
+
# min / max / minmax ride the core reduce Face gate (ORDERABLE storage
|
|
1663
|
+
# descent + output re-lift); the inherited methods return the Element /
|
|
1664
|
+
# CATimedelta shapes directly.
|
|
1518
1665
|
# @!endgroup
|
|
1519
1666
|
|
|
1520
1667
|
# sort / partition ride the core sort Face gate (ORDERABLE storage
|
|
@@ -1535,6 +1682,23 @@ class CATimedelta
|
|
|
1535
1682
|
# @param operand [CATimedelta, CATimedelta::Element]
|
|
1536
1683
|
# @return [CATimedelta] in self's unit.
|
|
1537
1684
|
# @raise [TypeError, ArgumentError] on an unreconcilable operand / unit.
|
|
1685
|
+
# Returns `[self, other]` in one unit, the finer grid of the two, which
|
|
1686
|
+
# holds every value of both exactly. A lookup between two duration
|
|
1687
|
+
# arrays of different units meets there, so it does not depend on which
|
|
1688
|
+
# array is the receiver. A calendar and a fixed-length duration have no
|
|
1689
|
+
# common unit and raise.
|
|
1690
|
+
# @api private
|
|
1691
|
+
def in_common_unit_with (other)
|
|
1692
|
+
return [self, other] if other.unit == unit
|
|
1693
|
+
unless CATimeUnitAlgebra.same_group?(unit, other.unit)
|
|
1694
|
+
raise ArgumentError,
|
|
1695
|
+
"cannot compare a #{unit} duration with a #{other.unit} duration: " \
|
|
1696
|
+
"a calendar length (months, years) has no fixed length in days"
|
|
1697
|
+
end
|
|
1698
|
+
u = CATimeUnitAlgebra.finer(unit, other.unit)
|
|
1699
|
+
[to_unit(u), other.to_unit(u)]
|
|
1700
|
+
end
|
|
1701
|
+
|
|
1538
1702
|
def to_comparable (operand)
|
|
1539
1703
|
case operand
|
|
1540
1704
|
when CATimedelta
|
|
@@ -1551,8 +1715,8 @@ class CATimedelta
|
|
|
1551
1715
|
end
|
|
1552
1716
|
end
|
|
1553
1717
|
|
|
1554
|
-
# @overload
|
|
1555
|
-
# Write-direction counterpart of
|
|
1718
|
+
# @overload element_to_storage(surface)
|
|
1719
|
+
# Write-direction counterpart of storage_to_element: brings a surface
|
|
1556
1720
|
# value object into this Face's int64 storage (count in self's unit) so a
|
|
1557
1721
|
# scalar store round-trips with a fetch. A {Element} is reconciled to
|
|
1558
1722
|
# self's unit via {#to_comparable} (lossless discipline; a `Time` /
|
|
@@ -1563,7 +1727,7 @@ class CATimedelta
|
|
|
1563
1727
|
# @return [Integer, Object] the storage-domain value, or `surface`
|
|
1564
1728
|
# unchanged for a pass-through type.
|
|
1565
1729
|
# @raise [TypeError, ArgumentError] on an unreconcilable surface / unit.
|
|
1566
|
-
def
|
|
1730
|
+
def element_to_storage (surface)
|
|
1567
1731
|
case surface
|
|
1568
1732
|
when Integer, String
|
|
1569
1733
|
surface
|
|
@@ -1609,6 +1773,17 @@ class CATimedelta
|
|
|
1609
1773
|
end
|
|
1610
1774
|
CATimeUnitAlgebra.finer(unit, ou)
|
|
1611
1775
|
end
|
|
1776
|
+
|
|
1777
|
+
# inspect's name for the array: the class and the tick, which the storage
|
|
1778
|
+
# type name (fixlen[8]) does not tell.
|
|
1779
|
+
private def inspect_label
|
|
1780
|
+
"#{self.class}[#{unit}]"
|
|
1781
|
+
end
|
|
1782
|
+
|
|
1783
|
+
# inspect's cell: the duration in the base unit of the tick.
|
|
1784
|
+
private def inspect_cell_formatter
|
|
1785
|
+
lambda { |e| "#{e.value * e.unit.count}#{e.unit.base}" }
|
|
1786
|
+
end
|
|
1612
1787
|
end
|
|
1613
1788
|
|
|
1614
1789
|
# Reads a time literal (Time / DateTime / Integer unix-seconds / String) into
|
|
@@ -1621,8 +1796,12 @@ module CATimeLiteral
|
|
|
1621
1796
|
|
|
1622
1797
|
# Date fields of a String literal, UTC. Ruby's Date._parse does not read
|
|
1623
1798
|
# the calendar-grid forms CATime#to_s prints -- "2019-09" comes back as a
|
|
1624
|
-
# month of 20 with a zone, and a bare "2019" as a month and a day --
|
|
1625
|
-
#
|
|
1799
|
+
# month of 20 with a zone, and a bare "2019" as a month and a day -- nor an
|
|
1800
|
+
# hour alone after the date ("2024-01-01T05", ISO 8601 reduced to the
|
|
1801
|
+
# hour), so those are read here and everything else is left to Date._parse.
|
|
1802
|
+
# Date._parse passes over what it cannot read, so text with a time after
|
|
1803
|
+
# the date that comes back without an hour is refused rather than read as
|
|
1804
|
+
# midnight.
|
|
1626
1805
|
#
|
|
1627
1806
|
# What Date._parse returns is then checked, because an out-of-range field
|
|
1628
1807
|
# used to flow into the civil kernel and normalise into a different date:
|
|
@@ -1634,23 +1813,71 @@ module CATimeLiteral
|
|
|
1634
1813
|
if format then Date._strptime(spec, format)
|
|
1635
1814
|
elsif spec =~ /\A(\d{4})-(\d{1,2})\z/ then { year: $1.to_i, mon: $2.to_i }
|
|
1636
1815
|
elsif spec =~ /\A(\d{4})\z/ then { year: $1.to_i }
|
|
1816
|
+
elsif (hour = hour_only_fields(spec)) then hour
|
|
1637
1817
|
else Date._parse(spec)
|
|
1638
1818
|
end
|
|
1639
1819
|
unless h && h[:year]
|
|
1640
1820
|
raise ArgumentError, "cannot parse time #{spec.inspect}"
|
|
1641
1821
|
end
|
|
1822
|
+
if !format && !h[:hour] &&
|
|
1823
|
+
(spec =~ /\d\s*T\s*\d/ || spec =~ /\A\s*[+-]?\d{4,}[-\/]\d{1,2}[-\/]\d{1,2}\s+\d/)
|
|
1824
|
+
raise ArgumentError, "cannot parse time #{spec.inspect}: the time after the date is not read"
|
|
1825
|
+
end
|
|
1826
|
+
# Text left over after the format is not part of a time in that format.
|
|
1827
|
+
if h[:leftover] && !h[:leftover].strip.empty?
|
|
1828
|
+
raise ArgumentError,
|
|
1829
|
+
"cannot parse time #{spec.inspect}: #{h[:leftover].inspect} is " \
|
|
1830
|
+
"left over after #{format.inspect}"
|
|
1831
|
+
end
|
|
1642
1832
|
y, m, d = h[:year], h[:mon] || 1, h[:mday] || 1
|
|
1643
1833
|
unless (1..12).cover?(m) && Date.valid_date?(y, m, d, Date::GREGORIAN)
|
|
1644
1834
|
raise ArgumentError,
|
|
1645
1835
|
"cannot parse time #{spec.inspect}: it reads as year #{y}, " \
|
|
1646
1836
|
"month #{m}, day #{d}, which is not a date"
|
|
1647
1837
|
end
|
|
1838
|
+
# Hour 24 is the end of the day, 24:00:00, and nothing past it.
|
|
1839
|
+
if h[:hour] == 24 && [h[:min], h[:sec], h[:sec_fraction]].any? { |v| v && v != 0 }
|
|
1840
|
+
raise ArgumentError,
|
|
1841
|
+
"cannot parse time #{spec.inspect}: hour 24 is only 24:00:00"
|
|
1842
|
+
end
|
|
1648
1843
|
h
|
|
1649
1844
|
end
|
|
1650
1845
|
private_class_method :parse_date_fields
|
|
1651
1846
|
|
|
1847
|
+
# "YYYY-MM-DDThh" with an optional zone ("Z", "+hh", "+hhmm", "+hh:mm").
|
|
1848
|
+
def hour_only_fields(spec)
|
|
1849
|
+
m = /\A(\d{4})-(\d{2})-(\d{2})T(\d{2})(?:(Z)|([+-])(\d{2})(?::?(\d{2}))?)?\z/.match(spec)
|
|
1850
|
+
return nil unless m
|
|
1851
|
+
h = { year: m[1].to_i, mon: m[2].to_i, mday: m[3].to_i, hour: m[4].to_i }
|
|
1852
|
+
raise ArgumentError, "cannot parse time #{spec.inspect}: hour #{h[:hour]}" if h[:hour] > 23
|
|
1853
|
+
if m[6]
|
|
1854
|
+
oh, om = m[7].to_i, m[8].to_i
|
|
1855
|
+
raise ArgumentError, "cannot parse time #{spec.inspect}: offset" if oh > 23 || om > 59
|
|
1856
|
+
h[:offset] = (oh * 3600 + om * 60) * (m[6] == "-" ? -1 : 1)
|
|
1857
|
+
elsif m[5]
|
|
1858
|
+
h[:offset] = 0
|
|
1859
|
+
end
|
|
1860
|
+
h
|
|
1861
|
+
end
|
|
1862
|
+
private_class_method :hour_only_fields
|
|
1863
|
+
|
|
1864
|
+
# An instant written outside CArray (Time / Date / DateTime / String) as a
|
|
1865
|
+
# length-1 CATime in the coarsest of :s / :ms / :us / :ns that holds it
|
|
1866
|
+
# exactly, so a comparison sees the instant it names and not that instant
|
|
1867
|
+
# floored onto some other grid. Finer than a nanosecond raises.
|
|
1868
|
+
def exact_instant(spec)
|
|
1869
|
+
sec = epoch_seconds(spec)
|
|
1870
|
+
[:s, :ms, :us, :ns].each_with_index do |u, k|
|
|
1871
|
+
ticks = sec * (10 ** (3 * k))
|
|
1872
|
+
if ticks.denominator == 1
|
|
1873
|
+
return CATime.wrap(CA_INT64([ticks.to_i]), unit: u)
|
|
1874
|
+
end
|
|
1875
|
+
end
|
|
1876
|
+
raise ArgumentError, "#{spec.inspect} is finer than a nanosecond"
|
|
1877
|
+
end
|
|
1878
|
+
|
|
1652
1879
|
# Exact Rational seconds since the Unix epoch for a start literal (Time /
|
|
1653
|
-
# DateTime / Integer unix-seconds / String). UTC default.
|
|
1880
|
+
# Date / DateTime / Integer unix-seconds / String). UTC default.
|
|
1654
1881
|
def epoch_seconds(spec, format = nil)
|
|
1655
1882
|
require 'date'
|
|
1656
1883
|
require 'time'
|
|
@@ -1680,6 +1907,8 @@ module CATimeLiteral
|
|
|
1680
1907
|
else
|
|
1681
1908
|
if defined?(DateTime) && spec.is_a?(DateTime)
|
|
1682
1909
|
spec.to_time.to_r
|
|
1910
|
+
elsif defined?(Date) && spec.is_a?(Date)
|
|
1911
|
+
Rational(CATimeCivil.days_from_civil_1(spec.year, spec.month, spec.day) * 86400)
|
|
1683
1912
|
else
|
|
1684
1913
|
raise ArgumentError, "cannot parse time #{spec.class}"
|
|
1685
1914
|
end
|
|
@@ -1727,6 +1956,189 @@ module CATimeLiteral
|
|
|
1727
1956
|
end
|
|
1728
1957
|
end
|
|
1729
1958
|
|
|
1959
|
+
# A format :infer can choose: a strptime format, read strictly (see
|
|
1960
|
+
# __strptime_fields__), and what the text carries (:date, :clock, or
|
|
1961
|
+
# :fraction of a second).
|
|
1962
|
+
TimeFormat = Struct.new(:format, :kind)
|
|
1963
|
+
|
|
1964
|
+
INFER_DATES = %w[%d/%m/%Y %m/%d/%Y %d-%m-%Y %m-%d-%Y %d.%m.%Y %d/%m/%y %m/%d/%y] +
|
|
1965
|
+
["%d %b %Y", "%d-%b-%Y", "%d-%b-%y", "%b %d, %Y", "%b %d %Y", "%Y%m%d"]
|
|
1966
|
+
|
|
1967
|
+
# A space in a format matches any run of spaces, none included.
|
|
1968
|
+
INFER_CLOCKS = [
|
|
1969
|
+
["%H:%M", :clock], ["%H:%M:%S", :clock], ["%H:%M:%S.%N", :fraction],
|
|
1970
|
+
["%I:%M %p", :clock], ["%I:%M:%S %p", :clock],
|
|
1971
|
+
].freeze
|
|
1972
|
+
|
|
1973
|
+
# A time after a date: none, or a clock with or without a zone. %z takes
|
|
1974
|
+
# an offset, Z, UTC or GMT, not a zone named for a place (some of those
|
|
1975
|
+
# names mean different offsets in different places).
|
|
1976
|
+
INFER_TIMES = ([["", :date]] +
|
|
1977
|
+
INFER_CLOCKS.flat_map { |clock, kind| [[" #{clock}", kind], [" #{clock} %z", kind]] }
|
|
1978
|
+
).freeze
|
|
1979
|
+
|
|
1980
|
+
# A date written in Japanese, year first ("2024<year>1<month>2<day>"),
|
|
1981
|
+
# takes a time in the same markers ("3<hour>4<minute>", "...5<second>")
|
|
1982
|
+
# or with colons.
|
|
1983
|
+
JA_YEAR, JA_MONTH, JA_DAY = "年", "月", "日"
|
|
1984
|
+
JA_HOUR, JA_MINUTE, JA_SECOND = "時", "分", "秒"
|
|
1985
|
+
INFER_JA_TIMES = ([["", :date]] +
|
|
1986
|
+
([["%H#{JA_HOUR}%M#{JA_MINUTE}", :clock],
|
|
1987
|
+
["%H#{JA_HOUR}%M#{JA_MINUTE}%S#{JA_SECOND}", :clock]] + INFER_CLOCKS)
|
|
1988
|
+
.map { |clock, kind| [" #{clock}", kind] }).freeze
|
|
1989
|
+
|
|
1990
|
+
INFER_FORMATS = (
|
|
1991
|
+
INFER_DATES.product(INFER_TIMES).map { |date, (time, kind)|
|
|
1992
|
+
TimeFormat.new(date + time, kind)
|
|
1993
|
+
} +
|
|
1994
|
+
INFER_JA_TIMES.map { |time, kind|
|
|
1995
|
+
TimeFormat.new("%Y#{JA_YEAR}%m#{JA_MONTH}%d#{JA_DAY}#{time}", kind)
|
|
1996
|
+
}
|
|
1997
|
+
).freeze
|
|
1998
|
+
INFER_FORMAT_STRINGS = INFER_FORMATS.map(&:format).freeze
|
|
1999
|
+
|
|
2000
|
+
# The one format of INFER_FORMATS that the text of object array `x` is
|
|
2001
|
+
# written in, `first` being its first present cell, as
|
|
2002
|
+
# [format, fields, address]: the fields __strptime_fields__ read in it,
|
|
2003
|
+
# and the address of the first cell not in it, or nil.
|
|
2004
|
+
#
|
|
2005
|
+
# The first cell gives the candidates and each later cell drops those it
|
|
2006
|
+
# does not fit, until one is left. Each candidate is read over the whole
|
|
2007
|
+
# array once, and the candidate a later cell leaves last is the one left:
|
|
2008
|
+
# no candidate for the first cell or none left (the last ones dropped by
|
|
2009
|
+
# one cell) raises CAFrame::UnreadableColumn, and more than one left at the
|
|
2010
|
+
# end raises CAFrame::AmbiguousTimeFormat (CAFrame is its only caller). A cell
|
|
2011
|
+
# after the choice that is not in the format is returned for the caller
|
|
2012
|
+
# to report.
|
|
2013
|
+
def infer_time_format(x, first)
|
|
2014
|
+
candidates = CArray.__strptime_fits__(first, INFER_FORMAT_STRINGS)
|
|
2015
|
+
if candidates.empty?
|
|
2016
|
+
raise CAFrame::UnreadableColumn,
|
|
2017
|
+
"cannot infer a time format from #{first.inspect}; " \
|
|
2018
|
+
"pass a strptime format, or :mixed to guess at each cell"
|
|
2019
|
+
end
|
|
2020
|
+
runs = candidates.map do |i|
|
|
2021
|
+
fields = x.__strptime_fields__(INFER_FORMATS[i].format, true)
|
|
2022
|
+
[INFER_FORMATS[i], fields, fields[7].first || Float::INFINITY]
|
|
2023
|
+
end
|
|
2024
|
+
last = runs.map(&:last).max
|
|
2025
|
+
left = runs.select { |run| run[2] == last }
|
|
2026
|
+
if left.size > 1
|
|
2027
|
+
if last == Float::INFINITY
|
|
2028
|
+
formats = left.map { |run| run[0].format }
|
|
2029
|
+
raise CAFrame::AmbiguousTimeFormat.new(
|
|
2030
|
+
"#{first.inspect} could be read as " \
|
|
2031
|
+
"#{formats.map(&:inspect).join(' or ')}, " \
|
|
2032
|
+
"and no cell tells which; pass the format",
|
|
2033
|
+
formats: formats)
|
|
2034
|
+
end
|
|
2035
|
+
raise CAFrame::UnreadableColumn,
|
|
2036
|
+
"#{x.flatten[last].inspect} is not in the format of #{first.inspect}; " \
|
|
2037
|
+
"pass a strptime format, or :mixed to guess at each cell"
|
|
2038
|
+
end
|
|
2039
|
+
f, fields, bad = left.first
|
|
2040
|
+
[f, fields, bad == Float::INFINITY ? nil : bad]
|
|
2041
|
+
end
|
|
2042
|
+
|
|
2043
|
+
# [kind, per] for __time_ticks_from_fields__: ticks of `per` nanoseconds
|
|
2044
|
+
# (:fixed) or months (:calendar), or nil when `res` is finer than a
|
|
2045
|
+
# nanosecond.
|
|
2046
|
+
def tick_kind(res)
|
|
2047
|
+
if CATimeUnitAlgebra::FIXED.key?(res.base)
|
|
2048
|
+
per = res.tick_ratio * 1_000_000_000
|
|
2049
|
+
kind = :fixed
|
|
2050
|
+
else
|
|
2051
|
+
per = res.tick_ratio
|
|
2052
|
+
kind = :calendar
|
|
2053
|
+
end
|
|
2054
|
+
(per.denominator == 1 && per >= 1) ? [kind, per.to_i] : nil
|
|
2055
|
+
end
|
|
2056
|
+
|
|
2057
|
+
# The ticks of every cell of `x` on the `res` grid, as an int64 array of
|
|
2058
|
+
# x's shape (missing and, under on_error: :mask, unparseable cells
|
|
2059
|
+
# masked), or nil when `res` is finer than a nanosecond. A strptime
|
|
2060
|
+
# format that __strptime_fields__ reads is read for the whole array in C;
|
|
2061
|
+
# otherwise each String is parsed into fields here. Either way the fields
|
|
2062
|
+
# become ticks for the whole array at once, and a cell that is not a
|
|
2063
|
+
# String goes through tick_index.
|
|
2064
|
+
def ticks_from_cells(x, res, format, on_error)
|
|
2065
|
+
require 'date'
|
|
2066
|
+
kind, per = tick_kind(res)
|
|
2067
|
+
return nil unless kind
|
|
2068
|
+
if format.is_a?(String)
|
|
2069
|
+
text = (x.data_type == CA_OBJECT && !x.face?) ? x : x.to_type(:object)
|
|
2070
|
+
fields = text.__strptime_fields__(format, false)
|
|
2071
|
+
return ticks_from_fields(fields, x, res, format, on_error) if fields
|
|
2072
|
+
end
|
|
2073
|
+
cells = x.flatten.to_a
|
|
2074
|
+
n = cells.size
|
|
2075
|
+
year = Array.new(n, 1970)
|
|
2076
|
+
mon = Array.new(n, 1)
|
|
2077
|
+
day = Array.new(n, 1)
|
|
2078
|
+
sod = Array.new(n, 0)
|
|
2079
|
+
frac = Array.new(n, 0)
|
|
2080
|
+
off = Array.new(n, 0)
|
|
2081
|
+
present = Array.new(n, 0)
|
|
2082
|
+
direct = {}
|
|
2083
|
+
cells.each_with_index do |s, i|
|
|
2084
|
+
next if s.nil? || UNDEF.equal?(s)
|
|
2085
|
+
begin
|
|
2086
|
+
if s.is_a?(String)
|
|
2087
|
+
h = parse_date_fields(s, format)
|
|
2088
|
+
year[i] = h[:year]
|
|
2089
|
+
mon[i] = h[:mon] || 1
|
|
2090
|
+
day[i] = h[:mday] || 1
|
|
2091
|
+
sod[i] = (h[:hour] || 0) * 3600 + (h[:min] || 0) * 60 + (h[:sec] || 0)
|
|
2092
|
+
frac[i] = (h[:sec_fraction] * 1_000_000_000).floor if h[:sec_fraction]
|
|
2093
|
+
off[i] = h[:offset] if h[:offset]
|
|
2094
|
+
present[i] = 1
|
|
2095
|
+
else
|
|
2096
|
+
direct[i] = tick_index(s, res, format)
|
|
2097
|
+
end
|
|
2098
|
+
rescue ArgumentError, TypeError
|
|
2099
|
+
raise if on_error == :raise
|
|
2100
|
+
end
|
|
2101
|
+
end
|
|
2102
|
+
raw = CArray.__time_ticks_from_fields__(
|
|
2103
|
+
CA_INT64(year), CA_INT64(mon), CA_INT64(day), CA_INT64(sod),
|
|
2104
|
+
CA_INT64(frac), CA_INT64(off), CA_UINT8(present), kind, per)
|
|
2105
|
+
check_ticks_fit(raw, present, present.count(1), res) { cells }
|
|
2106
|
+
direct.each { |i, t| raw[i] = t }
|
|
2107
|
+
raw.reshape(*x.shape)
|
|
2108
|
+
end
|
|
2109
|
+
|
|
2110
|
+
# Ticks from what __strptime_fields__ read; the cells it deferred are
|
|
2111
|
+
# read one by one through tick_index.
|
|
2112
|
+
def ticks_from_fields(fields, x, res, format, on_error)
|
|
2113
|
+
kind, per = tick_kind(res)
|
|
2114
|
+
year, mon, day, sod, frac, off, present, unreadable, deferred, = fields
|
|
2115
|
+
cells = nil
|
|
2116
|
+
if on_error == :raise && !unreadable.empty?
|
|
2117
|
+
cells = x.flatten.to_a
|
|
2118
|
+
raise ArgumentError, "cannot parse time #{cells[unreadable.first].inspect}"
|
|
2119
|
+
end
|
|
2120
|
+
raw = CArray.__time_ticks_from_fields__(year, mon, day, sod, frac, off,
|
|
2121
|
+
present, kind, per)
|
|
2122
|
+
check_ticks_fit(raw, present, present.sum, res) { cells ||= x.flatten.to_a }
|
|
2123
|
+
unless deferred.empty?
|
|
2124
|
+
cells ||= x.flatten.to_a
|
|
2125
|
+
deferred.each do |i|
|
|
2126
|
+
raw[i] = tick_index(cells[i], res, format)
|
|
2127
|
+
rescue ArgumentError, TypeError
|
|
2128
|
+
raise if on_error == :raise
|
|
2129
|
+
end
|
|
2130
|
+
end
|
|
2131
|
+
raw.reshape(*x.shape)
|
|
2132
|
+
end
|
|
2133
|
+
|
|
2134
|
+
# A tick that does not fit int64 is out of range, not unparseable.
|
|
2135
|
+
def check_ticks_fit(raw, present, present_count, res)
|
|
2136
|
+
return if raw.count_not_masked >= present_count
|
|
2137
|
+
cells = yield
|
|
2138
|
+
i = (0...raw.elements).find { |k| present[k] == 1 && UNDEF.equal?(raw[k]) }
|
|
2139
|
+
raise RangeError, "time #{cells[i].inspect} does not fit int64 ticks of #{res}"
|
|
2140
|
+
end
|
|
2141
|
+
|
|
1730
2142
|
# Single-literal build for {.time}: a 1-element CATime, honouring
|
|
1731
2143
|
# the on_error policy (raise, or a masked cell).
|
|
1732
2144
|
def to_time_array(literal, res, format, on_error)
|
|
@@ -1836,6 +2248,8 @@ class CArray
|
|
|
1836
2248
|
unless x.is_a?(CArray)
|
|
1837
2249
|
return CATimeLiteral.to_time_array(x, res, format, on_error)
|
|
1838
2250
|
end
|
|
2251
|
+
raw = CATimeLiteral.ticks_from_cells(x, res, format, on_error)
|
|
2252
|
+
return raw.time(unit: res) if raw
|
|
1839
2253
|
raw = CArray.int64(*x.shape)
|
|
1840
2254
|
x.each_index do |*idx|
|
|
1841
2255
|
s = x[*idx]
|