How go-sql-driver/mysql Handles Timezone Conversions and DST with `loc` and `parseTime`
The go-sql-driver/mysql delegates all timezone conversions and DST handling to Go's standard time package by passing the loc DSN parameter directly to time.Date() constructors, ensuring accurate IANA timezone database lookups without implementing custom DST logic.
When retrieving DATETIME or TIMESTAMP columns from MySQL using the github.com/go-sql-driver/mysql driver, enabling parseTime=true permits automatic conversion to Go time.Time values. Understanding how the driver manages timezone conversions and DST when the loc parameter is specified requires examining the implementation in dsn.go and utils.go.
How the loc Parameter Configures Timezone Loading
When opening a connection, the driver constructs a Config structure by parsing the DSN string in dsn.go. If the DSN contains a loc parameter (e.g., loc=America/New_York), the code executes a case "loc" branch that loads the IANA timezone location using time.LoadLocation and stores the resulting *time.Location in Config.Loc.
This location configuration—referenced around line 338 in dsn.go during writeDSNParam processing—becomes the authoritative timezone for all temporal conversions on that connection.
Converting MySQL Temporal Data to Go Time Values
During row scanning, the driver transforms raw MySQL date-time values into Go time.Time objects using different parsing strategies for text versus binary protocol results. Both paths utilize the *time.Location stored in the connection's Config.
Text Protocol Implementation
For text-based query results, the parseDateTime function in utils.go (lines 108-179) handles the conversion. This function receives the parsed date-time components (year, month, day, hour, minute, second, nanosecond) from the MySQL string representation, along with the configured location.
The function constructs the final timestamp using:
time.Date(year, month, day, hour, min, sec, nsec, loc)
The loc parameter passed here is exactly the *time.Location loaded from the DSN's loc parameter.
Binary Protocol Implementation
For prepared statements using the binary protocol, parseBinaryDateTime in utils.go (lines 230-274) performs the conversion. After decoding the binary representation of TIME, DATETIME, or TIMESTAMP values, this function similarly invokes time.Date(..., loc) using the location from Config.Loc.
DST Resolution and Edge Case Handling
Because the driver simply forwards the *time.Location to Go's standard time package, all DST rules and timezone offset calculations are delegated to the IANA timezone database. The driver contains no custom DST implementation.
Go's time package automatically applies the correct UTC offset for the specific date, respecting historic and future DST transitions. For edge cases during DST shifts:
- Ambiguous times occur when clocks fall back and a wall-clock time repeats. Go resolves these to the later UTC offset.
- Non-existent times occur when clocks spring forward and a wall-clock time is skipped. Go resolves these to the earlier UTC offset.
This ensures consistent timezone conversions and DST handling across all supported locations without driver intervention.
The Deprecated NullTime Exception
A critical limitation exists in nulltime.go: the deprecated NullTime type does not honor the loc DSN parameter. When scanning into NullTime, values are always interpreted as UTC regardless of the configured location. For proper timezone support, use the standard time.Time type with sql.NullTime instead.
Configuration Examples
To enable automatic timezone conversions with DST support, configure both parameters in your DSN:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"log"
"time"
)
func main() {
// Load America/New_York from the IANA database
dsn := "user:pass@tcp(localhost:3306)/test?parseTime=true&loc=America/New_York"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
var eventTime time.Time
err := db.QueryRow("SELECT event_at FROM events LIMIT 1").Scan(&eventTime)
if err != nil {
log.Fatal(err)
}
// eventTime contains the correct wall-clock time with DST applied
log.Printf("Event time: %v", eventTime)
}
DST transitions are handled transparently. For example, inserting a time during a DST gap:
loc, _ := time.LoadLocation("America/New_York")
// March 10, 2024 2:30 AM does not exist (clocks jump from 2:00 to 3:00 AM)
gapTime := time.Date(2024, 3, 10, 2, 30, 0, 0, loc)
_, err := db.Exec("INSERT INTO events (event_at) VALUES (?)", gapTime)
// When retrieved later, the driver resolves the non-existent time to 3:30 AM EDT
// using Go's standard DST resolution rules
Summary
- The driver loads IANA timezone locations via
time.LoadLocationindsn.gowhen parsing thelocDSN parameter, storing the result inConfig.Loc - Both
parseDateTime(lines 108-179) andparseBinaryDateTime(lines 230-274) inutils.gopass the configured*time.Locationtotime.Date() - All DST logic and timezone offset calculations are delegated entirely to Go's standard
timepackage - The deprecated
NullTimetype innulltime.goignores thelocparameter and treats all values as UTC - No custom DST implementation exists in the driver; it relies on the IANA database through Go's standard library
Frequently Asked Questions
Does the go-sql-driver/mysql implement its own DST logic?
No. According to the source code in utils.go, the driver delegates all timezone conversions and DST handling to Go's standard time package. It simply passes the *time.Location configured via the loc DSN parameter to time.Date() constructors, allowing the standard library to apply IANA timezone rules.
What happens when a queried time falls during a DST transition?
Go's time package resolves these edge cases automatically based on the IANA database. For ambiguous times during clock fallbacks, it selects the later UTC offset. For non-existent times during clock spring-forwards, it resolves to the earlier offset. This behavior applies consistently to both text and binary protocol parsing in the driver.
Why are my times always in UTC despite setting loc in the DSN?
If you are scanning into the deprecated NullTime type defined in nulltime.go, the driver explicitly ignores the loc parameter and parses all values as UTC. Switch to the standard time.Time type or sql.NullTime to respect your DSN location configuration.
Which source files contain the timezone conversion logic?
The key files are dsn.go (handling DSN parsing and location loading via time.LoadLocation) and utils.go (containing the parseDateTime and parseBinaryDateTime functions that construct time.Time values using the configured location).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →