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  }