`num`, `math` and `time`
Three small namespaces: num for range tests and numeric bridging, math for arithmetic, time
for dates.
num.Between(val, min, max) → bool
Section titled “num.Between(val, min, max) → bool”Whether val is between min and max, inclusive. Whole numbers.
| Parameter | Type | Meaning |
|---|---|---|
val | int64 | The value to test |
min | int64 | Lower bound, inclusive |
max | int64 | Upper bound, inclusive |
rule TermWithinRange "loan term between 12 and 360 months" { when !num.Between(to.Int(bbox.Doc().Right("Loan Term")), 12, 360) then findings.Error("TermWithinRange", "underwriting", "Term outside the permitted 12–360 months");}num.BetweenF(val, min, max) → bool
Section titled “num.BetweenF(val, min, max) → bool”The same for floats, and the one to use for money, rates and ratios.
| Parameter | Type | Meaning |
|---|---|---|
val | float64 | The value to test |
min | float64 | Lower bound, inclusive |
max | float64 | Upper bound, inclusive |
rule RateWithinBand "rate must sit between 2% and 12%" { when !num.BetweenF(to.Num(bbox.Doc().Right("Interest Rate").Percent()), 2.0, 12.0) then findings.Error("RateWithinBand", "compliance", "Rate outside the 2–12% band");}num.ToInt64(v) → int64 · num.ToFloat64(v) → float64
Section titled “num.ToInt64(v) → int64 · num.ToFloat64(v) → float64”Bridge any numeric value into the type the language dispatches on.
| Parameter | Type | Meaning |
|---|---|---|
v | any | The value to convert |
For values that came from a document, prefer to.Int and
to.Num: they read currency symbols, separators and
accounting parentheses, which these do not.
Every function here takes and returns floats.
math.Abs(x) → float64
Section titled “math.Abs(x) → float64”| Parameter | Type | Meaning |
|---|---|---|
x | float64 | The value |
The tolerance idiom: compare a difference against a threshold rather than testing equality on values that were read off a page.
rule PaymentsRecomputeCorrectly "the stated total matches the sum of parts" { when math.Abs(to.Num(out.Get("statedTotal")) - to.Num(out.Get("computedTotal"))) > 0.01 then findings.Error("PaymentsRecomputeCorrectly", "consistency", strings.Sprintf("Stated total %s does not match computed %s", to.Fixed(out.Get("statedTotal"), 2), to.Fixed(out.Get("computedTotal"), 2)));}A cent of tolerance is deliberate: two figures rounded independently on the same document routinely differ in the last place, and a rule that fires on that is noise.
math.Min(a, b) → float64 · math.Max(a, b) → float64
Section titled “math.Min(a, b) → float64 · math.Max(a, b) → float64”| Parameter | Type | Meaning |
|---|---|---|
a, b | float64 | The two values |
math.Round(x, decimals) → float64
Section titled “math.Round(x, decimals) → float64”Rounds to decimals places.
| Parameter | Type | Meaning |
|---|---|---|
x | float64 | The value |
decimals | int64 | How many decimal places. A bare integer here |
Note the mixed types: x needs its decimal point, decimals must not have one.
math.Floor(x) → float64 · math.Ceil(x) → float64
Section titled “math.Floor(x) → float64 · math.Ceil(x) → float64”| Parameter | Type | Meaning |
|---|---|---|
x | float64 | The value |
math.Percent(part, whole) → float64
Section titled “math.Percent(part, whole) → float64”part as a percentage of whole.
| Parameter | Type | Meaning |
|---|---|---|
part | float64 | The portion |
whole | float64 | The total |
Returns a percentage, not a fraction: math.Percent(25.0, 100.0) is 25, not 0.25. That matches
to.Pct, which also does not scale.
rule LoanToValue "LTV above 80% requires mortgage insurance" { when math.Percent(to.Num(out.Get("loanAmount")), to.Num(out.Get("propertyValue"))) > 80.0 && !out.Bool("hasMortgageInsurance", false) then findings.Critical("LoanToValue", "underwriting", strings.Sprintf("LTV of %s%% requires mortgage insurance", to.Fixed(math.Percent(to.Num(out.Get("loanAmount")), to.Num(out.Get("propertyValue"))), 1)));}Guard the denominator where it may be zero or unread, to.IsNum on the property value first.
time.Now() → time
Section titled “time.Now() → time”The current time. Use sparingly: a rule that depends on the wall clock produces a different result on a rerun, which makes a decision hard to reproduce when someone asks why it was made. Prefer a date carried on the input.
time.ParseTime(layout, value) → time
Section titled “time.ParseTime(layout, value) → time”Parses value using layout.
| Parameter | Type | Meaning |
|---|---|---|
layout | string | The reference layout, e.g. "2006-01-02" |
value | string | The text to parse |
to.Date is usually better. It tries the profile’s layouts
in order, so it reads what documents actually contain without you naming the format.
time.FormatTime(t, layout) → string
Section titled “time.FormatTime(t, layout) → string”| Parameter | Type | Meaning |
|---|---|---|
t | time | The time |
layout | string | The output layout |
to.DateStr does the parse and the format in one call, and
returns the empty string rather than a formatted zero date on failure.
time.IsBefore(t1, t2) → bool · time.IsAfter(t1, t2) → bool
Section titled “time.IsBefore(t1, t2) → bool · time.IsAfter(t1, t2) → bool”Whether t1 is before / after t2.
| Parameter | Type | Meaning |
|---|---|---|
t1, t2 | time | The two times |
Guard both sides with to.IsDate first. An unreadable date is the zero time, and the zero time
compares as before everything, so an unguarded comparison silently passes or silently fires,
depending which side failed.
time.DaysBetween(t1, t2) → int64 · time.MonthsBetween(t1, t2) → int64 · time.YearsBetween(t1, t2) → int64
Section titled “time.DaysBetween(t1, t2) → int64 · time.MonthsBetween(t1, t2) → int64 · time.YearsBetween(t1, t2) → int64”The interval between two times.
| Parameter | Type | Meaning |
|---|---|---|
t1, t2 | time | The two times |
rule RescissionPeriod "closing must be at least three days after disclosure" { when to.IsDate(bbox.Doc().Right("Closing Date")) && to.IsDate(bbox.Doc().Right("Disclosure Date")) && time.DaysBetween(to.Date(bbox.Doc().Right("Disclosure Date")), to.Date(bbox.Doc().Right("Closing Date"))) < 3 then findings.Critical("RescissionPeriod", "compliance", "Fewer than three days between disclosure and closing");}time.AddDays(t, days) → time · time.AddMonths(t, months) → time
Section titled “time.AddDays(t, days) → time · time.AddMonths(t, months) → time”| Parameter | Type | Meaning |
|---|---|---|
t | time | The starting time |
days / months | int64 | How many to add: negative to subtract |
AddMonths clamps rather than overflowing: adding one month to 31 January gives the last day of
February, not 2 or 3 March.
rule RateLockExpiry "record when the rate lock runs out" { when to.IsDate(bbox.Doc().Right("Lock Date")) then out.Set("lockExpiry", time.FormatTime( time.AddDays(to.Date(bbox.Doc().Right("Lock Date")), 45), "2006-01-02"));}See also
Section titled “See also”to: conversion, reading numbers and dates out of textstrings, text, formatting a message around a figure- The rule language: operators and type dispatch