post Image
Golangのgormで、カスタマイズしたtime.Timeに差し変える

gormというORMで、デフォルトで定義されている論理削除可能な構造体は、下記のようなものですが、

// gorm.Model
type Model struct {
    ID        uint64 `gorm:"primary_key"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt *time.Time
}

これをクライアント側に返却するとき、時刻をJSONエンコードなりTextエンコードなりすると思いますが、
そのまま使うと時刻文字列のフォーマットが、RFC3339Nanoというナノ秒まで持つフォーマットを使って変換されてしまいます。

// Time Format
const (
    RFC3339     = "2006-01-02T15:04:05Z07:00"
    RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
)

// JSON Encode
func (t Time) MarshalJSON() ([]byte, error) {
    if y := t.Year(); y < 0 || y >= 10000 {
        // RFC 3339 is clear that years are 4 digits exactly.
        // See golang.org/issue/4556#c15 for more discussion.
        return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
    }
    return []byte(t.Format(`"` + RFC3339Nano + `"`)), nil
}

クライアントによっては、ナノ秒など解釈できないものがあるためかなり不便です。
普通そんな細かい精度の時刻など必要としない場合の方が多いため、 ミリ秒以下を切り捨てたいところです。

そんなときは、時刻のタイプ自体をカスタマイズすることで解決することが出来ます。

restime.go
package typedef

import (
    "database/sql/driver"
    "errors"
    "time"
)

// XXX: アプリでナノ秒は必要ないので秒まででフォーマット
//{
//    "createdAt": "2015-08-09T15:03:05.135166971Z",
//    "objectID": "55c112a40000a1"
//}
// See: http://qiita.com/taizo/items/2c3a338f1aeea86ce9e2
type ResourceTime struct {
    time.Time
}

func (rt ResourceTime) MarshalJSON() ([]byte, error) {
    t := rt.Time
    if y := t.Year(); y < 0 || y >= 10000 {
        return nil, errors.New("ResourceTime.MarshalJSON: year outside of range [0,9999]")
    }
    return []byte(t.Format(`"` + time.RFC3339 + `"`)), nil
}

func (rt ResourceTime) MarshalText() ([]byte, error) {
    t := rt.Time
    if y := t.Year(); y < 0 || y >= 10000 {
        return nil, errors.New("Time.MarshalText: year outside of range [0,9999]")
    }
    return []byte(t.Format(time.RFC3339)), nil
}

// Sql driver interface

func (rt *ResourceTime) Scan(value interface{}) error {
    rt.Time = value.(time.Time)
    return nil
}

func (rt ResourceTime) Value() (driver.Value, error) {
    return rt.Time, nil
}

やってることは、

  • エンコードの時刻文字列フォーマットを秒数まで出力するものに変更
  • SQL Driverが実装すべきメソッドを定義(ScanValue)

これだけでクライアントに返却する時刻文字列のフォーマットを変更することができました。

type Model struct {
    ID        uint64 `gorm:"primary_key"`
    CreatedAt ResourceTime `json:"createdAt"`
    UpdatedAt ResourceTime `json:"updatedAt"`
    DeletedAt *time.Time   `json:"deletedAt,omitempty"`
}

未解決事項

論理削除で使われる DeleteAtは、time.Timeへのポインターで定義されていますが、これを *ResourceTimeに変更するとMySQLにカラムは追加されますが、実行時、レコード追加するときにエラーになります。
今のところよくわからなかったので、別途 view objectみたいな構造体を定義して詰め直し、時刻フォーマットを変更する方法で回避しました。
詳細は、この辺参考 JSON and struct composition in Go

参考

環境

  • Golang 1.4.2
  • MySQL 5.6.26

『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。