覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Go言語 - 日時のフォーマット処理

本エントリでは、Go言語で日時を特定の書式にフォーマットする処理を実行してみます。

基本的にはtimeパッケージのTime.Format関数を利用することになります。


例えばJavaなら、LocalDateTime#format やら SimpleDateFormat#format 等を利用してフォーマットを行いますが、
その時に与える書式は例えばyyyy/MM/dd HH:mm:ssという文字列になります。

で、これと同じことをGoでやろうとした場合、指定する書式は

2006/01/02 15:04:05

です。具体的な上記の日時を書式として与える必要があります。

慣れの問題なんでしょうけど、具体的な値を指定するというのは私には違和感がありました。

一応典型的な書式はtimeパッケージでconstとして提供されています。(が、日本人好みのスラッシュが入った書式は無いです・・)

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)に固定されていて、例えば年部分を 2017 にしたり、月部分を 12 にしたり とかはできません。(やると結果がおかしなことになります)

ただし、12時間表記にするために時間部分の 15 を 03 にしたり、 ゼロ埋めしないように 秒部分の 05 を 5 にしたり等はOKです。


以下で現在日時を色々な書式でフォーマットして出力してみました。

■サンプルコード

package main

import (
    "fmt"
    "time"
)

func main() {
    nowTime := time.Now()

    const format1 = "2006/01/02 15:04:05" // 24h表現、0埋めあり
    fmt.Printf("now -> %s\n", nowTime.Format(format1))

    const format2 = "2006/1/2 3:4:5" // 12h表現、0埋め無し
    fmt.Printf("now -> %s\n", nowTime.Format(format2))

    const DateFormat = "2006/01/02"
    const TimeFormat = "15:04:05"
    const MilliFormat = "2006/01/02 15:04:05.000"
    const MicroFormat = "2006/01/02 15:04:05.000000"
    const NanoFormat = "2006/01/02 15:04:05.000000000"

    fmt.Printf("yyyy/MM/dd -> %s\n", nowTime.Format(DateFormat))
    fmt.Printf("HH:mm:ss   -> %s\n", nowTime.Format(TimeFormat))

    // ミリ秒まで出力
    fmt.Printf("Milli -> %s\n", nowTime.Format(MilliFormat))

    // マイクロ秒まで出力
    fmt.Printf("Micro -> %s\n", nowTime.Format(MicroFormat))

    // ナノ秒まで出力
    fmt.Printf("Nano  -> %s\n", nowTime.Format(NanoFormat))

    // Unixtimeに変換
    unixTime := nowTime.Unix()
    fmt.Printf("unixTime -> %d\n", unixTime)
}


■実行結果

now -> 2017/05/24 19:21:04
now -> 2017/5/24 7:21:4
yyyy/MM/dd -> 2017/05/24
HH:mm:ss   -> 19:21:04
Milli -> 2017/05/24 19:21:04.135
Micro -> 2017/05/24 19:21:04.135562
Nano  -> 2017/05/24 19:21:04.135562200
unixTime -> 1495621264


というわけで無事に日時のフォーマット処理ができました。(2006/01/02 15:04:05を頭に入れとこう。)



関連記事

Go言語 - melodyを利用してWebSocketサーバを立てる

melodyGinを使ってWebSocketサーバを立てて、簡易的なチャット画面を作ってみました。

実際には、melody Exampleを、ほぼそのまま流用しているだけです。


準備

以下を実行してmelodyを利用可能な状態にしておきます

go get gopkg.in/olahol/melody.v1


プログラムとhtml

WebSocketサーバと動作確認用チャット画面のindex.htmlとして以下を用意しました。

■WebSocketサーバ(Goプログラム)

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    "gopkg.in/olahol/melody.v1"
)

func main() {
    log.Println("Websocket App start.")

    router := gin.Default()
    m := melody.New()

    rg := router.Group("/sampleapp")
    rg.GET("/", func(ctx *gin.Context) {
        http.ServeFile(ctx.Writer, ctx.Request, "index.html")
    })

    rg.GET("/ws", func(ctx *gin.Context) {
        m.HandleRequest(ctx.Writer, ctx.Request)
    })

    m.HandleMessage(func(s *melody.Session, msg []byte) {
        m.Broadcast(msg)
    })

    m.HandleConnect(func(s *melody.Session) {
        log.Printf("websocket connection open. [session: %#v]\n", s)
    })

    m.HandleDisconnect(func(s *melody.Session) {
        log.Printf("websocket connection close. [session: %#v]\n", s)
    })

    // Listen and server on 0.0.0.0:8989
    router.Run(":8989")

    fmt.Println("Websocket App End.")
}


■index.html

<html>
  <head>
    <title>Chat powered by Melody</title>
  </head>

  <style>
    #chat {
      text-align: left;
      color:#ffffff;
      background: #113131;
      width: 400px;
      min-height: 300px;
      padding: 10px;
      font-family: 'Lucida Grande', 'Hiragino Kaku Gothic ProN', 'ヒラギノ角ゴ ProN W3', 'Meiryo', 'メイリオ', sans-serif;
      font-size: small;
    }
  </style>

  <body>

    <center>
      <h3>Sample Chat</h3>
      <pre id="chat"></pre>
      <label id="title"></label>
      <input placeholder="say something" id="text" type="text">
    </center>

    <script>
      var url = "ws://" + window.location.host + "/sampleapp/ws";
      var ws = new WebSocket(url);
      var name = "Guest-" + Math.floor(Math.random() * 1000);
      var chat = document.getElementById("chat");
      document.getElementById("title").innerText = name + ": ";
      
      var text = document.getElementById("text");
      var now = function () {
        return new Date().toLocaleString();
      };

      ws.onmessage = function (msg) {
        var line =  now() + " : " + msg.data + "\n";
        chat.innerText += line;
      };

      text.onkeydown = function (e) {
        if (e.keyCode === 13 && text.value !== "") {
          ws.send("[" + name + "] > " + text.value);
          text.value = "";
        }
      };
    </script>

  </body>
</html>


実行してみた

対象のアプリを実行します。実行すると以下のようなログがコンソールに出力されます

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /sampleapp/               --> main.main.func1 (3 handlers)
[GIN-debug] GET    /sampleapp/ws             --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8989


http://localhost:8989/sampleapp/にアクセスすることでチャット画面が利用できました


f:id:nini_y:20170523160815g:plain


というわけで、お遊び程度ですがかなりコード量少なくWebSocketのサーバサイドを実装することができました。



関連エントリ

Go言語 - Goの特徴

Go言語(Golang)の特徴(メリット)として以下のようなものが一般的に挙げられています


高パフォーマンス

一般的に軽量言語(LL)ではメモリ管理のコストなどに注意してコードを構成しなければパフォーマンスが極端に悪くなることがあります。
それに対して、Goは細部を気にせずにコードを書いても比較的パフォーマンスを引き出しやすい傾向があります。


メモリ管理からの解放

C/C++等はメモリ管理を手動で行う必要がありますが、Goではメモリ管理をGoランライムに任せることができます


コンパイル速度の速さ

C/C++等に比べてGoのコンパイル速度は極端に早いです。
コンパイル速度が速いので、コードを修正してビルド・実行という開発の流れを気軽に何度も実行することができます


スタイルやコード整形

プログラムをチーム開発している際に、どの書式スタイルに統一するかという議論がなされることがあります。
Goではスタイルの規定が最初からしっかりしていて、かつ、そのスタイルをサポートするためのコード整形ツールも揃っています。
このことにより、Goではコードのスタイルに関して質を簡単に保つことができます。


シンプルな言語仕様

Goは機能の割にとてもシンプルな仕様で作られています。
人によっては1日程度で言語仕様を理解することもできます。


シングルバイナリ

Goで書かれたプログラムは基本的には単体で実行可能なシングルバイナリとして生成されます。
一旦コンパイルしてしまえばLL系言語で必要なランタイムや依存関係の管理は必要なくなります。
アプリのインストール・デプロイといった作業も非常に簡単になります。



関連エントリ

Go言語 - Ginを使ってHello World

Go言語ではnet/httpを利用することでHTTPサーバを立てることができまが、GinといったWebアプリ用のフレームワークも存在しています。


事前準備

以下を実行してGinのパッケージ取得を行います

go get github.com/gin-gonic/gin


とりあえずHello World

以下 Hello World の文字列を返す例です。

Engine.GETにより、ハンドリング対象のパスとハンドラを登録します。


■サンプルコード

package main

import (
    "log"

    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    router.GET("/hello", func(ctx *gin.Context) {
        ctx.String(200, "Hello World")
    })

    if err := router.Run(":8686"); err != nil {
        log.Fatal("Server Run Failed.: ", err)
    }
}


■実行結果

http://localhost:8686/helloにアクセスすると以下の結果が出力されます

Hello World


静的ページを返す

URLへのアクセスに対して静的ページを返す場合は、router.StaticFS 等を利用します。

以下サンプルでは、staticディレクトリにindex.htmlとuser.htmlというファイルを用意しているものとします

f:id:nini_y:20170519185348p:plain


■サンプルコード

package main

import (
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    router.StaticFS("/web", http.Dir("static"))

    if err := router.Run(":8686"); err != nil {
        log.Fatal("Server Run Failed.: ", err)
    }
}

■実行結果

  • http://localhost:8686/web/ に アクセスすると staticディレクトリのindex.html の内容が返される
  • http://localhost:8686/web/user.html に アクセスすると staticディレクトリのuser.html の内容が返される



関連エントリ

Go言語 - HTTPサーバでHello World

Go言語ではnet/httpを利用することでHTTPサーバを立てることができます。

とりあえず今回はお試しな感じでHTTPサーバを立ててみます


とりあえずHello World

以下 Hello World の文字列を返す例です。

http.HandleFuncにより、ハンドリング対象のパスとハンドラを登録します。その後にhttp.ListenAndServeでHTTPサーバを起動します

■サンプルコード

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {

    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World")
    })

        // ポート = 8686
    if err := http.ListenAndServe(":8686", nil); err != nil {
        log.Fatal("ListenAndServe failed.", err)
    }
}


■実行結果

http://localhost:8686/helloにアクセスすると以下の結果が出力されます

Hello World


静的ページを返す

URLへのアクセスに対して静的ページを返す場合は、http.FileServer を利用します。

以下サンプルでは、staticディレクトリにindex.htmlとuser.htmlというファイルを用意しているものとします

f:id:nini_y:20170519174645p:plain


■サンプルコード

package main

import (
    "log"
    "net/http"
)

func main() {
    http.Handle("/", http.FileServer(http.Dir("static")))

    if err := http.ListenAndServe(":8686", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

■実行結果

  • http://localhost:8686/ に アクセスすると staticディレクトリのindex.html の内容が返される
  • http://localhost:8686/user.html に アクセスすると staticディレクトリのuser.html の内容が返される


URLからprefix部分を取り除いてハンドリングする場合

アクセスされたURLのPrefixを除いて、ハンドリングする場合は http.StripPrefix を利用します

■サンプルコード

package main

import (
    "log"
    "net/http"
)

func main() {
    
    // アクセスされたURLから /web 部分を取り除いてハンドリングする
    http.Handle("/web", http.StripPrefix("/web", http.FileServer(http.Dir("static"))))

    if err := http.ListenAndServe(":8686", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

■実行結果

  • http://localhost:8686/web/ に アクセスすると staticディレクトリのindex.html の内容が返される
  • http://localhost:8686/web/user.html に アクセスすると staticディレクトリのuser.html の内容が返される



関連エントリ