Question

How to Convert a Unix Timestamp to time.Time in Go

gotimeunix-timestamp

Question

I am trying to convert a Unix timestamp into a time.Time value in Go, but I get an "out of range" error. This is confusing because I thought the layout I used was valid according to the Go documentation.

Here is the code:

package main

import (
    "fmt"
    "time"
)

func main() {
    tm, err := time.Parse("1136239445", "1405544146")
    if err != nil {
        panic(err)
    }

    fmt.Println(tm)
}

Why does this fail, and what is the correct way to convert a Unix timestamp to time.Time in Go?

Short Answer

By the end of this page, you will understand why time.Parse() does not work for Unix timestamps in Go, how Go's time layout system actually works, and how to correctly convert Unix seconds into a time.Time using time.Unix(). You will also learn common mistakes, practical usage patterns, and how this appears in real Go codebases.

Concept

In Go, time.Parse() is used for parsing formatted date/time strings, not raw Unix timestamps.

A Unix timestamp is typically a number representing the number of seconds since:

  • 1970-01-01 00:00:00 UTC

For example:

  • 1405544146 means a specific moment in time measured as elapsed seconds since the Unix epoch.

Why time.Parse() fails

The first argument to time.Parse() is not a pattern like YYYY-MM-DD and it is not an example of your input format in a generic sense. In Go, the layout must use the special reference date:

Mon Jan 2 15:04:05 MST 2006

or pieces of it, such as:

2006-01-02
15:04:05

When you write:

time.Parse("1136239445", "1405544146")

Go tries to interpret 1136239445 as a time layout based on the reference date rules. Since it is just a random number and not a valid layout, parsing fails.

The correct tool: time.Unix()

To convert a Unix timestamp to time.Time, use:

time.Unix(seconds, nanoseconds)

Example:

tm := time.Unix(1405544146, 0)

This creates a time.Time from Unix seconds directly.

Why this matters

In real programs, timestamps often come from:

  • APIs
  • databases
  • log files
  • message queues
  • JSON payloads

Knowing when to use time.Parse() versus time.Unix() prevents bugs and makes your code clearer.

Mental Model

Think of Go time handling like having two different doors:

  • time.Parse() is the door for reading a clock/calendar string such as "2024-06-10 14:30:00"
  • time.Unix() is the door for reading a stopwatch counter such as 1405544146

A formatted date string says:

  • "Here is the date written in human-readable form."

A Unix timestamp says:

  • "Here is the number of seconds since a fixed starting point."

If you try to send a stopwatch number through the calendar-string door, it will not fit. That is exactly what happened in the original code.

Take Quiz

Syntax and Examples

Core syntax

Parsing a formatted date string

parsedTime, err := time.Parse("2006-01-02 15:04:05", "2024-06-10 12:30:00")

Use this when your input is a human-readable date/time string.

Converting a Unix timestamp

t := time.Unix(1405544146, 0)

Use this when your input is seconds since the Unix epoch.

Beginner-friendly example

package main

import (
    "fmt"
    "time"
)

func main() {
    timestamp := int64(1405544146)
    tm := time.Unix(timestamp, 0)

    fmt.Println(tm)
    fmt.Println(tm.UTC())
}

Explanation

  • timestamp is stored as int64, which matches what time.Unix() expects.
  • time.Unix(timestamp, 0) means:
    • first argument = seconds
    • second argument = nanoseconds
  • fmt.Println(tm) prints the time in the local time zone representation.
  • tm.UTC() shows the same instant in UTC.

If the timestamp is a string

Often timestamps arrive as strings from JSON, forms, or APIs.

package main

import (
    "fmt"
    "strconv"
    "time"
)

func main() {
    raw := "1405544146"

    seconds, err := strconv.ParseInt(raw, 10, 64)
    if err != nil {
        panic(err)
    }

    tm := time.Unix(seconds, 0)
    fmt.Println(tm)
}

Here:

  • strconv.ParseInt() converts the string to an integer.
  • time.Unix() converts that integer to time.Time.

Step by Step Execution

Consider this example:

package main

import (
    "fmt"
    "time"
)

func main() {
    ts := int64(1405544146)
    tm := time.Unix(ts, 0)
    fmt.Println(tm.UTC())
}

Step by step

1. Create a timestamp value

ts := int64(1405544146)
  • ts stores the Unix timestamp.
  • It is an int64, which is the expected type for time.Unix().

2. Convert it to time.Time

tm := time.Unix(ts, 0)
  • ts is treated as seconds since 1970-01-01 00:00:00 UTC.
  • 0 means there are no extra nanoseconds.
  • Go creates a time.Time value representing that exact instant.

3. Print the result in UTC

fmt.Println(tm.UTC())
  • tm.UTC() converts the display to UTC.
  • This helps avoid confusion caused by local time zones.

What if you used time.Parse() instead?

tm, err := time.Parse("1136239445", "1405544146")

Go would try to treat 1136239445 as a layout string based on the special reference date format. Since it is not a valid layout, parsing fails.

So the important rule is:

  • Use time.Parse() for formatted date strings
  • Use time.Unix() for Unix timestamps

Real World Use Cases

Unix timestamps are extremely common in real software.

APIs

Many APIs send timestamps like:

{"created_at": 1718012000}

In Go, you convert them with time.Unix() before displaying or comparing dates.

Logging systems

Log entries may store event times as Unix seconds or milliseconds. Converting them to time.Time makes them easier to format and analyze.

Databases

Some databases or legacy systems store timestamps as integers instead of full datetime values. Go applications often convert those integers into time.Time when reading records.

Expiration and scheduling

Applications frequently check whether a timestamp is before or after the current time:

expiresAt := time.Unix(expirySeconds, 0)
if time.Now().After(expiresAt) {
    fmt.Println("expired")
}

Data processing scripts

CSV, JSON, or event-stream data often contains epoch-based timestamps. Converting them lets you group, sort, and filter records by time.

Take Quiz

Real Codebase Usage

In real Go codebases, developers usually do more than a simple conversion.

Parse, validate, convert

If a timestamp comes in as a string, a common pattern is:

func parseUnixTimestamp(raw string) (time.Time, error) {
    seconds, err := strconv.ParseInt(raw, 10, 64)
    if err != nil {
        return time.Time{}, fmt.Errorf("invalid unix timestamp: %w", err)
    }

    return time.Unix(seconds, 0), nil
}

This pattern keeps parsing logic reusable.

Guard clauses

Developers often reject invalid input early:

func process(raw string) error {
    if raw == "" {
        return fmt.Errorf("timestamp is required")
    }

    seconds, err := strconv.ParseInt(raw, 10, 64)
    if err != nil {
        return fmt.Errorf("bad timestamp: %w", err)
    }

    tm := time.Unix(seconds, 0)
    fmt.Println(tm)
    return nil
}

Handling milliseconds

Some systems send milliseconds, not seconds. A common codebase pattern is dividing by 1000 or using time.UnixMilli() when available.

ms := int64(1405544146000)
t := time.UnixMilli(ms)

If using older code or different style:

ms := int64(1405544146000)
t := time.Unix(0, ms*int64(time.Millisecond))

Time zone normalization

Teams often normalize to UTC before storing or comparing times:

t := time.Unix(seconds, 0).UTC()

This reduces confusion in distributed systems.

JSON and API layers

A service may receive a timestamp string, convert it, validate it, and then store a time.Time in the domain model. This keeps the rest of the code working with proper time values instead of raw strings.

Common Mistakes

1. Using time.Parse() for Unix timestamps

Broken code:

tm, err := time.Parse("1136239445", "1405544146")

Why it is wrong:

  • time.Parse() expects a layout based on Go's reference date.
  • A Unix timestamp is not a formatted date string.

Fix:

tm := time.Unix(1405544146, 0)

2. Forgetting to convert a string to an integer first

Broken code:

raw := "1405544146"
tm := time.Unix(raw, 0)

Why it is wrong:

  • time.Unix() expects integer values, not strings.

Fix:

seconds, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
    panic(err)
}
tm := time.Unix(seconds, 0)

3. Confusing seconds with milliseconds

Broken code:

ts := int64(1405544146000)
tm := time.Unix(ts, 0)

Why it is wrong:

  • That value looks like milliseconds, but time.Unix() interprets it as seconds.
  • The result will be far in the future.

Fix:

ts := int64(1405544146000)
tm := time.UnixMilli(ts)

Or:

tm := time.Unix(0, ts*int64(time.Millisecond))

4. Being confused by time zones

Broken expectation:

fmt.Println(time.Unix(1405544146, 0))

Why it may seem wrong:

  • The printed time may use your local time zone.
  • The timestamp itself is still correct.

Fix:

fmt.Println(time.Unix(1405544146, 0).UTC())

5. Ignoring parse errors from string input

Broken code:

seconds, _ := strconv.ParseInt("not-a-number", 10, 64)
tm := time.Unix(seconds, 0)

Why it is risky:

  • Ignoring the error can silently produce bad results.

Fix:

seconds, err := strconv.ParseInt("not-a-number", 10, 64)
if err != nil {
    return
}
tm := time.Unix(seconds, 0)

Comparisons

TaskCorrect Go ToolExample
Parse a formatted date stringtime.Parse()time.Parse("2006-01-02", "2024-06-10")
Convert Unix seconds to time.Timetime.Unix()time.Unix(1405544146, 0)
Convert Unix milliseconds to time.Timetime.UnixMilli()time.UnixMilli(1405544146000)
Convert Unix nanoseconds to time.Timetime.Unix(0, ns) or time.UnixNano patternstime.Unix(0, 1405544146000000000)

time.Parse() vs time.Unix()

Featuretime.Parse()time.Unix()
Input typeFormatted stringNumeric epoch value
Example input"2024-06-10 15:00:00"1405544146
Needs layout?YesNo
Best forHuman-readable datesUnix timestamps

Seconds vs milliseconds

Timestamp typeExampleCorrect conversion
Seconds1405544146time.Unix(ts, 0)
Milliseconds1405544146000time.UnixMilli(ts)
Nanoseconds1405544146000000000time.Unix(0, ts)
Take Quiz

Cheat Sheet

// Formatted date string -> time.Time
 t, err := time.Parse("2006-01-02 15:04:05", "2024-06-10 12:30:00")

// Unix seconds -> time.Time
 t := time.Unix(1405544146, 0)

// Unix milliseconds -> time.Time
 t := time.UnixMilli(1405544146000)

// String timestamp -> int64 -> time.Time
 s := "1405544146"
 seconds, err := strconv.ParseInt(s, 10, 64)
 t := time.Unix(seconds, 0)

// Show in UTC
 fmt.Println(t.UTC())

Rules to remember

  • time.Parse() is for formatted date strings.
  • time.Unix() is for Unix timestamps in seconds.
  • Use strconv.ParseInt() if the timestamp starts as a string.
  • Watch out for milliseconds vs seconds.
  • Use .UTC() when you want consistent output.

Common layouts in Go

"2006-01-02"
"15:04:05"
"2006-01-02 15:04:05"

These are based on Go's reference time, not custom placeholders like YYYY or MM.

FAQ

Why does time.Parse() not work with Unix timestamps in Go?

time.Parse() expects a formatted date string and a Go layout based on the reference date. A Unix timestamp is just a number of seconds since the epoch, so time.Unix() is the correct function.

How do I convert a Unix timestamp string to time.Time in Go?

First convert the string to int64 with strconv.ParseInt(), then pass it to time.Unix().

seconds, err := strconv.ParseInt("1405544146", 10, 64)
t := time.Unix(seconds, 0)

What is the difference between time.Unix() and time.UnixMilli()?

  • time.Unix() expects seconds.
  • time.UnixMilli() expects milliseconds.

Use the one that matches your input data.

Why does the printed time look different from what I expected?

Go may print the time in your local time zone. Try using .UTC() to see the timestamp in UTC.

What type should I use for Unix timestamps in Go?

int64 is the usual choice because time.Unix() expects int64 values.

Can I parse "2024-06-10" with time.Unix()?

No. That is a formatted date string, so use time.Parse() instead.

How do I know whether a timestamp is in seconds or milliseconds?

Milliseconds usually have more digits. For modern dates:

  • seconds: around 10 digits
  • milliseconds: around 13 digits

Always check the API or data source documentation.

Take Quiz

Mini Project

Description

Build a small Go program that reads Unix timestamps from strings, converts them into time.Time, and prints both local time and UTC time. This demonstrates the full workflow you will often use in APIs, scripts, or data import tools.

Goal

Create a program that safely converts string-based Unix timestamps into readable Go time.Time values.

Requirements

  • Accept at least three timestamp strings in a slice.
  • Convert each string to an integer using proper error handling.
  • Convert valid timestamps to time.Time using time.Unix().
  • Print both the local time and UTC version for each valid timestamp.
  • Skip invalid values without crashing the whole program.
Take Quiz

Keep learning

Related questions

View more