github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/timeutil/pgdate/math.go (about) 1 // Copyright 2018 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 pgdate 12 13 import ( 14 "unicode" 15 "unicode/utf8" 16 ) 17 18 var daysInMonth = [2][13]int{ 19 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 20 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 21 } 22 23 // DateToJulianDay is based on the date2j function in PostgreSQL 10.5. 24 func DateToJulianDay(year int, month int, day int) int { 25 if month > 2 { 26 month++ 27 year += 4800 28 } else { 29 month += 13 30 year += 4799 31 } 32 33 century := year / 100 34 jd := year*365 - 32167 35 jd += year/4 - century + century/4 36 jd += 7834*month/256 + day 37 38 return jd 39 } 40 41 // isLeap returns true if the given year is a leap year. 42 func isLeap(year int) bool { 43 return (year%4 == 0) && (year%100 != 0 || year%400 == 0) 44 } 45 46 // julianDayToDate is based on the j2date function in PostgreSQL 10.5. 47 func julianDayToDate(j int) (year int, month int, day int) { 48 jd := uint(j) 49 jd += 32044 50 quad := jd / 146097 51 extra := (jd-quad*146097)*4 + 3 52 jd += 60 + quad*3 + extra/146097 53 quad = jd / 1461 54 jd -= quad * 1461 55 y := jd * 4 / 1461 56 if y != 0 { 57 jd = (jd + 305) % 365 58 } else { 59 jd = (jd + 306) % 366 60 } 61 jd += 123 62 y += quad * 4 63 year = int(y - 4800) 64 quad = jd * 2141 / 65536 65 day = int(jd - 7834*quad/256) 66 month = int((quad+10)%12 + 1) 67 68 return 69 } 70 71 // stringChunk is returned by chunk(). 72 type stringChunk struct { 73 // The contiguous span of characters that did not match the filter and 74 // which appear immediately before Match. 75 NotMatch string 76 // The contiguous span of characters that matched the filter. 77 Match string 78 } 79 80 // chunk filters the runes in a string and populates the buffer with 81 // contiguous spans of alphanumeric characters. The number of 82 // chunks will be returned along with any leftover, unmatching text. 83 // If the string cannot be stored entirely within the buffer, 84 // -1 will be returned. 85 func chunk(s string, buf []stringChunk) (int, string) { 86 // pprof says that passing the buffer into chunk instead 87 // of returning one is significantly faster than returning one here. 88 // BenchmarkChunking went from 180 ns/op down to 78 ns/op, 89 // presumably because the compiler can stack-allocate the 90 // initial make(). 91 92 matchStart := 0 93 matchEnd := 0 94 previousMatchEnd := 0 95 count := 0 96 maxIdx := len(buf) - 1 97 98 flush := func() bool { 99 if matchEnd > matchStart { 100 notMatch := s[previousMatchEnd:matchStart] 101 match := s[matchStart:matchEnd] 102 103 // Special-case to handle ddThh delimiter 104 if len(match) == 5 && (match[2] == 'T' || match[2] == 't') { 105 if count+1 > maxIdx { 106 return false 107 } 108 buf[count] = stringChunk{ 109 NotMatch: notMatch, 110 Match: match[:2], 111 } 112 buf[count+1] = stringChunk{ 113 NotMatch: "t", 114 Match: match[3:], 115 } 116 count += 2 117 } else { 118 if count > maxIdx { 119 return false 120 } 121 buf[count] = stringChunk{ 122 NotMatch: notMatch, 123 Match: match, 124 } 125 count++ 126 } 127 previousMatchEnd = matchEnd 128 matchStart = matchEnd 129 } 130 return true 131 } 132 133 for offset, r := range s { 134 if unicode.IsDigit(r) || unicode.IsLetter(r) { 135 if matchStart >= matchEnd { 136 matchStart = offset 137 } 138 // We're guarded by IsDigit() || IsLetter() above, so 139 // RuneLen() should always return a reasonable value. 140 matchEnd = offset + utf8.RuneLen(r) 141 } else if !flush() { 142 return -1, "" 143 } 144 } 145 if !flush() { 146 return -1, "" 147 } 148 149 return count, s[matchEnd:] 150 }