Module: Ruby::Rego::Builtins::Times
- Extended by:
- RegistryHelpers
- Defined in:
- lib/ruby/rego/builtins/times.rb,
lib/ruby/rego/builtins/times/diff.rb,
lib/ruby/rego/builtins/times/parse.rb,
lib/ruby/rego/builtins/times/rfc3339.rb,
lib/ruby/rego/builtins/times/duration.rb,
lib/ruby/rego/builtins/times/go_layout.rb,
lib/ruby/rego/builtins/times/arithmetic.rb,
lib/ruby/rego/builtins/times/go_layout/parser.rb,
lib/ruby/rego/builtins/times/go_layout/scanner.rb,
lib/ruby/rego/builtins/times/go_layout/formatter.rb,
lib/ruby/rego/builtins/times/go_layout/parser/zones.rb,
lib/ruby/rego/builtins/times/go_layout/parser/fields.rb,
lib/ruby/rego/builtins/times/go_layout/parser/compose.rb,
lib/ruby/rego/builtins/times/go_layout/parser/consumers.rb
Overview
time.add_date — Go Time.AddDate calendar arithmetic with zone-aware re-anchoring. Lives apart from the time.* core so that file stays under RubyCritic’s complexity budget. Reopens Times; bare references to shared helpers/constants (operand_parts, localize, bounded, integer_value, raise_time_error, NANOS_PER_SECOND) resolve via the reopened module’s scope.
Defined Under Namespace
Modules: GoLayout
Constant Summary collapse
- INT64_MAX =
(2**63) - 1
- INT64_MIN =
-(2**63)
- NANOS_PER_SECOND =
1_000_000_000- DAY_NAMES =
Go’s Weekday.String() names, indexed by Ruby’s Time#wday (Sunday == 0); kept as an explicit table that mirrors Go’s spelling rather than deriving the name another way.
%w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday].freeze
- TIME_FUNCTIONS =
{ "time.parse_rfc3339_ns" => { arity: 1, handler: :parse_rfc3339_ns }, "time.parse_ns" => { arity: 2, handler: :parse_ns }, "time.parse_duration_ns" => { arity: 1, handler: :parse_duration_ns }, "time.now_ns" => { arity: 0, handler: :now_ns }, "time.date" => { arity: 1, handler: :date }, "time.clock" => { arity: 1, handler: :clock }, "time.weekday" => { arity: 1, handler: :weekday }, "time.diff" => { arity: 2, handler: :diff }, "time.add_date" => { arity: 4, handler: :add_date }, "time.format" => { arity: 1, handler: :format } }.freeze
- RFC3339 =
/\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|[+-]\d{2}:\d{2})\z/- DURATION_UNITS =
Nanoseconds per standard Go duration unit.
{ "ns" => 1, "us" => 1_000, "µs" => 1_000, "μs" => 1_000, "ms" => 1_000_000, "s" => NANOS_PER_SECOND, "m" => 60 * NANOS_PER_SECOND, "h" => 3600 * NANOS_PER_SECOND }.freeze
- EXTENDED_HOURS =
OPA’s extended units, expressed as a multiple of hours.
{ "d" => 24, "w" => 7 * 24, "y" => 365 * 24 }.freeze
Class Method Summary collapse
-
.add_date(value, years_value, months_value, days_value) ⇒ Integer, Ruby::Rego::UndefinedValue
Adds years/months/days (Go’s Time.AddDate) to an instant, keeping the wall clock and zone, and returns the result as nanoseconds.
-
.clock(value) ⇒ Array(Integer, Integer, Integer)
[hour, minute, second].
-
.date(value) ⇒ Array(Integer, Integer, Integer)
[year, month, day].
-
.diff(left, right) ⇒ Array(Integer, Integer, Integer, Integer, Integer, Integer)
The calendar difference between two instants as [years, months, days, hours, minutes, seconds], all non-negative — matching OPA (which uses icza/gox’s algorithm).
-
.format(value) ⇒ String, Ruby::Rego::UndefinedValue
Formats an instant (ns, [ns, tz], or [ns, tz, layout]) using a Go reference-time layout (default RFC3339Nano; a named constant or a literal layout otherwise), matching OPA.
-
.now_ns ⇒ Integer
The current wall-clock time in nanoseconds since the Unix epoch (Go’s time.Now().UnixNano()).
-
.parse_duration_ns(value) ⇒ Integer, Ruby::Rego::UndefinedValue
-
.parse_ns(layout_value, value_value) ⇒ Integer, Ruby::Rego::UndefinedValue
Epoch nanoseconds, or undefined when the value does not match the layout, a field is out of range, input is left over, or the instant falls outside the int64-nanosecond range.
-
.parse_rfc3339_ns(value) ⇒ Integer, Ruby::Rego::UndefinedValue
-
.register! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
-
.weekday(value) ⇒ String
The English weekday name.
Methods included from RegistryHelpers
Class Method Details
.add_date(value, years_value, months_value, days_value) ⇒ Integer, Ruby::Rego::UndefinedValue
Adds years/months/days (Go’s Time.AddDate) to an instant, keeping the wall clock and zone, and returns the result as nanoseconds. The shifted calendar date is normalised the way Go’s time.Date does — overflow rolls forward (Jan 31 + 1mo -> Mar 2/3), not clamped — and the wall clock is re-anchored in the operand’s zone (a DST gap/overlap is resolved exactly as Go’s time.Date does). Out of the int64 range, or a non-integer count, is undefined. :reek:LongParameterList :reek:TooManyStatements
25 26 27 28 29 30 31 32 33 34 |
# File 'lib/ruby/rego/builtins/times/arithmetic.rb', line 25 def self.add_date(value, years_value, months_value, days_value) nanos, zone = operand_parts(value, "time.add_date") years = int_arg(years_value, "time.add_date") months = int_arg(months_value, "time.add_date") days = int_arg(days_value, "time.add_date") fields = shift_date(localize(nanos, zone, "time.add_date"), years, months, days) bounded(reconstruct_ns(fields, zone, nanos % NANOS_PER_SECOND)) || UndefinedValue.new rescue RangeError UndefinedValue.new end |
.clock(value) ⇒ Array(Integer, Integer, Integer)
Returns [hour, minute, second].
80 81 82 83 |
# File 'lib/ruby/rego/builtins/times.rb', line 80 def self.clock(value) time = tz_instant(value, "time.clock") [time.hour, time.min, time.sec] end |
.date(value) ⇒ Array(Integer, Integer, Integer)
Returns [year, month, day].
74 75 76 77 |
# File 'lib/ruby/rego/builtins/times.rb', line 74 def self.date(value) time = tz_instant(value, "time.date") [time.year, time.month, time.day] end |
.diff(left, right) ⇒ Array(Integer, Integer, Integer, Integer, Integer, Integer)
The calendar difference between two instants as [years, months, days, hours, minutes, seconds], all non-negative — matching OPA (which uses icza/gox’s algorithm). Both instants are decomposed in the FIRST operand’s timezone (Go realigns the second to the first’s location), then a borrow-normalised component subtraction is taken from the earlier to the later. The second operand’s zone is still resolved (and so validated) even though the decomposition uses the first’s. :reek:TooManyStatements
23 24 25 26 27 28 29 30 31 |
# File 'lib/ruby/rego/builtins/times/diff.rb', line 23 def self.diff(left, right) left_nanos, zone = operand_parts(left, "time.diff") right_nanos, right_zone = operand_parts(right, "time.diff") in_zone(utc_instant(0), right_zone, "time.diff") # validate the 2nd zone too left_nanos, right_nanos = right_nanos, left_nanos if left_nanos > right_nanos # order earlier->later diff_components(localize(left_nanos, zone, "time.diff"), localize(right_nanos, zone, "time.diff")) rescue RangeError raise_time_error("time.diff") end |
.format(value) ⇒ String, Ruby::Rego::UndefinedValue
Formats an instant (ns, [ns, tz], or [ns, tz, layout]) using a Go reference-time layout (default RFC3339Nano; a named constant or a literal layout otherwise), matching OPA.
105 106 107 108 109 110 |
# File 'lib/ruby/rego/builtins/times.rb', line 105 def self.format(value) nanos, zone, layout = operand_parts(value, "time.format") GoLayout.format(localize(nanos, zone, "time.format"), layout) rescue RangeError UndefinedValue.new end |
.now_ns ⇒ Integer
The current wall-clock time in nanoseconds since the Unix epoch (Go’s time.Now().UnixNano()). Impure: within one evaluation OPA fixes the clock once so every call returns the same value. That per-evaluation consistency is supplied by an evaluator overlay that calls this method once at evaluation start (see Evaluator#evaluate); this base handler reads the clock fresh, serving as the single source of the timestamp computation and the value outside an evaluation.
96 97 98 99 |
# File 'lib/ruby/rego/builtins/times.rb', line 96 def self.now_ns now = ::Time.now (now.to_i * NANOS_PER_SECOND) + now.nsec end |
.parse_duration_ns(value) ⇒ Integer, Ruby::Rego::UndefinedValue
23 24 25 |
# File 'lib/ruby/rego/builtins/times/duration.rb', line 23 def self.parse_duration_ns(value) parse_extended_duration(string_arg(value, "time.parse_duration_ns")) || UndefinedValue.new end |
.parse_ns(layout_value, value_value) ⇒ Integer, Ruby::Rego::UndefinedValue
Returns epoch nanoseconds, or undefined when the value does not match the layout, a field is out of range, input is left over, or the instant falls outside the int64-nanosecond range.
18 19 20 21 22 23 |
# File 'lib/ruby/rego/builtins/times/parse.rb', line 18 def self.parse_ns(layout_value, value_value) layout = string_arg(layout_value, "time.parse_ns") value = string_arg(value_value, "time.parse_ns") # parse returns the ns Integer (0 is valid and truthy) or nil → undefined. GoLayout.parse(layout, value) || UndefinedValue.new end |
.parse_rfc3339_ns(value) ⇒ Integer, Ruby::Rego::UndefinedValue
18 19 20 21 22 |
# File 'lib/ruby/rego/builtins/times/rfc3339.rb', line 18 def self.parse_rfc3339_ns(value) match = RFC3339.match(string_arg(value, "time.parse_rfc3339_ns")) nanos = match && rfc3339_nanos(match) nanos || UndefinedValue.new end |
.register! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
64 65 66 67 68 |
# File 'lib/ruby/rego/builtins/times.rb', line 64 def self.register! registry = BuiltinRegistry.instance register_configured_functions(registry, TIME_FUNCTIONS) registry end |
.weekday(value) ⇒ String
Returns the English weekday name.
86 87 88 |
# File 'lib/ruby/rego/builtins/times.rb', line 86 def self.weekday(value) DAY_NAMES.fetch(tz_instant(value, "time.weekday").wday) end |