Age() function not working with time.Time

Issue

Trying to use age() function from postgresql with DateOfBirth

type Person struct {
  ID int
  DOB time.Time
}
query := `SELECT id, AGE(dateofbirth) from person WHERE id = $1`

But got an error

Unsupported Scan, storing driver.Value type []uint8 into type *time.Time

The age() function work if I just pass the DOB as string

type Person struct {
  ID int
  DOB string
}

But I want to check the validity of DOB so I use time.Parse("2006-01-02", DOB)

func createPerson(w http.ResponseWriter, r *http.Request) {
  err := r.ParseForm()
  if err != nil {
        log.Fatal(err)
  }
  dob := r.PostForm.Get("dob")
  birthday, _ := time.Parse("2006-01-02", dob)

  err = models.person.Insert(birthday)
  if err != nil {
        log.Fatal("Server Error: ", err)
        return
  }
  http.Redirect(w, r, "/", http.StatusSeeOther)
}

func getPerson(w http.ResponseWriter, r *http.Request) {
  id, err := strconv.Atoi(chi.URLParam(r, "id"))
  if err != nil {
    log.Fatal("Not found", err)
    return
  }
  person, err = models.person.Get(id)
  if err != nil {
      log.Fatal("Server Error: ", err)
      return
  }
  render.HTML(w, http.StatusOK, "person.html", person)
}

Solution

Your database driver is responsible for transforming the incoming data from the database into Go data types. I’ll assume you’re using pq, which has the following rules about data types:

This package returns the following types for values from the PostgreSQL backend:

  • integer types smallint, integer, and bigint are returned as int64
  • floating-point types real and double precision are returned as float64
  • character types char, varchar, and text are returned as string
  • temporal types date, time, timetz, timestamp, and timestamptz are returned as time.Time
  • the boolean type is returned as bool
  • the bytea type is returned as []byte

All other types are returned directly from the backend as []byte values in text format.

In PostgreSQL, the AGE function returns data type interval. This is not one of the directly supported data types, so pq processes it as []byte. That is why it works when you make DOB a string. Conversion between []byte and string in Go is trivial, so the driver is able to fill in the value.

The driver fails to fill the []byte into a time.Time value because it doesn’t know what conversion routine to use. Actually, there is no direct conversion routine because interval represents a duration of time, while time.Time represents an instance of time. interval is more analogous to a time.Duration value. Still, the driver doesn’t support any automatic conversion from interval to any type, and I’m not aware of a way to add a new conversion. You would have to implement the conversion yourself after fetching the data from the database.

You could also switch to the pgx driver, which supports an Interval type.

Answered By – Hymns For Disco

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.