github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/duration/duration.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package duration 12 13 import ( 14 "bytes" 15 "fmt" 16 "math" 17 "math/big" 18 "strings" 19 "time" 20 "unicode" 21 22 "github.com/cockroachdb/apd/v3" 23 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgcode" 24 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgerror" 25 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/types" 26 "github.com/cockroachdb/cockroachdb-parser/pkg/util/arith" 27 "github.com/cockroachdb/errors" 28 ) 29 30 const ( 31 // MicrosPerMilli is the amount of microseconds in a millisecond. 32 MicrosPerMilli = 1000 33 // MillisPerSec is the amount of milliseconds in a second. 34 MillisPerSec = 1000 35 // MicrosPerSec is the amount of microseconds in a second. 36 MicrosPerSec = MicrosPerMilli * MillisPerSec 37 // SecsPerMinute is the amount of seconds in a minute. 38 SecsPerMinute = 60 39 // SecsPerHour is the amount of seconds in an hour. 40 SecsPerHour = 3600 41 // SecsPerDay is the amount of seconds in a day. 42 SecsPerDay = 86400 43 // MinsPerHour is the amount of minutes in an hour. 44 MinsPerHour = 60 45 // HoursPerDay is the number of hours in a day. 46 HoursPerDay = 24 47 // DaysPerMonth is the assumed amount of days in a month. 48 // is always evaluated to 30, as it is in postgres. 49 DaysPerMonth = 30 50 // DaysPerYear is the number of days in a year. 51 // It is assumed to include a quarter day to account for the leap year. 52 // Matches DAYS_PER_YEAR in postgres. 53 DaysPerYear = 365.25 54 // MonthsPerYear is the amount of months in the year. 55 MonthsPerYear = 12 56 ) 57 58 const ( 59 nanosInDay = 24 * int64(time.Hour) // Try as I might, couldn't do this without the cast. 60 nanosInMonth = DaysPerMonth * nanosInDay 61 nanosInSecond = 1000 * 1000 * 1000 62 nanosInMicro = 1000 63 ) 64 65 var ( 66 bigDaysInMonth = apd.NewBigInt(DaysPerMonth) 67 bigNanosInDay = apd.NewBigInt(nanosInDay) 68 bigNanosInMonth = apd.NewBigInt(nanosInMonth) 69 ) 70 71 // errEncodeOverflow is returned by Encode when the sortNanos returned would 72 // have overflowed or underflowed. 73 var errEncodeOverflow = pgerror.WithCandidateCode(errors.New("overflow during Encode"), pgcode.IntervalFieldOverflow) 74 75 // A Duration represents a length of time. 76 // 77 // A duration of "1 month" cannot be represented as a fixed number of 78 // nanoseconds because the length of months vary. The same is true for days 79 // because of leap seconds. Given a begin or end time to anchor a duration, the 80 // nanosecond count can be calculated, but it's useful to represent durations 81 // such as "1 year 3 months" without an anchor. Duration allows this. 82 // 83 // For the purposes of Compare and Encode, 1 month is considered equivalent to 84 // 30 days and 1 day is equivalent to 24 * 60 * 60 * 1E9 nanoseconds. 85 // 86 // Although the Nanos field is a number of nanoseconds, all operations 87 // round to the nearest microsecond. Any setting of this field should avoid 88 // setting with precision below microseconds. The only exceptions are the 89 // encode/decode operations. 90 // 91 // TODO(dan): Until the overflow and underflow handling is fixed, this is only 92 // useful for durations of < 292 years. 93 type Duration struct { 94 Months int64 95 Days int64 96 // nanos is an unexported field so that it cannot be misused by other 97 // packages. It should almost always be rounded to the nearest microsecond. 98 nanos int64 99 } 100 101 // MakeDuration returns a Duration rounded to the nearest microsecond. 102 func MakeDuration(nanos, days, months int64) Duration { 103 return Duration{ 104 Months: months, 105 Days: days, 106 nanos: rounded(nanos), 107 } 108 } 109 110 // MakeDurationJustifyHours returns a duration where hours are moved 111 // to days if the number of hours exceeds 24. 112 func MakeDurationJustifyHours(nanos, days, months int64) Duration { 113 const nanosPerDay = int64(HoursPerDay * time.Hour) 114 extraDays := nanos / nanosPerDay 115 days += extraDays 116 nanos -= extraDays * nanosPerDay 117 return Duration{ 118 Months: months, 119 Days: days, 120 nanos: rounded(nanos), 121 } 122 } 123 124 // Age returns a Duration rounded to the nearest microsecond 125 // from the time difference of (lhs - rhs). 126 // 127 // Note that we cannot use time.Time's sub, as time.Duration does not give 128 // an accurate picture of day/month differences. 129 // 130 // This is lifted from Postgres' timestamptz_age. The following comment applies: 131 // Note that this does not result in an accurate absolute time span 132 // since year and month are out of context once the arithmetic 133 // is done. 134 func Age(lhs, rhs time.Time) Duration { 135 // Strictly compare only UTC time. 136 lhs = lhs.UTC() 137 rhs = rhs.UTC() 138 139 years := int64(lhs.Year() - rhs.Year()) 140 months := int64(lhs.Month() - rhs.Month()) 141 days := int64(lhs.Day() - rhs.Day()) 142 hours := int64(lhs.Hour() - rhs.Hour()) 143 minutes := int64(lhs.Minute() - rhs.Minute()) 144 seconds := int64(lhs.Second() - rhs.Second()) 145 nanos := int64(lhs.Nanosecond() - rhs.Nanosecond()) 146 147 flip := func() { 148 years = -years 149 months = -months 150 days = -days 151 hours = -hours 152 minutes = -minutes 153 seconds = -seconds 154 nanos = -nanos 155 } 156 157 // Flip signs so we're always operating from a positive. 158 if rhs.After(lhs) { 159 flip() 160 } 161 162 // For each field that is now negative, promote them to positive. 163 // We could probably use smarter math here, but to keep things simple and postgres-esque, 164 // we'll do the same way postgres does. We do not expect these overflow values 165 // to be too large from the math above anyway. 166 for nanos < 0 { 167 nanos += int64(time.Second) 168 seconds-- 169 } 170 for seconds < 0 { 171 seconds += SecsPerMinute 172 minutes-- 173 } 174 for minutes < 0 { 175 minutes += MinsPerHour 176 hours-- 177 } 178 for hours < 0 { 179 hours += HoursPerDay 180 days-- 181 } 182 for days < 0 { 183 // Get days in month preceding the current month of whichever is greater. 184 if rhs.After(lhs) { 185 days += daysInCurrentMonth(lhs) 186 } else { 187 days += daysInCurrentMonth(rhs) 188 } 189 months-- 190 } 191 for months < 0 { 192 months += MonthsPerYear 193 years-- 194 } 195 196 // Revert the sign back. 197 if rhs.After(lhs) { 198 flip() 199 } 200 201 return Duration{ 202 Months: years*MonthsPerYear + months, 203 Days: days, 204 nanos: rounded( 205 nanos + 206 int64(time.Second)*seconds + 207 int64(time.Minute)*minutes + 208 int64(time.Hour)*hours, 209 ), 210 } 211 } 212 213 func daysInCurrentMonth(t time.Time) int64 { 214 // Take the first day of the month, add a month and subtract a day. 215 // This returns the last day of the month, which the number of days in the month. 216 return int64(time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, -1).Day()) 217 } 218 219 // DecodeDuration returns a Duration without rounding nanos. 220 func DecodeDuration(months, days, nanos int64) Duration { 221 return Duration{ 222 Months: months, 223 Days: days, 224 nanos: nanos, 225 } 226 } 227 228 // Nanos returns the nanos of d. 229 func (d Duration) Nanos() int64 { 230 return d.nanos 231 } 232 233 // SetNanos rounds and sets nanos. 234 func (d *Duration) SetNanos(nanos int64) { 235 d.nanos = rounded(nanos) 236 } 237 238 // round rounds nanos to the nearest microsecond. 239 func (d Duration) round() Duration { 240 d.nanos = rounded(d.nanos) 241 return d 242 } 243 244 // rounded returns nanos rounded to the nearest microsecond. 245 func (d Duration) rounded() int64 { 246 return rounded(d.nanos) 247 } 248 249 // rounded returns nanos rounded to the nearest microsecond. 250 func rounded(nanos int64) int64 { 251 dur := time.Duration(nanos) * time.Nanosecond 252 v := dur.Round(time.Microsecond).Nanoseconds() 253 // Near the boundaries of int64 will return the argument unchanged. Check 254 // for those cases and truncate instead of round so that we never have nanos. 255 if m := v % nanosInMicro; m != 0 { 256 v -= m 257 } 258 return v 259 } 260 261 // Compare returns an integer representing the relative length of two Durations. 262 // The result will be 0 if d==x, -1 if d < x, and +1 if d > x. 263 func (d Duration) Compare(x Duration) int { 264 normD := d.normalize() 265 normX := x.normalize() 266 if normD.Months < normX.Months { 267 return -1 268 } else if normD.Months > normX.Months { 269 return 1 270 } else if normD.Days < normX.Days { 271 return -1 272 } else if normD.Days > normX.Days { 273 return 1 274 } else if normD.nanos < normX.nanos { 275 return -1 276 } else if normD.nanos > normX.nanos { 277 return 1 278 } 279 return 0 280 } 281 282 // FromInt64 converts an int64 number of seconds to a 283 // duration. Inverse conversion of AsInt64. 284 func FromInt64(x int64) Duration { 285 days := x / (nanosInDay / nanosInSecond) 286 seconds := x % (nanosInDay / nanosInSecond) 287 d := Duration{Days: days, nanos: seconds * nanosInSecond} 288 return d.normalize() 289 } 290 291 // FromFloat64 converts a float64 number of seconds to a duration. Inverse 292 // conversion of AsFloat64. 293 func FromFloat64(x float64) Duration { 294 months := int64(x / float64(nanosInMonth/nanosInSecond)) 295 secDays := math.Mod(x, float64(nanosInMonth/nanosInSecond)) 296 days := int64(secDays / float64(nanosInDay/nanosInSecond)) 297 secsRem := math.Mod(secDays, float64(nanosInDay/nanosInSecond)) 298 d := Duration{Months: months, Days: days, nanos: int64(secsRem * 1e9)} 299 return d.normalize().round() 300 } 301 302 // FromBigInt converts an apd.BigInt number of nanoseconds to a duration. Inverse 303 // conversion of AsBigInt. Boolean false if the result overflows. 304 func FromBigInt(src *apd.BigInt) (Duration, bool) { 305 var rem apd.BigInt 306 var monthsDec apd.BigInt 307 monthsDec.QuoRem(src, bigNanosInMonth, &rem) 308 if !monthsDec.IsInt64() { 309 return Duration{}, false 310 } 311 312 var daysDec apd.BigInt 313 var nanosRem apd.BigInt 314 daysDec.QuoRem(&rem, bigNanosInDay, &nanosRem) 315 // Note: we do not need to check for overflow of daysDec because any 316 // excess bits were spilled into months above already. 317 318 d := Duration{Months: monthsDec.Int64(), Days: daysDec.Int64(), nanos: nanosRem.Int64()} 319 return d.normalize().round(), true 320 } 321 322 // AsInt64 converts a duration to an int64 number of seconds. 323 // The conversion may overflow, in which case the boolean return 324 // value is false. 325 func (d Duration) AsInt64() (int64, bool) { 326 numYears := d.Months / 12 327 numMonthsInYear := d.Months % 12 328 // To do overflow detection with years, we have to convert to float in order 329 // to maintain accuracy, as DaysPerYear is a floating point number. 330 ySecs := float64(numYears*SecsPerDay) * DaysPerYear 331 // Since float has a higher range than int, fail if the number of seconds in the year 332 // value is greater than what an int can handle. 333 if ySecs > float64(math.MaxInt64) || ySecs < float64(math.MinInt64) { 334 return 0, false 335 } 336 mSecs, ok := arith.MulHalfPositiveWithOverflow(numMonthsInYear, nanosInMonth/nanosInSecond) 337 if !ok { 338 return 0, ok 339 } 340 dSecs, ok := arith.MulHalfPositiveWithOverflow(d.Days, nanosInDay/nanosInSecond) 341 if !ok { 342 return 0, ok 343 } 344 if dSecs, ok = arith.AddWithOverflow(mSecs, dSecs); !ok { 345 return 0, ok 346 } 347 if dSecs, ok = arith.AddWithOverflow(dSecs, int64(ySecs)); !ok { 348 return 0, ok 349 } 350 return arith.AddWithOverflow(dSecs, d.nanos/nanosInSecond) 351 } 352 353 // AsFloat64 converts a duration to a float64 number of seconds. 354 func (d Duration) AsFloat64() float64 { 355 numYears := d.Months / 12 356 numMonthsInYear := d.Months % 12 357 return (float64(d.Nanos()) / float64(time.Second)) + 358 float64(d.Days*SecsPerDay) + 359 float64(numYears*SecsPerDay)*DaysPerYear + 360 float64(numMonthsInYear*DaysPerMonth*SecsPerDay) 361 } 362 363 // AsBigInt converts a duration to an apd.BigInt with the number of nanoseconds. 364 func (d Duration) AsBigInt(dst *apd.BigInt) { 365 dst.SetInt64(d.Months) 366 dst.Mul(dst, bigDaysInMonth) 367 dst.Add(dst, apd.NewBigInt(d.Days)) 368 dst.Mul(dst, bigNanosInDay) 369 // Uses rounded instead of nanos here to remove any on-disk nanos. 370 dst.Add(dst, apd.NewBigInt(d.rounded())) 371 } 372 373 const ( 374 hourNanos = uint64(time.Hour / time.Nanosecond) 375 minuteNanos = uint64(time.Minute / time.Nanosecond) 376 secondNanos = uint64(time.Second / time.Nanosecond) 377 ) 378 379 // Format emits a string representation of a Duration to a Buffer truncated to microseconds. 380 func (d Duration) Format(buf *bytes.Buffer) { 381 d.FormatWithStyle(buf, IntervalStyle_POSTGRES) 382 } 383 384 // FormatWithStyle emits a string representation of a Duration to a Buffer 385 // truncated to microseconds with a given style. 386 func (d Duration) FormatWithStyle(buf *bytes.Buffer, style IntervalStyle) { 387 switch style { 388 case IntervalStyle_POSTGRES: 389 d.encodePostgres(buf) 390 case IntervalStyle_ISO_8601: 391 d.encodeISO8601(buf) 392 case IntervalStyle_SQL_STANDARD: 393 d.encodeSQLStandard(buf) 394 default: 395 d.encodePostgres(buf) 396 } 397 } 398 399 func (d Duration) encodePostgres(buf *bytes.Buffer) { 400 if d.nanos == 0 && d.Days == 0 && d.Months == 0 { 401 buf.WriteString("00:00:00") 402 return 403 } 404 405 wrote := false 406 // Defining both arguments in the signature gives a 10% speedup. 407 wrotePrev := func(wrote bool, buf *bytes.Buffer) bool { 408 if wrote { 409 buf.WriteString(" ") 410 } 411 return true 412 } 413 414 months := d.Months 415 if absGE(months, 12) { 416 years := months / 12 417 wrote = wrotePrev(wrote, buf) 418 fmt.Fprintf(buf, "%d year%s", years, isPlural(years)) 419 months %= 12 420 } 421 if months != 0 { 422 wrote = wrotePrev(wrote, buf) 423 fmt.Fprintf(buf, "%d mon%s", months, isPlural(months)) 424 } 425 426 // Keep track of whether the previous unit was negative. 427 // If so, and the next one is positive, add `+` in front of the positive unit. 428 wasNegative := d.Months < 0 429 if d.Days != 0 { 430 wrote = wrotePrev(wrote, buf) 431 if wasNegative && d.Days > 0 { 432 buf.WriteString("+") 433 } 434 fmt.Fprintf(buf, "%d day%s", d.Days, isPlural(d.Days)) 435 wasNegative = d.Days < 0 436 } 437 438 if d.nanos == 0 { 439 return 440 } 441 442 wrotePrev(wrote, buf) 443 444 if d.nanos/nanosInMicro < 0 { 445 buf.WriteString("-") 446 } else if wasNegative { 447 buf.WriteString("+") 448 } 449 450 hours, minutes, seconds, micros := extractAbsTime(d.nanos) 451 fmt.Fprintf(buf, "%02d:%02d:%02d", hours, minutes, seconds) 452 453 if micros != 0 { 454 s := fmt.Sprintf(".%06d", micros) 455 buf.WriteString(strings.TrimRight(s, "0")) 456 } 457 } 458 459 func (d Duration) encodeSQLStandard(buf *bytes.Buffer) { 460 hasNegative := d.Months < 0 || d.Days < 0 || d.nanos < 0 461 hasPositive := d.Months > 0 || d.Days > 0 || d.nanos > 0 462 hasYearMonth := d.Months != 0 463 hasDayTime := d.Days != 0 || d.nanos != 0 464 hasDay := d.Days != 0 465 466 // A SQL Standard value always has the same sign, and must only 467 // have Year-Month XOR (Days and/or Time) values. 468 sqlStandardValue := !(hasNegative && hasPositive) && 469 !(hasYearMonth && hasDayTime) 470 471 var years, months, days int64 472 hours, minutes, seconds, micros := extractTime(d.nanos) 473 if absGE(d.Months, 12) { 474 years = d.Months / 12 475 d.Months %= 12 476 } 477 months = d.Months 478 days = d.Days 479 480 if hasNegative && sqlStandardValue { 481 buf.WriteByte('-') 482 years = -years 483 months = -months 484 days = -days 485 hours = -hours 486 minutes = -minutes 487 seconds = -seconds 488 micros = -micros 489 } 490 491 switch { 492 case !hasNegative && !hasPositive: 493 buf.WriteByte('0') 494 case !sqlStandardValue: 495 yearSign := '+' 496 if years < 0 || months < 0 { 497 yearSign = '-' 498 years = -years 499 months = -months 500 } 501 daySign := '+' 502 if days < 0 { 503 daySign = '-' 504 days = -days 505 } 506 secSign := '+' 507 if d.nanos < 0 { 508 secSign = '-' 509 hours = -hours 510 minutes = -minutes 511 seconds = -seconds 512 micros = -micros 513 } 514 needSpace := false 515 if hasYearMonth { 516 fmt.Fprintf( 517 buf, 518 "%c%d-%d", 519 yearSign, years, months, 520 ) 521 needSpace = true 522 } 523 if hasDayTime { 524 if needSpace { 525 buf.WriteString(" ") 526 } 527 fmt.Fprintf( 528 buf, 529 "%c%d %c%d:%02d:", 530 daySign, days, 531 secSign, hours, minutes, 532 ) 533 writeSecondsMicroseconds(buf, seconds, micros) 534 } 535 case hasYearMonth: 536 fmt.Fprintf(buf, "%d-%d", years, months) 537 case hasDay: 538 fmt.Fprintf(buf, "%d %d:%02d:", days, hours, minutes) 539 writeSecondsMicroseconds(buf, seconds, micros) 540 default: 541 fmt.Fprintf(buf, "%d:%02d:", hours, minutes) 542 writeSecondsMicroseconds(buf, seconds, micros) 543 } 544 } 545 546 func (d Duration) encodeISO8601(buf *bytes.Buffer) { 547 if d.nanos == 0 && d.Days == 0 && d.Months == 0 { 548 buf.WriteString("PT0S") 549 return 550 } 551 552 buf.WriteByte('P') 553 years := d.Months / 12 554 writeISO8601IntPart(buf, years, 'Y') 555 d.Months %= 12 556 557 writeISO8601IntPart(buf, d.Months, 'M') 558 writeISO8601IntPart(buf, d.Days, 'D') 559 if d.nanos != 0 { 560 buf.WriteByte('T') 561 } 562 563 hnAbs, mnAbs, snAbs, microsAbs := extractAbsTime(d.nanos) 564 hours := int64(hnAbs) 565 minutes := int64(mnAbs) 566 seconds := int64(snAbs) 567 microsSign := "" 568 569 if d.nanos < 0 { 570 hours = -hours 571 minutes = -minutes 572 seconds = -seconds 573 microsSign = "-" 574 } 575 writeISO8601IntPart(buf, hours, 'H') 576 writeISO8601IntPart(buf, minutes, 'M') 577 578 if microsAbs != 0 { 579 // the sign is captured by microsSign, hence using abs values here. 580 s := fmt.Sprintf("%s%d.%06d", microsSign, snAbs, microsAbs) 581 trimmed := strings.TrimRight(s, "0") 582 fmt.Fprintf(buf, "%sS", trimmed) 583 } else { 584 writeISO8601IntPart(buf, seconds, 'S') 585 } 586 } 587 588 // Return an ISO-8601-style interval field, but only if value isn't zero. 589 func writeISO8601IntPart(buf *bytes.Buffer, value int64, units rune) { 590 if value == 0 { 591 return 592 } 593 fmt.Fprintf(buf, "%d%c", value, units) 594 } 595 596 func writeSecondsMicroseconds(buf *bytes.Buffer, seconds, micros int64) { 597 fmt.Fprintf(buf, "%02d", seconds) 598 599 if micros != 0 { 600 s := fmt.Sprintf(".%06d", micros) 601 buf.WriteString(strings.TrimRight(s, "0")) 602 } 603 } 604 605 // extractAbsTime returns positive amount of hours, minutes, seconds, 606 // and microseconds contained in the given amount of nanoseconds. 607 func extractAbsTime(nanosOrig int64) (hours, minutes, seconds, micros uint64) { 608 // Extract abs(d.nanos). See https://play.golang.org/p/U3_gNMpyUew. 609 var nanos uint64 610 if nanosOrig >= 0 { 611 nanos = uint64(nanosOrig) 612 } else { 613 nanos = uint64(-nanosOrig) 614 } 615 616 hours = nanos / hourNanos 617 nanos %= hourNanos 618 minutes = nanos / minuteNanos 619 nanos %= minuteNanos 620 seconds = nanos / secondNanos 621 nanos %= secondNanos 622 micros = nanos / nanosInMicro 623 return 624 } 625 626 // extractTime returns signed amount of hours, minutes, seconds, 627 // and microseconds contained in the given amount of nanoseconds. 628 func extractTime(nanosOrig int64) (hours, minutes, seconds, micros int64) { 629 hnAbs, mnAbs, snAbs, microsAbs := extractAbsTime(nanosOrig) 630 hours = int64(hnAbs) 631 minutes = int64(mnAbs) 632 seconds = int64(snAbs) 633 micros = int64(microsAbs) 634 if nanosOrig < 0 { 635 hours = -hours 636 minutes = -minutes 637 seconds = -seconds 638 micros = -micros 639 } 640 return 641 } 642 643 func isPlural(i int64) string { 644 if i == 1 { 645 return "" 646 } 647 return "s" 648 } 649 650 // absGE returns whether x is greater than or equal to y in magnitude. 651 // y is always positive, x may be negative. 652 func absGE(x, y int64) bool { 653 if x < 0 { 654 return x <= -y 655 } 656 return x >= y 657 } 658 659 // String returns a string representation of a Duration. 660 func (d Duration) String() string { 661 var buf bytes.Buffer 662 d.Format(&buf) 663 return buf.String() 664 } 665 666 // ISO8601String returns an ISO 8601 representation ('P1Y2M3DT4H') of a Duration. 667 func (d Duration) ISO8601String() string { 668 var buf bytes.Buffer 669 d.FormatWithStyle(&buf, IntervalStyle_ISO_8601) 670 return buf.String() 671 } 672 673 // StringNanos returns a string representation of a Duration including 674 // its hidden nanoseconds value. To be used only by the encoding/decoding 675 // packages for pretty printing of on-disk values. The encoded value is 676 // expected to be in "postgres" interval style format. 677 func (d Duration) StringNanos() string { 678 var buf bytes.Buffer 679 d.encodePostgres(&buf) 680 nanos := d.nanos % nanosInMicro 681 if nanos != 0 { 682 fmt.Fprintf(&buf, "%+dns", nanos) 683 } 684 return buf.String() 685 } 686 687 // Encode returns three integers such that the original Duration is recoverable 688 // (using Decode) and the first int will approximately sort a collection of 689 // encoded Durations. 690 func (d Duration) Encode() (sortNanos int64, months int64, days int64, err error) { 691 // Calculate the total nanoseconds while checking for int64 overflow as: 692 // 693 // totalNanos = d.Months*nanosInMonth + d.Days*nanosInDay * d.nanos 694 // 695 monthNanos, ok := arith.MulHalfPositiveWithOverflow(d.Months, nanosInMonth) 696 if !ok { 697 return 0, 0, 0, errEncodeOverflow 698 } 699 dayNanos, ok := arith.MulHalfPositiveWithOverflow(d.Days, nanosInDay) 700 if !ok { 701 return 0, 0, 0, errEncodeOverflow 702 } 703 totalNanos, ok := arith.AddWithOverflow(monthNanos, dayNanos) 704 if !ok { 705 return 0, 0, 0, errEncodeOverflow 706 } 707 totalNanos, ok = arith.AddWithOverflow(totalNanos, d.nanos) 708 if !ok { 709 return 0, 0, 0, errEncodeOverflow 710 } 711 return totalNanos, d.Months, d.Days, nil 712 } 713 714 // EncodeBigInt is the same as Encode, except that it always returns 715 // successfully and is slower. 716 func (d Duration) EncodeBigInt() (sortNanos *big.Int, months int64, days int64) { 717 bigMonths := big.NewInt(d.Months) 718 bigMonths.Mul(bigMonths, big.NewInt(nanosInMonth)) 719 bigDays := big.NewInt(d.Days) 720 bigDays.Mul(bigDays, big.NewInt(nanosInDay)) 721 totalNanos := big.NewInt(d.nanos) 722 totalNanos.Add(totalNanos, bigMonths).Add(totalNanos, bigDays) 723 return totalNanos, d.Months, d.Days 724 } 725 726 // Decode reverses the three integers returned from Encode and produces an equal 727 // Duration to the original. 728 func Decode(sortNanos int64, months int64, days int64) (Duration, error) { 729 nanos := sortNanos - months*nanosInMonth - days*nanosInDay 730 // TODO(dan): Handle underflow, then document that DecodeBigInt can be used 731 // in underflow cases. 732 return Duration{Months: months, Days: days, nanos: nanos}, nil 733 } 734 735 // TODO(dan): Write DecodeBigInt. 736 737 // Add returns the time t+d, using a configurable mode. 738 func Add(t time.Time, d Duration) time.Time { 739 // Fast path adding units < 1 day. 740 // Avoiding AddDate(0,0,0) is required to prevent changing times 741 // on DST boundaries. 742 // For example, 2020-10-2503:00+03 and 2020-10-25 03:00+02 are both 743 // valid times, but `time.Date` in `time.AddDate` may translate 744 // one to the other. 745 if d.Months == 0 && d.Days == 0 { 746 return t.Add(time.Duration(d.nanos)) 747 } 748 // We can fast-path if the duration is always a fixed amount of time, 749 // or if the day number that we're starting from can never result 750 // in normalization. 751 if d.Months == 0 || t.Day() <= 28 { 752 return t.AddDate(0, int(d.Months), int(d.Days)).Add(time.Duration(d.nanos)) 753 } 754 755 // Adjustments for 1-based math. 756 expectedMonth := time.Month((int(t.Month())-1+int(d.Months))%MonthsPerYear) + 1 757 // If we have a negative duration, we have a negative modulus. 758 // Push it back up to the positive expectedMonth. 759 if expectedMonth <= 0 { 760 expectedMonth += MonthsPerYear 761 } 762 763 // Use AddDate() to get a rough value. This might overshoot the 764 // end of the expected month by multiple days. We could iteratively 765 // subtract a day until we jump a month backwards, but that's 766 // at least twice as slow as computing the correct value ourselves. 767 res := t.AddDate(0 /* years */, int(d.Months), 0 /* days */) 768 769 // Unpack fields as little as possible. 770 year, month, _ := res.Date() 771 hour, min, sec := res.Clock() 772 773 if month != expectedMonth { 774 // Pro-tip: Count across your knuckles and the divots between 775 // them, wrapping around when you hit July. Knuckle == 31 days. 776 var lastDayOfMonth int 777 switch expectedMonth { 778 case time.February: 779 // Leap year if divisible by 4, but not centuries unless also divisible by 400. 780 // Adjust the earth's orbital parameters? 781 if year%4 == 0 && (year%100 != 0 || year%400 == 0) { 782 lastDayOfMonth = 29 783 } else { 784 lastDayOfMonth = 28 785 } 786 case time.January, time.March, time.May, time.July, time.August, time.October, time.December: 787 lastDayOfMonth = 31 788 default: 789 lastDayOfMonth = 30 790 } 791 792 res = time.Date( 793 year, expectedMonth, lastDayOfMonth, 794 hour, min, sec, 795 res.Nanosecond(), res.Location()) 796 } 797 798 return res.AddDate(0, 0, int(d.Days)).Add(time.Duration(d.nanos)) 799 } 800 801 // Add returns a Duration representing a time length of d+x. 802 func (d Duration) Add(x Duration) Duration { 803 return MakeDuration(d.nanos+x.nanos, d.Days+x.Days, d.Months+x.Months) 804 } 805 806 // Sub returns a Duration representing a time length of d-x. 807 func (d Duration) Sub(x Duration) Duration { 808 return MakeDuration(d.nanos-x.nanos, d.Days-x.Days, d.Months-x.Months) 809 } 810 811 // Mul returns a Duration representing a time length of d*x. 812 func (d Duration) Mul(x int64) Duration { 813 return MakeDuration(d.nanos*x, d.Days*x, d.Months*x) 814 } 815 816 // Div returns a Duration representing a time length of d/x. 817 func (d Duration) Div(x int64) Duration { 818 return d.DivFloat(float64(x)) 819 } 820 821 // MulFloat returns a Duration representing a time length of d*x. 822 func (d Duration) MulFloat(x float64) Duration { 823 monthInt, monthFrac := math.Modf(float64(d.Months) * x) 824 dayInt, dayFrac := math.Modf((float64(d.Days) * x) + (monthFrac * DaysPerMonth)) 825 826 return MakeDuration( 827 int64((float64(d.nanos)*x)+(dayFrac*float64(nanosInDay))), 828 int64(dayInt), 829 int64(monthInt), 830 ) 831 } 832 833 // DivFloat returns a Duration representing a time length of d/x. 834 func (d Duration) DivFloat(x float64) Duration { 835 // In order to keep it compatible with PostgreSQL, we use the same logic. 836 // Refer to https://github.com/postgres/postgres/blob/e56bce5d43789cce95d099554ae9593ada92b3b7/src/backend/utils/adt/timestamp.c#L3266-L3304. 837 month := int32(float64(d.Months) / x) 838 day := int32(float64(d.Days) / x) 839 840 remainderDays := (float64(d.Months)/x - float64(month)) * DaysPerMonth 841 remainderDays = secRoundToEven(remainderDays) 842 secRemainder := (float64(d.Days)/x - float64(day) + 843 remainderDays - float64(int64(remainderDays))) * SecsPerDay 844 secRemainder = secRoundToEven(secRemainder) 845 if math.Abs(secRemainder) >= SecsPerDay { 846 day += int32(secRemainder / SecsPerDay) 847 secRemainder -= float64(int32(secRemainder/SecsPerDay) * SecsPerDay) 848 } 849 day += int32(remainderDays) 850 microSecs := float64(time.Duration(d.nanos).Microseconds())/x + secRemainder*MicrosPerMilli*MillisPerSec 851 retNanos := time.Duration(int64(math.RoundToEven(microSecs))) * time.Microsecond 852 853 return MakeDuration( 854 retNanos.Nanoseconds(), 855 int64(day), 856 int64(month), 857 ) 858 } 859 860 // secRoundToEven rounds the given float to the nearest second, 861 // assuming the input float is a microsecond representation of 862 // time 863 // This maps to the TSROUND macro in Postgres. 864 func secRoundToEven(f float64) float64 { 865 return math.RoundToEven(f*MicrosPerMilli*MillisPerSec) / (MicrosPerMilli * MillisPerSec) 866 } 867 868 // normalized returns a new Duration transformed using the equivalence rules. 869 // Each quantity of days greater than the threshold is moved into months, 870 // likewise for nanos. Integer overflow is avoided by partial transformation. 871 func (d Duration) normalize() Duration { 872 if d.Days > 0 { 873 d = d.shiftPosDaysToMonths() 874 } else if d.Days < 0 { 875 d = d.shiftNegDaysToMonths() 876 } 877 // After shifting days into months, there are two cases: 878 // - Months did not hit MaxInt64 or MinInt64, in which case Days is now in 879 // (-30,30). We shift nanos, then days one more time in case the nano shift 880 // made a full month. 881 // - Months did hit MaxInt64 or MinInt64, in which case there can be no more 882 // months. We only need to shift nanos. 883 if d.nanos > 0 { 884 d = d.shiftPosNanosToDays() 885 d = d.shiftPosDaysToMonths() 886 } else if d.nanos < 0 { 887 d = d.shiftNegNanosToDays() 888 d = d.shiftNegDaysToMonths() 889 } 890 return d 891 } 892 893 func (d Duration) shiftPosDaysToMonths() Duration { 894 var maxMonths = int64(math.MaxInt64) 895 if d.Months > 0 { 896 // If d.Months < 0, then this would overflow, but because of the exchange 897 // rate, we can never transfer more than math.MaxInt64 anyway. 898 maxMonths = math.MaxInt64 - d.Months 899 } 900 monthsFromDays := int64Min(d.Days/DaysPerMonth, maxMonths) 901 d.Months += monthsFromDays 902 d.Days -= monthsFromDays * DaysPerMonth 903 return d 904 } 905 906 func (d Duration) shiftPosNanosToDays() Duration { 907 var maxDays = int64(math.MaxInt64) 908 if d.Days > 0 { 909 // If d.Days < 0, then this would overflow, but because of the exchange 910 // rate, we can never transfer more than math.MaxInt64 anyway. 911 maxDays = math.MaxInt64 - d.Days 912 } 913 daysFromNanos := int64Min(d.nanos/nanosInDay, maxDays) 914 d.Days += daysFromNanos 915 d.nanos -= daysFromNanos * nanosInDay 916 return d 917 } 918 919 func (d Duration) shiftNegDaysToMonths() Duration { 920 var minMonths = int64(math.MinInt64) 921 if d.Months < 0 { 922 // If d.Months > 0, then this would overflow, but because of the exchange 923 // rate, we can never transfer more than math.MaxInt64 anyway. 924 minMonths = math.MinInt64 - d.Months 925 } 926 monthsFromDays := int64Max(d.Days/DaysPerMonth, minMonths) 927 d.Months += monthsFromDays 928 d.Days -= monthsFromDays * DaysPerMonth 929 return d 930 } 931 932 func (d Duration) shiftNegNanosToDays() Duration { 933 var minDays = int64(math.MinInt64) 934 if d.Days < 0 { 935 // If d.Days > 0, then this would overflow, but because of the exchange 936 // rate, we can never transfer more than math.MaxInt64 anyway. 937 minDays = math.MinInt64 - d.Days 938 } 939 daysFromNanos := int64Max(d.nanos/nanosInDay, minDays) 940 d.Days += daysFromNanos 941 d.nanos -= daysFromNanos * nanosInDay 942 return d 943 } 944 945 func int64Max(a int64, b int64) int64 { 946 if a > b { 947 return a 948 } 949 return b 950 } 951 952 func int64Min(a int64, b int64) int64 { 953 if a < b { 954 return a 955 } 956 return b 957 } 958 959 const ( 960 minTimeDuration time.Duration = -1 << 63 961 maxTimeDuration time.Duration = 1<<63 - 1 962 ) 963 964 // DiffMicros computes the microsecond difference between two time values. The reason 965 // this function is necessary even though time.Sub(time) exists is that time.Duration 966 // can only hold values up to ~290 years, because it stores duration at the nanosecond 967 // resolution. This function should be used if a difference of more than 290 years is 968 // possible between time values, and a microsecond resolution is acceptable. 969 func DiffMicros(t1, t2 time.Time) int64 { 970 micros := int64(0) 971 nanos := time.Duration(0) 972 for { 973 // time.Sub(time) can overflow for durations larger than ~290 years, so 974 // we need to perform this diff iteratively. If this method overflows, 975 // it will return either minTimeDuration or maxTimeDuration. 976 d := t1.Sub(t2) 977 overflow := d == minTimeDuration || d == maxTimeDuration 978 if d == minTimeDuration { 979 // We use -maxTimeDuration here because -minTimeDuration would overflow. 980 d = -maxTimeDuration 981 } 982 micros += int64(d / time.Microsecond) 983 nanos += d % time.Microsecond 984 if !overflow { 985 break 986 } 987 t1 = t1.Add(-d) 988 } 989 micros += int64(nanos / time.Microsecond) 990 nanoRem := nanos % time.Microsecond 991 if nanoRem >= time.Microsecond/2 { 992 micros++ 993 } else if nanoRem <= -time.Microsecond/2 { 994 micros-- 995 } 996 return micros 997 } 998 999 // AddMicros adds the microsecond delta to the provided time value. The reason 1000 // this function is necessary even though time.Add(duration) exists is that time.Duration 1001 // can only hold values up to ~290 years, because it stores duration at the nanosecond 1002 // resolution. This function makes it possible to add more than 290 years to a time.Time, 1003 // at the tradeoff of working on a microsecond resolution. 1004 func AddMicros(t time.Time, d int64) time.Time { 1005 negMult := time.Duration(1) 1006 if d < 0 { 1007 negMult = -1 1008 d = -d 1009 } 1010 const maxMicroDur = int64(maxTimeDuration / time.Microsecond) 1011 for d > maxMicroDur { 1012 const maxWholeNanoDur = time.Duration(maxMicroDur) * time.Microsecond 1013 t = t.Add(negMult * maxWholeNanoDur) 1014 d -= maxMicroDur 1015 } 1016 return t.Add(negMult * time.Duration(d) * time.Microsecond) 1017 } 1018 1019 // Truncate returns a new duration obtained from the first argument 1020 // by discarding the portions at finer resolution than that given by the 1021 // second argument. 1022 // Example: Truncate(time.Second+1, time.Second) == time.Second. 1023 func Truncate(d time.Duration, r time.Duration) time.Duration { 1024 if r == 0 { 1025 panic(errors.AssertionFailedf("zero passed as resolution")) 1026 } 1027 return d - (d % r) 1028 } 1029 1030 // ParseInterval parses the given interval in the given style. 1031 func ParseInterval( 1032 style IntervalStyle, s string, itm types.IntervalTypeMetadata, 1033 ) (Duration, error) { 1034 // At this time the only supported interval formats are: 1035 // - SQL standard. 1036 // - Postgres compatible. 1037 // - iso8601 format (with designators only), see interval.go for 1038 // sources of documentation. 1039 // - Golang time.parseDuration compatible. 1040 1041 // If it's a blank string, exit early. 1042 if len(s) == 0 { 1043 return Duration{}, makeParseError(s, types.Interval, nil) 1044 } 1045 if s[0] == 'P' { 1046 // If it has a leading P we're most likely working with an iso8601 1047 // interval. 1048 dur, err := iso8601ToDuration(s) 1049 if err != nil { 1050 return Duration{}, makeParseError(s, types.Interval, err) 1051 } 1052 return dur, nil 1053 } 1054 if strings.IndexFunc(s, unicode.IsLetter) == -1 { 1055 // If it has no letter, then we're most likely working with a SQL standard 1056 // interval, as both postgres and golang have letter(s) and iso8601 has been tested. 1057 dur, err := sqlStdToDuration(s, itm) 1058 if err != nil { 1059 return Duration{}, makeParseError(s, types.Interval, err) 1060 } 1061 return dur, nil 1062 } 1063 1064 // We're either a postgres string or a Go duration. 1065 // Our postgres syntax parser also supports golang, so just use that for both. 1066 dur, err := parseDuration(style, s, itm) 1067 if err != nil { 1068 return Duration{}, makeParseError(s, types.Interval, err) 1069 } 1070 return dur, nil 1071 } 1072 1073 func makeParseError(s string, typ *types.T, err error) error { 1074 if err != nil { 1075 return pgerror.Wrapf(err, pgcode.InvalidTextRepresentation, 1076 "could not parse %q as type %s", s, typ) 1077 } 1078 return pgerror.Newf(pgcode.InvalidTextRepresentation, 1079 "could not parse %q as type %s", s, typ) 1080 }