このエントリーをはてなブックマークに追加

Go言語で時刻を

来たるべきGoConに向けていろいろ調査。やっぱり事前準備が重要ですよね。

go-modeのインストール

エディタはもちろんemacsですが、さすがにgo-modeは標準で入っていないので入れます。

  1. melpaを追加

    (require 'package)
    (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)
    (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))
    (package-initialize)
    
  2. M-x list-package

  3. go-modeを探してiでインストールの印をつけてxでインストール

これだけで準備OKです。

さて、言語の勉強はlocaltimeからはじめるのが流行りなのでgoの時刻関係ライブラリであるtime packageを調べてみます。

time packageを使ってみる

http://golang.org/pkg/time/にドキュメントがあります。

適当なディレクトリを掘って、main.goというファイルを作り、以下のように書きます。

package main

import (
     "fmt"
     "time"
)

func main() {
       t := time.Now() // 現在時刻を得る
       fmt.Println(t)  // 表示
}

pygmentsはgoにも対応しているのが素敵ですね。あとは go run で実行です。

% go run main.go
2013-04-09 22:57:14.831084 +0900 JST
%

goはコンパイルする言語ですが、go runで実行することで、スクリプト言語のように逐次コンパイルして実行してくれます。

さて、現在時刻も得られたので、もう少しいろいろしてみます。

時刻の一部を取得

fmt.Println(t.Day())
fmt.Println(t.ISOWeek())
fmt.Println(t.Unix())
fmt.Println(t.Location())
fmt.Println(t.MarshalJSON())

 /* 結果
 9
 2013 15
 1365548234
 UTC
 [34 50 48 49 51 45 48 52 45 48 57 84 50 50 58 53 55 58 49 52 90 34] <nil>
 */

簡単ですね。

文字列から時刻へ

指定の文字列を指定のフォーマットに従って認識して時刻に直します。pythonでいうと、datetime.strptimeですね。

var timeformat = "2006-01-02 15:04:05"
t,err := time.Parse(timeformat, "2013-04-09 22:57:14")
if err != nil{
   panic(err)
}

(追記: 初出時timeformatが一部間違っていました。コメントでの指摘ありがとうございます)

ここで注目なのはtimeformatです。goでは、pythonやperlなどのようなdateコマンドの%Yとかは使いません。

Mon Jan 2 15:04:05 MST 2006 (MST is GMT-0700)

という文字列からそれぞれの要素を取ってformatとします。

例をいくつか出します。

"%Y%M%dT%H%m%s"の場合、
var timeformat = "20060102T150405" を使う
"%H%m%s %Y/%M/%d "とかが仮にあった場合
var timeformat = "150405 2006/01/02" を使う

つまり、timeformatだからといって好き勝手な文字列を使うのではなく、上記の文字列から選んでね、ということです。

一見%Yとかの方がいい気もしますが、あれ言語によって微妙に違ったり、%mと%Mがごっちゃになったりするしで、この方がわかりやすいと思います。

時刻から文字列へ

fmt.Println()でいい感じにISO-8601にのっとって出力してくれますが、もう少し変えたい場合もあります。pythonでいうと、datetime.strftimeです。

fmt.Println(t.Format("20060102 150405"))
// 結果
// 20130409 225714

Format()を使います。この形式もParseと同じですね。

未来か判定

2つのTime型を比べて未来かどうかを判定します。

t, err := time.Parse(timeformat, "2013-04-09 22:57:14")
f, err := time.Parse(timeformat, "2012-01-01 22:57:14")
fmt.Println(f.After(t))
// -> false

After()を使います。あるいはBefore()を使ってもいいですね。

時刻の差分

pythonでいうと、datetime.timedeltaですね。

t, err := time.Parse(timeformat, "2013-04-09 22:57:14")
f, err := time.Parse(timeformat, "2012-01-01 22:57:14")
fmt.Println(f.Sub(t))
// -> -11136h0m0s

Sub()の返り値はDuration型となります。Duration型は時間までなようですね。

一日後、一日前

AddDate()を使います。もちろん、負の値も対応です。

var timeformat = "2006-01-02 15:04:05"
f, err := time.Parse(timeformat, "2012-12-31 00:00:00")
if err != nil {
   panic(err)
}
fmt.Println(f.AddDate(0, 0, 1))
// -> 2013-01-01 00:00:00 +0000 UTC

// 日が足りない場合は足される
t, err := time.Parse(timeformat, "2013-01-31 00:00:00")
fmt.Println(t.AddDate(0, 1, 0))
// -> 2013-03-03 00:00:00 +0000 UTC

// うるう年も対応
t2, err := time.Parse(timeformat, "2012-03-01 00:00:00")
fmt.Println(t2.AddDate(0, 0, -1))
// -> 2012-02-29 00:00:00 +0000 UTC
t3, err := time.Parse(timeformat, "2012-02-29 00:00:00")
fmt.Println(t3.AddDate(1, 0, 0))
// -> 2013-03-01 00:00:00 +0000 UTC

Sleep

ここまでは時刻関連でしたが、timeパッケージはsleepなどもできます。

time.Sleep(100 * time.Millisecond)

タイムアウト

After()を使うことで指定した時間後にchannelを通じてメッセージが送られてきます。Erlangみたいですね。

c1 := make(chan string)

select {
case msg1 := <- c1:
   fmt.Println("Message 1", msg1)
case <- time.After(time.Second):
   fmt.Println("timeout")
}

一定間隔で

Tickを使うことで、指定した時間の間隔でメッセージが送られてきます。

c := time.Tick(1 * time.Second)
for now := range c {
   fmt.Printf("%v\n", now)
}

まとめ

go言語のtimeパッケージを使ってみました。時刻関係はいろいろハマりどころがあるですが、標準でここまで揃ってると使いやすいですね。

また、go言語の魅力であるchannelを使う例も示しました。channelと組み合わせることでいろいろな広がりが出てきますね。(といっても、自分ではChannelを使いこなせてないんですが…)