Skip to content

year 0 in time.Time is valid but always throws an error #1223

Open
@gkdr

Description

@gkdr

Issue description

after upgrade from v1.5.0 to v.1.6.0, a time.Time representing an actual time without a date cannot be inserted into a column of type TIME any longer, as it has year 0 set. funnily, this value was previously even retrieved from the DB. year 0 is also in general not an invalid time.Time value for real dates.

from https://golang.org/pkg/time/#Parse :

Elements omitted from the value are assumed to be zero or, when zero is impossible, one, so parsing "3:04pm" returns the time corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is 0, this time is before the zero Time). Years must be in the range 0000..9999.

the following recently added line throws an error when the year is 0:

mysql/utils.go

Line 285 in 417641a

return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap

as far as i can see, appendDateTime(), the function which contains this line, is called twice, and both times when the given time.Time does not have its zero value as determined by IsZero(). however, the zero value for a time.Time is in year 1, see https://golang.org/pkg/time/#Time.IsZero :

IsZero reports whether t represents the zero time instant, January 1, year 1, 00:00:00 UTC.

what is the reason the range was picked to be [1, 9999] here?

if you can confirm this is actually an issue, i'm willing to try and fix it 🙂

Example code

package main

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

func main() {
	db, err := sql.Open("mysql", "user:pw@instance:3306/db")
	if err != nil {
		panic(err)
	}

	if _, err := db.Exec("CREATE TEMPORARY TABLE IF NOT EXISTS `time_test` (`id` int not null, `some_time` time not null);"); err != nil {
		panic(err)
	}

	if _, err := db.Exec("insert into `time_test` values(1, '08:30:30');"); err != nil {
		panic(err)
	}

	var someTime string
	row := db.QueryRow("select some_time from `time_test` where id = 1;")
	if err := row.Scan(&someTime); err != nil {
		panic(err)
	}

	fmt.Printf("this worked: %v\n", someTime)

	mysqlTimeFormat := "15:04:05"
	parsedTime, err := time.Parse(mysqlTimeFormat, someTime)
	if err != nil {
		panic(err)
	}

	// this works in 1.5.0, panics in 1.6.0
	if _, err := db.Exec("update `time_test` set some_time = ? where id = 1;", parsedTime); err != nil {
		panic(err)
	}

	fmt.Println("success!")
}

Error log

the error i receive is the error from the line i specified above:
panic: year is not in the range [1, 9999]: 0

Configuration

Driver version (or git SHA): v1.6.0

Go version: 1.16.2

Server version: MySQL 8.0.20

Server OS: official mysql docker container, iirc its based on debian but should be irrelevant.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions