# How go-sql-driver/mysql Handles Timezone Conversions and DST with `loc` and `parseTime`

> Learn how go-sql-driver/mysql leverages Go's time package and the loc parameter for accurate timezone conversions and DST handling. Discover seamless integration without custom logic.

- Repository: [Go SQL Drivers/mysql](https://github.com/go-sql-driver/mysql)
- Tags: deep-dive
- Published: 2026-03-02

---

**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`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go) and [`utils.go`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/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:

```go
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`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/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:

```go
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:

```go
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.LoadLocation` in [`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go) when parsing the `loc` DSN parameter, storing the result in `Config.Loc`
- Both `parseDateTime` (lines 108-179) and `parseBinaryDateTime` (lines 230-274) in [`utils.go`](https://github.com/go-sql-driver/mysql/blob/main/utils.go) pass the configured `*time.Location` to `time.Date()`
- All DST logic and timezone offset calculations are delegated entirely to Go's standard `time` package
- The deprecated `NullTime` type in [`nulltime.go`](https://github.com/go-sql-driver/mysql/blob/main/nulltime.go) ignores the `loc` parameter 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`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go) (handling DSN parsing and location loading via `time.LoadLocation`) and [`utils.go`](https://github.com/go-sql-driver/mysql/blob/main/utils.go) (containing the `parseDateTime` and `parseBinaryDateTime` functions that construct `time.Time` values using the configured location).