// Temporal component access and duration arithmetic: reading a named // component off an epoch-millis instant or a (months, days, millis) // duration, applying a duration to an instant (checked calendar add), and // the Euclidean integer-math helpers they share. Split from temporal.go, // which holds the ISO parsing and formatting.
package eval import // Component is a temporal component accessor (.year/.month/.day/.hour/... // /.epochMillis) on an epoch-millis temporal; ok is false for an unknown // component (the caller yields Null). func ( int64, string) (int64, bool) { := floorDiv(, MSPerDay) := - *MSPerDay , , := CivilFromDays() switch { case "year": return , true case "month": return int64(), true case "day": return int64(), true case "hour": return / 3_600_000, true case "minute": return ( / 60_000) % 60, true case "second": return ( / 1000) % 60, true case "millisecond": return % 1000, true case "epochMillis": return , true case "epochSeconds": return floorDiv(, 1000), true } return 0, false } // DurationComponent is a duration component accessor on a (months, days, // millis) duration, with the groups independent (Neo4j's convention): the // months group answers years/quarters/months (months is the group TOTAL, // so duration({years: 1}).months = 12), the days group weeks/days, and // the millis group hours/minutes/seconds/milliseconds (each the group // total at that unit, so PT2H reads .minutes = 120). ok=false for an // unknown component. func (, , int64, string) (int64, bool) { switch { case "years": return / 12, true case "quarters": return / 3, true case "months": return , true case "weeks": return / 7, true case "days": return , true case "hours": return / 3_600_000, true case "minutes": return / 60_000, true case "seconds": return / 1000, true case "milliseconds": return , true } return 0, false } // ApplyDuration applies a duration (months, days, millis) to an // epoch-millis temporal, sign = +1 (add) or -1 (subtract). Months are a // calendar add (day clamped to the target month length, e.g. Jan 31 + 1 // month = Feb 28); days and millis are absolute. Every step is checked: // ok=false on overflow, so the caller yields Null rather than a wrapped // nonsense instant. Note the boundary -- shifting a recent instant by // i64-scale milliseconds to a very negative-but-representable value is exact // and stays ok; only a genuine overflow declines. func (, , , , int64) (int64, bool) { // A months-free duration is pure tick arithmetic: with months == 0 the // civil round-trip below is the identity (no month carry, no day // clamp), so the result reduces to a single shifted addition. if == 0 { , := mulChk(, MSPerDay) if ! { return 0, false } if , = addChk(, ); ! { return 0, false } if , = mulChk(, ); ! { return 0, false } return addChk(, ) } := floorDiv(, MSPerDay) := - *MSPerDay , , := CivilFromDays() , := mulChk(, ) if ! { return 0, false } , := addChk(int64()-1, ) if ! { return 0, false } // y0 is bounded (a representable instant) and floorDiv(total, 12) fits, // so this sum cannot overflow; the range guard below rejects an absurd // year before DaysFromCivil's era term can overflow. := + floorDiv(, 12) if < -maxCivilYear || > maxCivilYear { return 0, false } := uint32(floorMod(, 12) + 1) := if := DaysInMonth(, ); > { = } , := mulChk(, ) if ! { return 0, false } , := addChk(DaysFromCivil(, , ), ) if ! { return 0, false } , := mulChk(, MSPerDay) if ! { return 0, false } if , = addChk(, ); ! { return 0, false } , := mulChk(, ) if ! { return 0, false } return addChk(, ) } // floorDiv is Euclidean-style division rounding toward negative infinity // (Rust's div_euclid for positive divisors). func floorDiv(, int64) int64 { := / if % != 0 && ( < 0) != ( < 0) { -- } return } // floorMod is the non-negative remainder matching floorDiv. func floorMod(, int64) int64 { return - floorDiv(, )* } // parseI64 parses a decimal integer with optional sign and surrounding // spaces, comma-ok. func parseI64( string) (int64, bool) { = strings.TrimSpace() if == "" { return 0, false } := false if [0] == '+' || [0] == '-' { = [0] == '-' = [1:] if == "" { return 0, false } } var int64 for := 0; < len(); ++ { := [] if < '0' || > '9' { return 0, false } = *10 + int64(-'0') } if { = - } return , true }