1つのプログラムで見る基本
time.Now() は現在のローカル時刻を返し、time.Sleep は現在のゴルーチンを一時停止し、time.Since は経過時間を測り、time.Date は特定の瞬間を組み立てます。このページの残りでは、それぞれを順に取り上げます。例は固定の日付とUTCを使っているので、どこで実行しても出力は同じです。
SleepとDuration
time.Duration はナノ秒を数える int64 です。パッケージには、読みやすい値を作るための定数が定義されています。
| 定数 | 値 |
|---|---|
time.Nanosecond | 1 |
time.Microsecond | 1000 ns |
time.Millisecond | 1000 µs |
time.Second | 1000 ms |
time.Minute | 60 s |
time.Hour | 60 min |
タイムゾーンが夏時間で切り替わるときは1日が24時間とは限らないので、time.Day はありません。暦の日数には AddDate を使います(後述)。
Duration が整数であることから、よくあるバグが2つ生まれます。
time.Sleep(5)は5 ナノ秒 スリープします。必ず単位を掛けます:time.Sleep(5 * time.Second)。nがintの変数のとき、time.Sleep(n * time.Second)はコンパイルできません。変換します:time.Duration(n) * time.Second。5のような定数は、型なし定数がDuration型に合わせて変わるので、変換なしで使えます。
ParseDuration は ns、us(または µs)、ms、s、m、h を受け付け、"2h45m" や "1.5s" のように組み合わせられます。日の単位はありません。
フォーマット:2006-01-02という基準レイアウト
Goは %Y-%m-%d も yyyy-MM-dd も使いません。レイアウトとは、基準時刻
Mon Jan 2 15:04:05 MST 2006
を、出力したい形で書いたものです。値はそれぞれが一意になるよう選ばれています。月が 1、日が 2、時が 3(または 15)、分が 4、秒が 5、年が 6(2006)、タイムゾーンのオフセットが -7(-0700)です。01/02 03:04:05PM '06 -0700 と読むと覚えやすいでしょう。
最もよく使うレイアウトのトークンは次のとおりです。
| トークン | 意味 | 例 |
|---|---|---|
2006 / 06 | 年、4桁または2桁 | 2026 / 26 |
01 / 1 / Jan / January | 月 | 03 / 3 / Mar / March |
02 / 2 / _2 | 日(ゼロ埋め、そのまま、空白埋め) | 05 / 5 / " 5" |
Mon / Monday | 曜日 | Thu / Thursday |
15 | 時、24時間制 | 09 |
03 / 3 | 時、12時間制 | 09 / 9 |
04 / 4 | 分 | 07 / 7 |
05 / 5 | 秒 | 03 / 3 |
PM / pm | 午前・午後の表示 | AM |
.000 / .999 | 秒の小数部(固定桁 / 末尾のゼロを削る) | .250 / .25 |
MST | タイムゾーンの略称 | UTC |
-0700 / -07:00 | 数値のタイムゾーンオフセット | +0000 / +00:00 |
Z07:00 | -07:00 と同様だが、UTCでは Z と表示 | Z |
定義済みのレイアウトは次のとおりです。
| 定数 | レイアウト |
|---|---|
time.RFC3339 | 2006-01-02T15:04:05Z07:00(APIとJSONにはこれを使う) |
time.RFC3339Nano | 2006-01-02T15:04:05.999999999Z07:00 |
time.DateTime(Go 1.20) | 2006-01-02 15:04:05 |
time.DateOnly(Go 1.20) | 2006-01-02 |
time.TimeOnly(Go 1.20) | 15:04:05 |
time.Kitchen | 3:04PM |
time.RFC1123 | Mon, 02 Jan 2006 15:04:05 MST(HTTPの日付には代わりに http.TimeFormat を使う) |
典型的な間違いは、"2023-01-01" のように違う数字でレイアウトを書くことです。Goはそれを拒否しません。認識しない文字はそのままコピーし、認識するものは置き換えます。それぞれの 2 は日、3 は12時間制の時、2つの 01 は月なので、3月5日9時07分は 5059-03-03 と整形されます。"YYYY-MM-DD" のようなレイアウトにはトークンがまったくないので、そのまま表示されます。整形された日付がおかしく見えたら、レイアウトが基準の値を正確に使っているか確認してください。
文字列を時刻にパースする
time.Parse(layout, value) は同じレイアウトを使い、チェックが必要なエラーを返します。
入力にタイムゾーンがなければ、Parse はUTCを返します。同じ壁時計の文字列でもタイムゾーンによって別の瞬間を意味しうるので、ParseInLocation があります。エラーメッセージは一致しなかった部分を示すので、レイアウトのデバッグに役立ちます。
タイムゾーン
time.Time は、瞬間と、表示に使うロケーションの組です。In でロケーションを変えると表示の仕方が変わるだけで、どの瞬間かは変わりません。
time.UTCは常に使えます。時刻はUTC(またはオフセット付きのRFC 3339)で保存・送信し、表示のときだけ変換します。time.Localはマシンのタイムゾーンです。サーバーやコンテナではUTCであることが多く、ノートPCではそうでないので、それに依存するコードは場所によって違う振る舞いをします。time.LoadLocation("Asia/Tokyo")はOSからIANAのデータベースを読みます。最小限のコンテナイメージにはそれがないことが多く、その場合この呼び出しはエラーを返します。ブランクインポート_ "time/tzdata"はデータベースをバイナリに埋め込む(約450KB)ので、常に動くようになります。エラーは必ず処理してください。time.FixedZone(name, offsetSeconds)は一定のオフセットを持つタイムゾーンを作ります。夏時間のルールを持たないので、名前付きの地域ではなく、受け取ったオフセットに使います。
計算と比較
出力で注目すべき点は次のとおりです。
- 1月31日に対する
AddDate(0, 1, 0)は、2月28日ではなく 3月3日 になります。Goは1か月を足して「2月31日」を得てから、あふれた分を正規化します。「翌月の同じ日、ただし月末で止める」が必要なら、その処理は自分で書きます。 SubはDurationを返し、その上限は約292年です。暦の日数の差には、UTCの0時に揃えた日付を比較し、24時間で割ります。- 1日の始まりを得るには、
Date()から時刻を組み立て直します。t.Truncate(24 * time.Hour)はUTCのゼロ時刻を基準に丸めるので、UTC以外のタイムゾーンでは間違った答えになります。
比較には Equal、Before、After を使い、== は決して使いません。 time.Now() はモノトニッククロックの読み取り値を含んでおり、壁時計が調整されても time.Since が正しくなるよう使われます。== はその読み取り値とロケーションも比較するので、同じ瞬間を表す2つの Time の値が等しくならないことがあります。同じ理由で、time.Time を正規化せずにマップのキーに使ってはいけません(t.UTC().Round(0) でモノトニックの読み取り値を取り除けます)。
ゼロ値の time.Time は、UTCの1年1月1日0時0分です。t.IsZero() でチェックします。
経過時間を測る
start := time.Now()
doWork()
log.Printf("doWork took %v", time.Since(start))
time.Since(start) は time.Now().Sub(start) で、time.Until(deadline) は deadline.Sub(time.Now()) です。どちらも使えるときはモノトニッククロックを使うので、システムの時計が変わっても安全です。コードのベンチマークには、手で計測するのではなくtestingパッケージのベンチマーク機能を使います。
タイマーとティッカー
time.After(d) は、d の後に一度受信できるチャネルを返します。time.Timer は同じものに Stop メソッドが付いたものです。time.Ticker は止めるまで周期ごとに値を届けます。
20、40、60ミリ秒の3回のティックは110ミリ秒のタイマーよりずっと前に届くので、プログラムは3回目のティックの後に止まります。time.AfterFunc(d, f) は d の後に専用のゴルーチンで f を実行し、一度きりの遅延処理に便利です。
Go 1.23以降、参照されなくなったタイマーとティッカーは Stop を呼ばなくてもガベージコレクションされ、止めたりリセットしたりしたタイマーのチャネルが古い値を届けることもなくなりました。それでも、defer で Stop を呼ぶのが、ティッカーの役目が終わったことを示すわかりやすい方法です。時間制限が1回の待機ではなく操作全体にかかるなら、たいていはタイマーより context.WithTimeout のほうが読みやすくなります。
JSONの中の時刻
time.Time は、JSONではナノ秒の精度でRFC 3339の文字列と自動的に相互変換されます。
type Event struct {
Name string `json:"name"`
At time.Time `json:"at"`
}
// {"name":"deploy","at":"2026-09-23T14:30:00Z"}
別の形式のJSON文字列はアンマーシャルに失敗します。Unixタイムスタンプや独自の形式には、int64 か文字列で保存して変換するか、独自の UnmarshalJSON を持つ型を定義します。
よくある間違い
time.Sleep(1)やtime.Sleep(n)に数値だけを渡す。 それはナノ秒です。- 間違った数字のレイアウト。
"2023-01-01"や"YYYY-MM-DD"はレイアウトではありません。"2006-01-02"を使います。 - 分と月を取り違える。 分は
04、月は01です。"15:01"は時の後に分ではなく月を表示します。 ==で比較する。Equalを使います。time.Localに頼る。 マシンによって違います。time.UTCか読み込んだロケーションで明示します。LoadLocationやParseのエラーを無視する。 どちらも実際の入力で失敗します。失敗したParseはゼロの時刻を返し、それは1年として表示されます。失敗したLoadLocationはnilのロケーションを返し、t.In(loc)をpanicさせます。
よくある質問
Goでスリープするには?
time.Duration を渡して time.Sleep を呼びます:time.Sleep(2 * time.Second) や time.Sleep(500 * time.Millisecond)。time.Sleep(2) のように数値だけを書いてもコンパイルできますが、Durationはナノ秒を数えるので2ナノ秒しかスリープしません。Sleepがブロックするのは現在のゴルーチンだけです。
なぜGoの日付フォーマットは2006-01-02 15:04:05なのですか?
Goは例を示すことで日付を整形します。レイアウトは、基準時刻 Mon Jan 2 15:04:05 MST 2006 を出力したい形で書いたものです。その各部分はアメリカ式の順で数が増えていきます。月が1、日が2、時が3(24時間制では15)、分が4、秒が5、年が6(2006)、タイムゾーンのオフセットが7(-0700)です。ですから "2006-01-02" は年-月-日、"02/01/2006" は日/月/年を意味します。
Goで日付の文字列をパースするには?
基準時刻で書いたレイアウトとともに time.Parse(layout, value) を使います:t, err := time.Parse("2006-01-02", "2026-09-23")。err は必ずチェックします。文字列にタイムゾーンの情報がなければ結果はUTCになります。別のタイムゾーンとして解釈するには time.ParseInLocation を使います。
GoでUnixタイムスタンプを取得するには?
time.Now().Unix() は、UTCの1970年1月1日からの秒数を int64 で返します。UnixMilli()、UnixMicro()、UnixNano() はより細かい単位を返します。逆方向には time.Unix(sec, 0) や time.UnixMilli(ms) を使います。
Goで2つの時刻を比較するには?
t1.Before(t2)、t1.After(t2)、t1.Equal(t2) を使います。== は使ってはいけません。ロケーションとモノトニッククロックの読み取り値も比較するので、同じ瞬間を表す2つの値が等しくならないことがあります。t2.Sub(t1) で差をDurationとして得られます。