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  }