スターティングGo言語7章のメモ - os/time

Goの標準パッケージを入門レベルで学ぼうと思って書き残したメモです。
スターティングGo言語の7章の以下のパッケージについて記載しています。
単なる写経に近いクソ記事ですw。

* osパッケージ
* timeパッケージ

## osパッケージ

```go
import "os"
```

### ホスト名の取得

```go
func main() {
	host, _ := os.Hostname()
	fmt.Println(host)
}
```

### 環境変数

```go
func main() {
	// 環境変数一覧を表示
	for _, env := range os.Environ(){
		fmt.Println(env)
	}

	// 環境変数の名前を指定して取得
	fmt.Println(os.Getenv("HOME"))
}
```

### プログラムの終了

```go
func main() {
	// 終了コード1で終了する
	os.Exit(1)
}
```

### プロセス情報

```go
func main() {
	fmt.Printf("プロセスID: %v\n", os.Getpid())
	fmt.Printf("親プロセスID: %v\n",os.Getppid())
	fmt.Printf("ユーザID: %v\n", os.Getuid())
	fmt.Printf("グループID: %v\n", os.Getgid())
}
```

### log.Fatal

```go
func main()  {
	_, err := os.Open("README.md")
	if err != nil {
		log.Fatal(err)
	}
}
```

### コマンドライン引数

```go
func main()  {
	for _, v := range os.Args {
		fmt.Println(v)
	}
}
```

### ファイル操作

#### 読み込み専用ファイルのオープン



```go
func main()  {
	f, err := os.Open("README.md")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
}
```

#### os.File

```go
func main()  {
	f, err := os.Open("README.md")
	if err != nil {
		log.Fatal(err)
	}

	/* []byte型のスライスにファイルの内容を読み込む */
	bs1 := make([]byte,128)
	n ,err := f.Read(bs1) // nは実際に読み込んだバイト数
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(n)

	/* ファイルのオフセットを指定して読み込む */
	bs2 := make([]byte, 128)
	n2, err := f.ReadAt(bs2, 10) // 10バイト目から読み込む
	if err != nil {
		log.Println(err)
	}
	fmt.Println(n2)

	/* ファイルのステータス取得 */
	fi, err := f.Stat()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fi.Name()) // ファイル名
	fmt.Println(fi.Size()) // ファイルサイズ
	fmt.Println(fi.Mode()) // ファイルのモード
	fmt.Println(fi.ModTime()) // ファイルの最終更新時間
	fmt.Println(fi.IsDir()) // ディレクトリかどうか
	
}
```

実際にファイルを読み込む処理を書くと、こんなかんじでしょうか。

```go
func readFile(filepath string) (lines []string) {
	f, err := os.Open(filepath)

	if err != nil {
		log.Fatal(err)
	}

	buf := make([]byte, 1024)

	for {
		n, err := f.Read(buf)
		if err != nil {
			log.Fatal(err)
		}
		if err == io.EOF { // EOFの場合抜ける
			break
		}
		str := string(buf[:n])
		fmt.Println(str)
		lines = append(lines, str)
	}
	return
}

func main() {
	filepath := "README.md"

	for _, line := range readFile(filepath) {
		fmt.Println(line)
	}
}
```

#### 新規ファイルの作成

```go
func main() {
	f, err := os.Create("NewFile.txt") // ファイル名を指定して新規作成
	if err != nil {
		log.Fatal(err)
	}

	f.Write([]byte("Hello, World\n")) // ファイルに[]byte型の内容を書き込み
	f.WriteAt([]byte("Golang"), 7)    // オフセットを指定して書き込み
	f.Seek(0, io.SeekEnd)             // ファイルの末尾にオフセットを移動
	f.WriteString("Year")             // 文字列をファイルに書き込み
}
```

#### ファイルオープン詳細

`os.OpenFile`を使う。

```go
func main() {
	f, err := os.OpenFile("README.md", os.O_RDONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(f.Name())
	defer f.Close()
}
```


| フラグ| 意味 |
|:-----|:-----|
| O_RDONLY | 読み込み専用|
| O_WRONLY| 書き込み専用|
| O_RDWR | 読み書き可能|
| O_APPEND | ファイルの末尾に追記|
| O_CREATE | ファイルが存在しなければ新規作成|
| O_TRUNC | 可能であればファイルの内容をオープン時に空にする| 


#### ファイルの削除    
    
```go
func main() {
	if err := os.Remove("NewFile.txt"); err != nil {
		log.Fatal(err)
	}
}
```

#### ファイル名の変更と移動

```go
func main() {
	if err := os.Rename("NewFile.txt", "BackupFile.txt"); err != nil {
		log.Fatal(err)
	}
}
```

### ディレクトリ操作

#### カレントディレクトリ

```go
func main() {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(dir)
}
```


#### ディレクトリの読み込み 

```go
func main() {
	f, err := os.Open(".")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	// カレントディレクトリ下のディレクトリ名を列挙
	fis, err := f.Readdir(0)
	for _, fi := range fis {
		if fi.IsDir() {
			fmt.Println(fi.Name())
		}
	}
}
```

#### ディレクトリの作成

```go
func main() {
	// カレントディレクトリ配下にディレクトリを作成
	if err := os.Mkdir("foo", 0775); err != nil {
		log.Fatal(err)
	}

	// カレントディレクトリ配下にディレクトリを一括作成
	// mkdir -p に相当
	if err := os.MkdirAll("foo/bar/baz", 0755); err != nil {
		log.Fatal(err)
	}
}
```

#### ディレクトリの削除

```go
func main() {
	if err := os.Remove("NewFile.txt"); err != nil {
		log.Fatal(err)
	}

	if err := os.RemoveAll("foo") err != nil {
		log.Fatal(err)
	}
}
```

### その他のファイル操作

#### シンボリックリンクの操作

```go
func main() {
	// シンボリックリンクbar.txtを作成
	if err := os.Symlink("foo.txt", "bar.txt"); err != nil {
		fmt.Println(err)
	}

	// シンボリックリンクのリンク先を読み込む
	path, err := os.Readlink("bar.txt")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(path)
}
```

## timeパッケージ

```go
import "time"
```

### 現在の時刻取得

```go
func main() {
	t := time.Now()
	fmt.Println(t)
}
```

### 指定した時刻の生成

```go
func main() {
	t := time.Date(2019, 2, 3, 11, 50, 0, 0, time.Local)
	fmt.Println(t)
	fmt.Println(t.Year())
	fmt.Println(t.Month())
	fmt.Println(t.Day())
	fmt.Println(t.Weekday())
	fmt.Println(t.Hour())
	fmt.Println(t.Minute())
	fmt.Println(t.Second())
	fmt.Println(t.Nanosecond())
	fmt.Println(t.Zone())
}
```

|メソッド | 戻り値の型| 意味 |
|:---|:---|:--| 
| Year| int | 年 |
| YearDay| int | 1~366|
| Month| time.Month | 月|
| Weekday| time.Weekday | 曜日|
| Day| int| 1~31|
| Hour| int| 0~23 |
| Minute| int| 0~59|
| Second| int| 0~59|
| NanoSecond| int| ナノ秒|
| Zone| string, int | タイムゾーンとオフセット秒 |



### 時刻間隔の表現

```go
func main() {
	fmt.Println(time.Hour)        // 1h0m0s
	fmt.Println(time.Minute)      // 1m0s
	fmt.Println(time.Second)      // 1s
	fmt.Println(time.Millisecond) // 1ms
	fmt.Println(time.Microsecond) // 1µs
	fmt.Println(time.Nanosecond)  // 1ns
}
```

**文字列からtime.Durationを生成**

```go
func main() {
	// 文字列からtime.Durationを生成
	duration, err := time.ParseDuration("2h30m")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v : %T", duration, duration) // 2h30m0s : time.Duration
}
```

**time.Durationの計算**

```go
func main() {
	t := time.Now()
	fmt.Println(t) // 2019-02-03 12:01:38.307595 +0900 JST m=+0.000568779
	t = t.Add(2*time.Minute + 15*time.Microsecond)
	fmt.Println(t) // 2019-02-03 12:03:38.30761 +0900 JST m=+120.000583779
}
```



### 時刻の差分を取得

```go
func main() {
	t0 := time.Date(2020, 2,1,0,0,0,0,time.Local)
	t1 := time.Now()
	
	d := t0.Sub(t1)
	fmt.Println(d) // 8699h55m41.548192s
}
```

### 時刻の比較

```go
func main() {
	t0 := time.Now()
	t1 := t0.Add(24 * time.Hour)
	fmt.Println(t0) // 2019-02-03 12:09:01.320207 +0900 JST m=+0.000428654
	fmt.Println(t1) // 2019-02-04 12:09:01.320207 +0900 JST m=+86400.000428654

	// 時刻の比較
	fmt.Println(t1.Before(t0)) // false
	fmt.Println(t1.After(t0))  // true
}
```

### 年月日の増減

```go
func main() {
	t0 := time.Date(2019,2,1,0,0,0,0,time.Local)
	fmt.Println(t0) // 2019-02-01 00:00:00 +0900 JST

	// 1年増やす
	t1 := t0.AddDate(1, 0,0)
	fmt.Println(t1) // 2020-02-01 00:00:00 +0900 JST

	// 1ヶ月減らす
	t2 := t0.AddDate(0,-1,0)
	fmt.Println(t2) // 2019-01-01 00:00:00 +0900 JST
}
```

### 文字列からの時刻生成

```go
func main() {
	// 第一引数にフォーマットを指定
	// 第二引数がパース対象の文字列
	t, err := time.Parse(time.RFC822, "02 Jan 06 15:04 MST")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(t) // 2006-01-02 15:04:00 +0000 MST
}
```

第一引数のフォーマットは以下の通り定義されている。

```go
const (
	ANSIC       = "Mon Jan _2 15:04:05 2006"
	UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
	RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
	RFC822      = "02 Jan 06 15:04 MST"
	RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
	RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
	RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
	RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
	RFC3339     = "2006-01-02T15:04:05Z07:00"
	RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
	Kitchen     = "3:04PM"
	// Handy time stamps.
	Stamp      = "Jan _2 15:04:05"
	StampMilli = "Jan _2 15:04:05.000"
	StampMicro = "Jan _2 15:04:05.000000"
	StampNano  = "Jan _2 15:04:05.000000000"
)
```

自分でフォーマットを定義する場合は、例えば以下のようにする。
注意することとして、フォーマット指定のタイムスタンプは`2006年1月2日 15:04:05`で統一されていないといけない。

このフォーマット指定に疑問を抱いたので調べたところ、アメリカの時刻の順番らしい。”1月2日午後3時4分5秒2006年”でとなっていて、1,2,3,4,5,6と並んでいる。

```go
func main() {
	t, err := time.Parse("2006年1月2日 15時04分05秒", "2019年2月3日 21時15分00秒")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(t) // 2019-02-03 21:15:00 +0000 UTC
}
```

### 時刻からの文字列生成

```go
func main() {
	t := time.Now()
	f1 := t.Format(time.RFC822)
	f2 := t.Format(time.RFC3339)
	f3 := t.Format("2006年1月2日 15時43分28秒")
	f4 := t.Format("2006/01/02")

	fmt.Printf("f1: %v (%T)\n", f1, f1) // f1: 03 Feb 19 14:05 JST (string)
	fmt.Printf("f2: %v (%T)\n", f2, f2) // f2: 2019-02-03T14:05:05+09:00 (string)
	fmt.Printf("f3: %v (%T)\n", f3, f3) // f3: 2019年2月3日 14時52分38秒 (string)
	fmt.Printf("f4: %v (%T)\n", f4, f4) // f4: 2019/02/03 (string)
}
```


### 時刻のUTC変換

```go
func main() {
	t := time.Now()
	utc := t.UTC() // UTCに変換
	fmt.Printf("utc : %v (%T)\n", utc, utc) // utc : 2019-02-03 05:07:07.324682 +0000 UTC (time.Time)
}
```

### 時刻のローカルタイム変換

```go
func main() {
	t := time.Now()
	jst := t.Local() // ローカルタイムに変換
	fmt.Printf("jst : %v (%T)\n", jst, jst) // jst : 2019-02-03 14:08:24.109525 +0900 JST (time.Time)
}
```


### UNIX時間との相互変換

```go
func main() {
	t := time.Now()
	unix := t.Unix()
	fmt.Printf("unix : %v (%T)\n", unix, unix) // unix : 1549170553 (int64)
}
```

### 指定時間のスリープ

```go
func main() {
	for i := 0; i < 10; i ++ {
		fmt.Println(i)
		time.Sleep(100 * time.Millisecond) // 100msecスリープ
	}
}
```

### time.Tick

`time.Tick`は、指定した時間間隔ごとに現在時刻を表す`time.Time`型の値が送信されるチャネルを生成する。

```go
func main() {
	// 3秒間隔で現在の時刻を送信するチャネルを定義
	ch := time.Tick(3 * time.Second)

	// 無限ループ
	for {
		t := <-ch
		fmt.Println(t) // 3秒間隔で表示される
	}
}
```

### time.After

`time.After`は、チャネルに対して指定した時間間隔後に一度だけ現在時刻を表す`time.Time`を送信する。

```go
func main() {
	// 5秒後に時刻を送信するチャネル
	ch := time.After(5 * time.Second)

	// 5秒後に表示される
	v := <-ch
	fmt.Println(v)
}
```

A Tour of Goのサンプルでは`time.Tick`と`time.After`を組み合わせて`select-for`文で以下のような使い方をしていた。

```go
func main() {
	tick := time.Tick(100 * time.Millisecond)
	boom := time.After(500 * time.Millisecond)
	for {
		select {
		case <-tick: // tickチャネルから受信したの場合
			fmt.Println("tick.")
		case <-boom: // boomチャネルから受信した場合はreturnでfor文から抜ける。
			fmt.Println("BOOM!")
			return
		default: // デフォルトの場合は50msecスリープ
			fmt.Println("    .")
			time.Sleep(50 * time.Millisecond)
		}
	}
}
```

今週は週3回のクソ記事投稿目標をなんとか達成。。