terraform-provider-gitea/vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime_rfc3339.go
dependabot[bot] 84c9110a24
Bump github.com/hashicorp/terraform-plugin-sdk/v2 from 2.24.1 to 2.26.0
Bumps [github.com/hashicorp/terraform-plugin-sdk/v2](https://github.com/hashicorp/terraform-plugin-sdk) from 2.24.1 to 2.26.0.
- [Release notes](https://github.com/hashicorp/terraform-plugin-sdk/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-sdk/compare/v2.24.1...v2.26.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-sdk/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 20:25:53 +00:00

220 lines
6.4 KiB
Go

package stdlib
import (
"errors"
"strconv"
"time"
)
// This file inlines some RFC3339 parsing code that was added to the Go standard
// library's "time" package during the Go 1.20 development period but then
// reverted prior to release to follow the Go proposals process first.
//
// Our goal is to support only valid RFC3339 strings regardless of what version
// of Go is being used, because the Go stdlib is just an implementation detail
// of the cty stdlib and so these functions should not very their behavior
// significantly due to being compiled against a different Go version.
//
// These inline copies of the code from upstream should likely stay here
// indefinitely even if functionality like this _is_ accepted in a later version
// of Go, because this now defines cty's definition of RFC3339 parsing as
// intentionally independent of Go's.
func parseStrictRFC3339(str string) (time.Time, error) {
t, ok := parseRFC3339(str)
if !ok {
// If parsing failed then we'll try to use time.Parse to gather up a
// helpful error object.
_, err := time.Parse(time.RFC3339, str)
if err != nil {
return time.Time{}, err
}
// The parse template syntax cannot correctly validate RFC 3339.
// Explicitly check for cases that Parse is unable to validate for.
// See https://go.dev/issue/54580.
num2 := func(str string) byte { return 10*(str[0]-'0') + (str[1] - '0') }
switch {
case str[len("2006-01-02T")+1] == ':': // hour must be two digits
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: "15",
ValueElem: str[len("2006-01-02T"):][:1],
Message: ": hour must have two digits",
}
case str[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: ".",
ValueElem: ",",
Message: ": sub-second separator must be a period",
}
case str[len(str)-1] != 'Z':
switch {
case num2(str[len(str)-len("07:00"):]) >= 24: // timezone hour must be in range
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: "Z07:00",
ValueElem: str[len(str)-len("Z07:00"):],
Message: ": timezone hour out of range",
}
case num2(str[len(str)-len("00"):]) >= 60: // timezone minute must be in range
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: "Z07:00",
ValueElem: str[len(str)-len("Z07:00"):],
Message: ": timezone minute out of range",
}
}
default: // unknown error; should not occur
return time.Time{}, &time.ParseError{
Layout: time.RFC3339,
Value: str,
LayoutElem: time.RFC3339,
ValueElem: str,
Message: "",
}
}
}
return t, nil
}
func parseRFC3339(s string) (time.Time, bool) {
// parseUint parses s as an unsigned decimal integer and
// verifies that it is within some range.
// If it is invalid or out-of-range,
// it sets ok to false and returns the min value.
ok := true
parseUint := func(s string, min, max int) (x int) {
for _, c := range []byte(s) {
if c < '0' || '9' < c {
ok = false
return min
}
x = x*10 + int(c) - '0'
}
if x < min || max < x {
ok = false
return min
}
return x
}
// Parse the date and time.
if len(s) < len("2006-01-02T15:04:05") {
return time.Time{}, false
}
year := parseUint(s[0:4], 0, 9999) // e.g., 2006
month := parseUint(s[5:7], 1, 12) // e.g., 01
day := parseUint(s[8:10], 1, daysIn(time.Month(month), year)) // e.g., 02
hour := parseUint(s[11:13], 0, 23) // e.g., 15
min := parseUint(s[14:16], 0, 59) // e.g., 04
sec := parseUint(s[17:19], 0, 59) // e.g., 05
if !ok || !(s[4] == '-' && s[7] == '-' && s[10] == 'T' && s[13] == ':' && s[16] == ':') {
return time.Time{}, false
}
s = s[19:]
// Parse the fractional second.
var nsec int
if len(s) >= 2 && s[0] == '.' && isDigit(s, 1) {
n := 2
for ; n < len(s) && isDigit(s, n); n++ {
}
nsec, _, _ = parseNanoseconds(s, n)
s = s[n:]
}
// Parse the time zone.
loc := time.UTC
if len(s) != 1 || s[0] != 'Z' {
if len(s) != len("-07:00") {
return time.Time{}, false
}
hr := parseUint(s[1:3], 0, 23) // e.g., 07
mm := parseUint(s[4:6], 0, 59) // e.g., 00
if !ok || !((s[0] == '-' || s[0] == '+') && s[3] == ':') {
return time.Time{}, false
}
zoneOffsetSecs := (hr*60 + mm) * 60
if s[0] == '-' {
zoneOffsetSecs = -zoneOffsetSecs
}
loc = time.FixedZone("", zoneOffsetSecs)
}
t := time.Date(year, time.Month(month), day, hour, min, sec, nsec, loc)
return t, true
}
func isDigit(s string, i int) bool {
if len(s) <= i {
return false
}
c := s[i]
return '0' <= c && c <= '9'
}
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
if value[0] != '.' && value[0] != ',' {
err = errBadTimestamp
return
}
if nbytes > 10 {
value = value[:10]
nbytes = 10
}
if ns, err = strconv.Atoi(value[1:nbytes]); err != nil {
return
}
if ns < 0 {
rangeErrString = "fractional second"
return
}
// We need nanoseconds, which means scaling by the number
// of missing digits in the format, maximum length 10.
scaleDigits := 10 - nbytes
for i := 0; i < scaleDigits; i++ {
ns *= 10
}
return
}
// These are internal errors used by the date parsing code and are not ever
// returned by public functions.
var errBadTimestamp = errors.New("bad value for field")
// daysBefore[m] counts the number of days in a non-leap year
// before month m begins. There is an entry for m=12, counting
// the number of days before January of next year (365).
var daysBefore = [...]int32{
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}
func daysIn(m time.Month, year int) int {
if m == time.February && isLeap(year) {
return 29
}
return int(daysBefore[m] - daysBefore[m-1])
}
func isLeap(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}