Class: CATime::Grid
- Inherits:
-
Object
- Object
- CATime::Grid
- Defined in:
- lib/carray/time.rb
Overview
A tick resolution and where its ticks are anchored: the (unit:, origin:) pair that #timesteps, #snap and from_timesteps already
take, given a name and a value.
Resolution says how wide a tick is; Grid says that and where tick 0 starts. A CATime is stored as epoch-anchored int64 either way, so this reifies a calling convention -- it does not add a second time representation, and the reference-time epoch stays out of storage.
g = CATime::Grid.parse("12 hours since 2017-11-30 09:00")
t.snap(g, direction: :floor) # the pair, passed once
t.timesteps(g) # which tick each element falls in
g.at(CA_INT64([0, 1, 2])) # and back
Proleptic Gregorian, UTC. There is no calendar seam here on purpose: a swappable calendar taxes every operation that touches a time, and the benefit does not follow.
Constant Summary collapse
- ORIGIN_SHIFT =
udunits' origin-shift operators, all equivalent: the words
since,after,from,ref(whitespace-separated) and the@sign, which needs none ("seconds@1970-01-01"). Generous on input because aunitsattribute is written by whoever wrote the file; #to_s only ever emitssince, which is what CF-conforming files use. /\A(.+?)(?:\s+(?:since|after|from|ref)\s+|\s*@\s*)(.+)\z/i- WORD =
Plural unit words, the spelling Resolution.parse reads back. (Bare Resolution#to_s does not round-trip: it prints "h" / "12 h", and Resolution.parse takes neither.)
{ Y: "years", M: "months", W: "weeks", D: "days", h: "hours", m: "minutes", s: "seconds", ms: "milliseconds", us: "microseconds", ns: "nanoseconds", ps: "picoseconds", fs: "femtoseconds", as: "attoseconds" }.freeze
- FALLBACK_BASES =
Storage resolutions to fall back on when the unit's own tick cannot hold the origin exactly (a day grid anchored at 12:00 needs seconds).
%i[s ms us ns].freeze
Instance Attribute Summary collapse
-
#origin ⇒ Element?
readonly
The instant tick 0 starts at.
-
#storage ⇒ Resolution
readonly
The resolution an array on this grid is stored on: the unit itself, or finer when the origin's phase needs it.
-
#unit ⇒ Resolution
readonly
The tick this grid counts in.
Class Method Summary collapse
- .parse(spec, origin: nil) ⇒ Grid
-
.resolve(grid, unit, origin) ⇒ Array(Object, Object)
Normalizes the three ways a grid reaches a method -- positionally, as
unit:, or as the loose(unit:, origin:)pair -- into that pair.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Two grids are equal when they place ticks in the same places: same timestep, same origin.
-
#at(k) ⇒ CATime, Element
Tick indices -> instants: the from_timesteps direction, but answering on #storage so a phased origin survives.
-
#calendar? ⇒ Boolean
Whether the unit is a calendar one (:Y / :M), whose ticks are month ordinals rather than a fixed number of seconds.
-
#hash ⇒ Integer
Hashes with #==, so a grid works as a Hash key.
-
#index(time) ⇒ CArray
Instants -> tick indices: the #timesteps direction.
-
#initialize(unit, origin = nil) ⇒ Grid
constructor
A new instance of Grid.
-
#inspect ⇒ String
The #to_s spec, wrapped for the console.
-
#on(time) ⇒ CArray
Boolean, flagging elements that land on a tick.
-
#on?(time) ⇒ Boolean
Whether every element lands on a tick.
-
#origin_ticks ⇒ Integer
Storage ticks from the epoch to the origin.
-
#range(start, last) ⇒ Object
A regular series on this grid.
-
#series(start, count:) ⇒ Object
A regular series on this grid:
countelements fromstart, one per tick. -
#step_ticks ⇒ Integer
Storage ticks per unit tick.
-
#to_s ⇒ String
"12 hours since 2017-11-30 09:00:00", which Grid.parse reads back into an equal grid.
Constructor Details
#initialize(unit, origin = nil) ⇒ Grid
Returns a new instance of Grid.
2251 2252 2253 2254 2255 2256 2257 |
# File 'lib/carray/time.rb', line 2251 def initialize(unit, origin = nil) @unit = Resolution.parse(unit) @origin = origin @storage = resolve_storage @origin = CArray.time(origin, unit: @storage)[0] if origin freeze end |
Instance Attribute Details
#origin ⇒ Element? (readonly)
Returns the instant tick 0 starts at. nil is the epoch,
which is what every origin: keyword already defaults to.
2264 2265 2266 |
# File 'lib/carray/time.rb', line 2264 def origin @origin end |
#storage ⇒ Resolution (readonly)
Returns the resolution an array on this grid is stored on: the unit itself, or finer when the origin's phase needs it. Derived from (unit, origin) and memoized here, so a grid's identity stays the (count, unit, origin) triple.
2270 2271 2272 |
# File 'lib/carray/time.rb', line 2270 def storage @storage end |
#unit ⇒ Resolution (readonly)
Returns the tick this grid counts in.
2260 2261 2262 |
# File 'lib/carray/time.rb', line 2260 def unit @unit end |
Class Method Details
.parse(spec, origin: nil) ⇒ Grid
2228 2229 2230 2231 2232 2233 2234 2235 |
# File 'lib/carray/time.rb', line 2228 def self.parse(spec, origin: nil) return spec if spec.is_a?(Grid) if spec.is_a?(String) and (fields = ORIGIN_SHIFT.match(spec)) new(fields[1], fields[2]) else new(spec, origin) end end |
.resolve(grid, unit, origin) ⇒ Array(Object, Object)
Normalizes the three ways a grid reaches a method -- positionally, as
unit:, or as the loose (unit:, origin:) pair -- into that pair.
One call at the head of a method is the whole Grid arm.
2241 2242 2243 2244 2245 2246 2247 2248 2249 |
# File 'lib/carray/time.rb', line 2241 def self.resolve(grid, unit, origin) return [grid.unit, grid.origin] if grid.is_a?(Grid) return [unit.unit, unit.origin] if unit.is_a?(Grid) unless grid.nil? raise ArgumentError, "the positional argument must be a CATime::Grid (got #{grid.class})" end [unit, origin] end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Two grids are equal when they place ticks in the same places: same timestep, same origin.
2325 |
# File 'lib/carray/time.rb', line 2325 def ==(other) = other.is_a?(Grid) && other.unit == @unit && other.to_s == to_s |
#at(k) ⇒ CATime, Element
Tick indices -> instants: the CATime.from_timesteps direction, but answering on #storage so a phased origin survives.
2288 2289 2290 2291 2292 |
# File 'lib/carray/time.rb', line 2288 def at(k) return at(CArray.int64(1) { Integer(k) })[0] unless k.is_a?(CArray) return CATime.from_timesteps(k.int64, unit: @unit, origin: @origin) if calendar? (origin_ticks + k.int64 * step_ticks).time(unit: @storage) end |
#calendar? ⇒ Boolean
Returns whether the unit is a calendar one (:Y / :M), whose ticks are month ordinals rather than a fixed number of seconds.
2274 |
# File 'lib/carray/time.rb', line 2274 def calendar? = %i[Y M].include?(@unit.base) |
#hash ⇒ Integer
Hashes with #==, so a grid works as a Hash key.
2330 |
# File 'lib/carray/time.rb', line 2330 def hash = [@unit, to_s].hash |
#index(time) ⇒ CArray
Instants -> tick indices: the CATime#timesteps direction. Off-grid elements floor toward the past; ask #on when that would be a lie.
2297 |
# File 'lib/carray/time.rb', line 2297 def index(time) = time.timesteps(self) |
#inspect ⇒ String
Returns the #to_s spec, wrapped for the console.
2320 |
# File 'lib/carray/time.rb', line 2320 def inspect = "#<CATime::Grid #{self}>" |
#on(time) ⇒ CArray
Returns boolean, flagging elements that land on a tick.
2300 |
# File 'lib/carray/time.rb', line 2300 def on(time) = time.is_righttime(self) |
#on?(time) ⇒ Boolean
Returns whether every element lands on a tick.
2303 |
# File 'lib/carray/time.rb', line 2303 def on?(time) = on(time).all |
#origin_ticks ⇒ Integer
Returns storage ticks from the epoch to the origin.
2280 2281 2282 |
# File 'lib/carray/time.rb', line 2280 def origin_ticks @origin ? Integer(CATimeLiteral.epoch_seconds(@origin) / @storage.tick_ratio) : 0 end |
#range(start, last) ⇒ Object
A regular series on this grid.
2306 2307 2308 |
# File 'lib/carray/time.rb', line 2306 def range(start, last) = CArray.time_range(start, last, unit: @storage, step: @unit) # A regular series on this grid: +count+ elements from +start+, one per # tick. |
#series(start, count:) ⇒ Object
A regular series on this grid: count elements from start, one per
tick.
2309 |
# File 'lib/carray/time.rb', line 2309 def series(start, count:) = CArray.time_series(start, count: count, unit: @storage, step: @unit) |
#step_ticks ⇒ Integer
Returns storage ticks per unit tick.
2277 |
# File 'lib/carray/time.rb', line 2277 def step_ticks = Integer(@unit.tick_ratio / @storage.tick_ratio) |
#to_s ⇒ String
Returns "12 hours since 2017-11-30 09:00:00", which parse reads back into an equal grid.
2313 2314 2315 2316 2317 |
# File 'lib/carray/time.rb', line 2313 def to_s word = WORD.fetch(@unit.base) spec = @unit.count == 1 ? word : "#{@unit.count} #{word}" @origin ? "#{spec} since #{@origin.to_time.strftime('%Y-%m-%d %H:%M:%S')}" : spec end |