Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11757] ファイルの概要

このコミットは、Go言語の標準ライブラリであるosパッケージからTime関数を削除し、時間関連の機能がtimeパッケージに集約されるように変更したものです。これにより、Go 1リリースに向けてパッケージ間の責務を明確にし、より一貫性のあるAPI設計を目指しています。具体的には、src/pkg/os/time.goファイルが削除され、os_test.goから関連するテストコードが削除されました。また、この変更がGo 1のリリースノートに反映されるよう、doc/go1.htmldoc/go1.tmplが更新されています。

コミット

  • コミットハッシュ: 7750fc894ad8697349cc9b97bb5c0a9c2201c3ae
  • Author: Brad Fitzpatrick bradfitz@golang.org
  • Date: Fri Feb 10 11:44:51 2012 +1100

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/7750fc894ad8697349cc9b97bb5c0a9c2201c3ae

元コミット内容

os: remove Time; callers should use time.Time.

Part of issue 2947

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5651051

変更の背景

この変更の主な背景は、Go言語の標準ライブラリにおけるパッケージ設計の改善とAPIの一貫性の確保です。Go 1のリリースに向けて、各パッケージが明確な責務を持つように再編成が進められていました。

以前のosパッケージには、システムコールを通じて現在の時刻を取得するTime関数が含まれていました。しかし、Go言語にはすでに時間と日付を扱うための専用のtimeパッケージが存在します。osパッケージはオペレーティングシステムとのインタフェースを提供することを主な目的としており、時間管理はtimeパッケージの責務と考えるのがより適切です。

このコミットは、os.Time関数を削除し、代わりにtimeパッケージのtime.Now()関数やtime.Time型を使用するように促すことで、以下の目的を達成しようとしています。

  1. 責務の分離: 時間関連の機能をtimeパッケージに集約することで、各パッケージの役割を明確にし、コードベースのモジュール性を向上させます。
  2. APIの一貫性: 開発者が時間情報を扱う際に、常にtimeパッケージを使用するという一貫したパターンを提供します。これにより、学習コストが削減され、コードの可読性と保守性が向上します。
  3. 冗長性の排除: 複数のパッケージに類似の機能が分散している状態を解消し、冗長なコードを削減します。

コミットメッセージにある「Part of issue 2947」は、Goプロジェクトの内部的な課題追跡システムにおける特定の課題に関連する変更であることを示唆しています。これは、Go 1リリースに向けた大規模なAPI整理の一環であったと考えられます。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念とパッケージに関する知識が必要です。

  • Go言語のパッケージシステム: Go言語のコードはパッケージに組織化されます。パッケージは関連する機能の集合であり、コードの再利用性、モジュール性、および名前空間の管理に役立ちます。標準ライブラリは多数のパッケージで構成されており、それぞれが特定の機能を提供します。
  • osパッケージ: osパッケージは、オペレーティングシステム(OS)の機能へのプラットフォームに依存しないインタフェースを提供します。これには、ファイル操作、プロセス管理、環境変数へのアクセスなどが含まれます。
  • timeパッケージ: timeパッケージは、時間と日付の操作、測定、表示のための機能を提供します。これには、現在の時刻の取得、時間の加算・減算、日付のフォーマット、タイマーやティックの作成などが含まれます。
  • syscallパッケージ: syscallパッケージは、低レベルのオペレーティングシステムプリミティブへのインタフェースを提供します。これは、Goの標準ライブラリがOS固有の機能にアクセスするために内部的に使用することが多いパッケージです。os.Time関数も、内部でsyscall.Gettimeofdayを使用していました。
  • syscall.Timeval構造体: Unix系システムにおいて、timeval構造体は秒とマイクロ秒で時間を表現するために使用されます。syscall.Gettimeofday関数は、この構造体に現在の時刻を格納します。
  • Unixエポック (Unix Epoch): Unixエポックは、1970年1月1日00:00:00 UTC(協定世界時)を指します。多くのシステムでは、この時点からの経過秒数またはミリ秒数で時間を表現します。os.Time関数もUnixエポックからの秒数とナノ秒数を返していました。
  • Go 1の互換性保証: Go言語は、Go 1リリース以降、後方互換性を非常に重視しています。しかし、Go 1リリース前は、APIの安定化のために破壊的な変更が行われることがありました。このコミットは、Go 1リリース前のAPI整理の一環として行われたものです。

技術的詳細

このコミットの技術的な詳細は、osパッケージから時間取得の責務を完全に分離し、timeパッケージに一元化することにあります。

  1. os.Time関数の削除:

    • 以前のosパッケージには、Time() (sec int64, nsec int64, err error)という関数が存在しました。
    • この関数は、内部でsyscall.Gettimeofdayを呼び出し、現在の時刻をUnixエポックからの秒数とナノ秒数で返していました。
    • このコミットにより、src/pkg/os/time.goファイル自体が削除され、os.Time関数は利用できなくなりました。
  2. os_test.goからのテストコード削除:

    • src/pkg/os/os_test.goファイルから、TestTime関数が削除されました。これは、os.Time関数が削除されたため、そのテストも不要になったためです。
    • また、os_test.go内のdot変数(テスト対象のファイルリスト)から"time.go"のエントリも削除されています。
  3. ドキュメントの更新:

    • Go 1のリリースノートとなるdoc/go1.htmldoc/go1.tmplが更新されました。
    • これらのドキュメントには、os.Time関数が削除されたこと、そして呼び出し元はtimeパッケージのTime型(実際にはtime.Now()関数がtime.Time型を返す)を使用すべきである旨が明記されました。
    • 特に、「os.Timeを使用するコードはコンパイルに失敗し、手動で更新する必要がある」という注意書きが追加されており、この変更が破壊的な変更であることを示しています。

この変更により、開発者は時間情報を取得する際に、常にtime.Now()関数を使用し、その結果として得られるtime.Time型を扱うことになります。これにより、Goの標準ライブラリ全体で時間表現の一貫性が保たれ、よりリッチな時間操作機能(フォーマット、加算、比較など)をtime.Time型を通じて利用できるようになります。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更箇所は以下の通りです。

  1. src/pkg/os/time.goの削除: このファイル全体が削除されました。このファイルには、os.Time()関数の実装が含まれていました。

    --- a/src/pkg/os/time.go
    +++ /dev/null
    @@ -1,19 +0,0 @@
    -// Copyright 2009 The Go Authors. All rights reserved.
    -// Use of this source code is governed by a BSD-style
    -// license that can be found in the LICENSE file.
    -
    -package os
    -
    -import "syscall"
    -
    -// Time returns the current time, in whole seconds and
    -// fractional nanoseconds, plus an error if any. The current
    -// time is thus 1e9*sec+nsec, in nanoseconds.  The zero of
    -// time is the Unix epoch.\n-func Time() (sec int64, nsec int64, err error) {\n-\tvar tv syscall.Timeval\n-\tif e := syscall.Gettimeofday(&tv); e != nil {\n-\t\treturn 0, 0, NewSyscallError(\"gettimeofday\", e)\n-\t}\n-\treturn int64(tv.Sec), int64(tv.Usec) * 1000, err\n-}
    
  2. src/pkg/os/os_test.goの変更: TestTime関数の削除と、dot変数からの"time.go"の削除が行われました。

    --- a/src/pkg/os/os_test.go
    +++ b/src/pkg/os/os_test.go
    @@ -23,7 +23,6 @@ var dot = []string{\n    "error.go",\n    "file.go",\n    "os_test.go",\n-   "time.go",\n    "types.go",\n    "stat_darwin.go",\n    "stat_linux.go",
    @@ -744,19 +743,6 @@ func TestChdirAndGetwd(t *testing.T) {\n    fd.Close()\n }\n \n-func TestTime(t *testing.T) {\n-\t// Just want to check that Time() is getting something.\n-\t// A common failure mode on Darwin is to get 0, 0,\n-\t// because it returns the time in registers instead of\n-\t// filling in the structure passed to the system call.\n-\t// Too bad the compiler doesn\'t know that\n-\t// 365.24*86400 is an integer.\n-\tsec, nsec, err := Time()\n-\tif sec < (2009-1970)*36524*864 {\n-\t\tt.Errorf(\"Time() = %d, %d, %s; not plausible\", sec, nsec, err)\n-\t}\n-}\n-\n func TestSeek(t *testing.T) {\n    f := newFile("TestSeek", t)\n    defer Remove(f.Name())\n    ```
    
    
  3. doc/go1.htmlおよびdoc/go1.tmplの変更: Go 1のリリースノートに、os.Timeの削除とtimeパッケージへの移行に関する記述が追加されました。

    --- a/doc/go1.html
    +++ b/doc/go1.html
    @@ -1347,7 +1347,18 @@ Code that uses the old methods will fail to compile and must be updated by hand.\n The semantic change makes it difficult for the fix tool to update automatically.\n </p>\n \n-<h3 id=\"os_fileinfo\">The os.FileInfo type</h3>\n+<h3 id=\"os\">The os package</h3>\n+\n+<p>The <code>Time</code> function has been removed; callers should use\n+the <a href=\"/pkg/time/#Time\"><code>Time</code></a> type from the\n+<code>time</code> package.</p>\n+\n+<p>\n+<em>Updating</em>:\n+Code that uses <code>os.Time</code> will fail to compile and must be updated by hand.\n+</p>\n+\n+<h4 id=\"os_fileinfo\">The os.FileInfo type</h4>\n \n <p>\n Go 1 redefines the <a href=\"/pkg/os/#FileInfo\"><code>os.FileInfo</code></a> type,
    

    doc/go1.tmplも同様の変更が加えられています。

コアとなるコードの解説

削除されたos.Time()関数の実装は以下の通りでした。

package os

import "syscall"

// Time returns the current time, in whole seconds and
// fractional nanoseconds, plus an error if any. The current
// time is thus 1e9*sec+nsec, in nanoseconds.  The zero of
// time is the Unix epoch.
func Time() (sec int64, nsec int64, err error) {
	var tv syscall.Timeval
	if e := syscall.Gettimeofday(&tv); e != nil {
		return 0, 0, NewSyscallError("gettimeofday", e)
	}
	return int64(tv.Sec), int64(tv.Usec) * 1000, err
}

この関数は、syscallパッケージのGettimeofdayシステムコールを直接呼び出して、現在の時刻をsyscall.Timeval構造体で取得していました。Timeval構造体は秒(Sec)とマイクロ秒(Usec)のフィールドを持ちます。os.Time()は、これらの値から秒とナノ秒を計算して返していました。

この関数が削除された理由は、Go言語の設計哲学に基づいています。Goの標準ライブラリは、特定の機能を提供するパッケージがその機能に特化し、他のパッケージとの依存関係を最小限に抑えるように設計されています。時間に関する機能はtimeパッケージが専門であり、osパッケージが低レベルなシステムコールを通じて時間を提供する必要はありませんでした。

開発者は、os.Time()の代わりにtime.Now()関数を使用することで、より高レベルで抽象化されたtime.Time型を取得できます。time.Time型は、時刻の表現だけでなく、タイムゾーンの扱い、時刻の比較、フォーマット、期間の計算など、豊富な機能を提供します。これにより、開発者はより安全で表現力豊かな方法で時間情報を扱うことができるようになります。

例えば、以前は以下のように時刻を取得していたかもしれません。

// 以前のコード (os.Time が存在した場合)
sec, nsec, err := os.Time()
if err != nil {
    // エラーハンドリング
}
// sec と nsec を使って時間情報を構築

このコミット以降は、以下のようにtimeパッケージを使用します。

// 現在の推奨されるコード
now := time.Now()
sec := now.Unix() // Unixエポックからの秒数
nsec := now.Nanosecond() // 秒未満のナノ秒
// now (time.Time 型) を使って時間情報を扱う

このように、os.Timeの削除は、Go言語の標準ライブラリの設計原則に沿った、よりクリーンで一貫性のあるAPIを提供するための重要なステップでした。

関連リンク

参考にした情報源リンク