Open lgrote opened 3 months ago
No sure if this is an elegant way to solve this. However, this works for me.
func CivilTimeString(t civil.Time) string {
if t.Nanosecond == 0 {
return t.String()
}
micro := (t.Nanosecond + 500) / 1000 // round to nearest microsecond
t.Nanosecond = 0
if micro == 1000000 { // round to nearest second
t.Second = t.Second + 1
return t.String()
}
return t.String() + fmt.Sprintf(".%06d", micro)
}
func TestCivilTimeString(t *testing.T) {
tests := []struct {
s string
want string
}{
{"15:04:05.99999", "15:04:05.999990"},
{"15:04:05.999999", "15:04:05.999999"},
{"15:04:05.9999999", "15:04:06"},
{"15:04:05.9999995", "15:04:06"},
{"15:04:05.9999994", "15:04:05.999999"},
{"15:04:05.5000009999", "15:04:05.500001"},
}
for _, tt := range tests {
ti, err := civil.ParseTime(tt.s)
if err != nil {
t.Fatalf("Error parsing time: %v", err)
}
formatted := CivilTimeString(ti)
if formatted != tt.want {
t.Errorf("want %s, got %s", tt.want, formatted)
}
}
}
Client
BigQuery
Environment
distroless docker on GKE
Go Environment
$ go version go1.22.4 darwin/arm64 $ ❯ go env GO111MODULE='' GOARCH='arm64' GOBIN='' GOCACHE='/Users/lgr/Library/Caches/go-build' GOENV='/Users/lgr/Library/Application Support/go/env' GOEXE='' GOEXPERIMENT='' GOFLAGS='' GOHOSTARCH='arm64' GOHOSTOS='darwin' GOINSECURE='' GOMODCACHE='/Users/lgr/workspace/go/pkg/mod' GONOPROXY='' GONOSUMDB='' GOOS='darwin' GOPATH='/Users/lgr/workspace/go' GOPRIVATE='' GOPROXY='https://proxy.golang.org,direct' GOROOT='/usr/local/go/' GOSUMDB='sum.golang.org' GOTMPDIR='' GOTOOLCHAIN='auto' GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64' GOVCS='' GOVERSION='go1.22.4' GCCGO='gccgo' AR='ar' CC='clang' CXX='clang++' CGO_ENABLED='1' GOMOD='/Users/lgr/workspace/go/xxxx' 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 -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/ry/1805fhs14_n982jppzrh0v0h0000gn/T/go-build1957741136=/tmp/go-build -gno-record-gcc-switches -fno-common'
Code
Expected behavior
A
civil.DateTime
orcivil.Time
formatted with the functionsbigquery.CivilDateTimeString
orbigquery.CivilTimeString
should be insertable into bigquery with theDATETIME
andTIME
datatypes.Actual behavior
If the Nanoseconds of a
civil.Time
are rounded up (likedt3
above) bigquery does not accept the value and reponds with an error like this:The reason seems to be that the string format accepted by
DATETIME
andTIME
expects a maximum of 6 fractional digits cd. Data type docsHowever,
bigquery.CivilTimeString
can produce more fractional digits, as can be seen in the test above.Context
For me the error occured while using the
Uploader
and saving structs that usecivil.DateTime
as timestamp. I tracked the error down to thebigquery.CivilTimeString
function.