Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

time: layout string parser doesn't support all needed features to cover the RFC 3339 standard #63488

Closed
konatacarneiro opened this issue Oct 10, 2023 · 1 comment

Comments

@konatacarneiro
Copy link

konatacarneiro commented Oct 10, 2023

What version of Go are you using (go version)?

$ go version
go version go1.20.2 linux/amd64

Does this issue reproduce with the latest release?

Yes, both on Go 1.21 and on the unstable dev branch.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/bcarneiro/.cache/go-build"
GOENV="/home/bcarneiro/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/bcarneiro/go/pkg/mod"
GONOPROXY="github.com/dock-tech/*"
GONOSUMDB="github.com/dock-tech/*"
GOOS="linux"
GOPATH="/home/bcarneiro/go"
GOPRIVATE="github.com/dock-tech/*"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/bcarneiro/dock/softwares/go-sdks/go1.20.2"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/bcarneiro/dock/softwares/go-sdks/go1.20.2/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20.2"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2701446151=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Go's time library provides the time.RFC3339 layout string, which by its name is supposed to implement the RFC 3339 standard for timestamps. However, by reading the standard I found out that it's actually broader than I thought, and that the time.RFC3339 layout string actually only implements the common subset shared between RFC 3339 and ISO 8601, which consists of the timestamps where the date and time are separated by the uppercase "T" character.

I wanted full support for the RFC 3339 standard in the application I'm working on, so I tried to implement the rest by myself using time.Parse's layout string parser. By reading the standard, I came up with 3 extra layout strings to supplement time.RFC3339:

  • 2006-01-02 15:04:05Z07:00
  • 2006-01-02_15:04:05Z07:00
  • 2006-01-02t15:04:05z07:00

I then implemented a function to parse an RFC 3339 timestamp by successively calling time.Parse on each different string, returning the first result that doesn't return an error, or failing if all of them return an error. A test program containing a rewritten version of this function, ready to run, is available here: https://go.dev/play/p/vl5-BbRoulL?v=gotip

I then proceeded to test this function.

What did you expect to see?

I expected all of my custom-crafted layout strings to work and all of my test values to be parsed correctly.

What did you see instead?

One of my layout strings - the one with lowercase letters, 2006-01-02t15:04:05z07:00, did not work. This is the output from the test program linked above:

2023-10-10T12:19:21.275Z       - 2023-10-10 12:19:21.275 +0000 UTC
2023-10-10T09:19:21.275-03:00  - 2023-10-10 09:19:21.275 -0300 -0300
2023-10-10 12:19:21.275Z       - 2023-10-10 12:19:21.275 +0000 UTC
2023-10-10 09:19:21.275-03:00  - 2023-10-10 09:19:21.275 -0300 -0300
2023-10-10_12:19:21.275Z       - 2023-10-10 12:19:21.275 +0000 UTC
2023-10-10_09:19:21.275-03:00  - 2023-10-10 09:19:21.275 -0300 -0300
2023-10-10t12:19:21.275z       - <nil>
2023-10-10t09:19:21.275-03:00  - <nil>

Upon investigation, I landed on the following error being returned from the time.Parse function:

parsing time "2023-10-10t09:19:21.275-03:00" as "2006-01-02t15:04:05z07:00": cannot parse "-03:00" as "z07:00"

This pointed to the "z07:00" element not being recognized as a timezone element. Upon further investigation with a debugger, I landed on the function nextStdChunk() of format.go, where I found this code snippet:

		case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00,
			if len(layout) >= i+7 && layout[i:i+7] == "Z070000" {
				return layout[0:i], stdISO8601SecondsTZ, layout[i+7:]
			}
			if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" {
				return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:]
			}
			if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
				return layout[0:i], stdISO8601TZ, layout[i+5:]
			}
			if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
				return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
			}
			if len(layout) >= i+3 && layout[i:i+3] == "Z07" {
				return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
			}

The function looks for an uppercase "Z" to find the ISO timezone layout element - but nowhere in the function does it look for a lowercase "z". This means that the parser simply doesn't expect this specific case from RFC 3339.

@seankhliao
Copy link
Member

Duplicate of #20869

@seankhliao seankhliao marked this as a duplicate of #20869 Oct 10, 2023
@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Oct 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants