github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/timeutil/pgdate/setters.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 "strconv" 15 "strings" 16 "time" 17 18 "github.com/cockroachdb/cockroachdb-parser/pkg/util/errorutil/unimplemented" 19 ) 20 21 // The functions in this file are used by fieldExtract.Extract(). 22 23 // A fieldSetter is a helper function to set one or more 24 // fields within a fieldExtract in response to user input. 25 // These functions are used by fieldExtract.Extract(). 26 type fieldSetter func(p *fieldExtract, s string) error 27 28 var keywordSetters = map[string]fieldSetter{ 29 "jan": fieldSetterMonth(1), 30 "january": fieldSetterMonth(1), 31 "feb": fieldSetterMonth(2), 32 "february": fieldSetterMonth(2), 33 "mar": fieldSetterMonth(3), 34 "march": fieldSetterMonth(3), 35 "apr": fieldSetterMonth(4), 36 "april": fieldSetterMonth(4), 37 "may": fieldSetterMonth(5), 38 "jun": fieldSetterMonth(6), 39 "june": fieldSetterMonth(6), 40 "jul": fieldSetterMonth(7), 41 "july": fieldSetterMonth(7), 42 "aug": fieldSetterMonth(8), 43 "august": fieldSetterMonth(8), 44 "sep": fieldSetterMonth(9), 45 "sept": fieldSetterMonth(9), /* 4-chars, too in pg */ 46 "september": fieldSetterMonth(9), 47 "oct": fieldSetterMonth(10), 48 "october": fieldSetterMonth(10), 49 "nov": fieldSetterMonth(11), 50 "november": fieldSetterMonth(11), 51 "dec": fieldSetterMonth(12), 52 "december": fieldSetterMonth(12), 53 54 keywordYesterday: fieldSetterRelativeDate, 55 keywordToday: fieldSetterRelativeDate, 56 keywordTomorrow: fieldSetterRelativeDate, 57 58 keywordEraAD: fieldSetterExact(fieldEra, fieldValueCE), 59 keywordEraCE: fieldSetterExact(fieldEra, fieldValueCE), 60 61 keywordEraBC: fieldSetterExact(fieldEra, fieldValueBCE), 62 keywordEraBCE: fieldSetterExact(fieldEra, fieldValueBCE), 63 64 keywordAM: fieldSetterExact(fieldMeridian, fieldValueAM), 65 keywordPM: fieldSetterExact(fieldMeridian, fieldValuePM), 66 67 keywordAllBalls: fieldSetterUTC, 68 keywordGMT: fieldSetterUTC, 69 keywordUTC: fieldSetterUTC, 70 keywordZ: fieldSetterUTC, 71 keywordZulu: fieldSetterUTC, 72 } 73 74 // These abbreviations are taken from: 75 // https://github.com/postgres/postgres/blob/master/src/timezone/known_abbrevs.txt 76 // We have not yet implemented PostgreSQL's abbreviation-matching logic 77 // because we'd also need to incorporate more tzinfo than is readily-available 78 // from the time package. Instead, we have this map to provide a more 79 // useful error message (and telemetry) until we do implement this 80 // behavior. 81 var unsupportedAbbreviations = [...]string{ 82 "ACDT", "ACST", "ADT", "AEDT", "AEST", "AKDT", "AKST", "AST", "AWST", "BST", 83 "CAT", "CDT", "CDT", "CEST", "CET", "CST", "CST", "CST", "ChST", 84 "EAT", "EDT", "EEST", "EET", "EST", 85 // GMT has been removed from this list. 86 "HDT", "HKT", "HST", "IDT", "IST", "IST", "IST", "JST", "KST", 87 "MDT", "MEST", "MET", "MSK", "MST", "NDT", "NST", "NZDT", "NZST", 88 "PDT", "PKT", "PST", "PST", "SAST", "SST", "UCT", 89 // UTC has been removed from this list. 90 "WAT", "WEST", "WET", "WIB", "WIT", "WITA", 91 } 92 93 func init() { 94 for _, tz := range unsupportedAbbreviations { 95 keywordSetters[strings.ToLower(tz)] = fieldSetterUnsupportedAbbreviation 96 } 97 } 98 99 // fieldSetterExact returns a fieldSetter that unconditionally sets field to v. 100 func fieldSetterExact(field field, v int) fieldSetter { 101 return func(p *fieldExtract, _ string) error { 102 return p.Set(field, v) 103 } 104 } 105 106 // fieldSetterJulianDate parses a value like "J2451187" to set 107 // the year, month, and day fields. 108 func fieldSetterJulianDate(fe *fieldExtract, s string) (bool, error) { 109 if !strings.HasPrefix(s, "j") { 110 return false, nil 111 } 112 date, err := strconv.Atoi(s[1:]) 113 if err != nil { 114 return true, inputErrorf("could not parse julian date") 115 } 116 117 year, month, day := julianDayToDate(date) 118 119 if err := fe.Set(fieldYear, year); err != nil { 120 return true, err 121 } 122 if err := fe.Set(fieldMonth, month); err != nil { 123 return true, err 124 } 125 return true, fe.Set(fieldDay, day) 126 } 127 128 // fieldSetterMonth returns a fieldSetter that unconditionally sets 129 // the month to the given value. 130 func fieldSetterMonth(month int) fieldSetter { 131 return fieldSetterExact(fieldMonth, month) 132 } 133 134 // fieldSetterRelativeDate sets the year, month, and day 135 // in response to the inputs "yesterday", "today", and "tomorrow" 136 // relative to fieldExtract.now. 137 func fieldSetterRelativeDate(fe *fieldExtract, s string) error { 138 var offset int 139 switch s { 140 case keywordYesterday: 141 offset = -1 142 case keywordToday: 143 case keywordTomorrow: 144 offset = 1 145 } 146 147 year, month, day := fe.now().AddDate(0, 0, offset).Date() 148 149 if err := fe.Set(fieldYear, year); err != nil { 150 return err 151 } 152 if err := fe.Set(fieldMonth, int(month)); err != nil { 153 return err 154 } 155 return fe.Set(fieldDay, day) 156 } 157 158 // fieldSetterUTC unconditionally sets the timezone to UTC and 159 // removes the TZ fields from the wanted list. 160 func fieldSetterUTC(fe *fieldExtract, _ string) error { 161 fe.location = time.UTC 162 fe.wanted = fe.wanted.ClearAll(tzFields) 163 return nil 164 } 165 166 // fieldSetterUnsupportedAbbreviation always returns an error, but 167 // captures the abbreviation in telemetry. 168 func fieldSetterUnsupportedAbbreviation(_ *fieldExtract, s string) error { 169 return unimplemented.NewWithIssueDetail(31710, s, "timestamp abbreviations not supported") 170 }