golang中的time采用的是rfc3339
时间协议,mgo.v2不能直接将time.Time
类型的字段存入mongodb
中 ,同样也不方便将mongo存储的timestamp
转为time.Timer
。因此,需要实现一个自定义的时间类型BsonTime
。为了能够对mgo.v2
的序列化支持,需要实现标准库中的Unmarshaler
接口。实现了该接口能够将mongodb和golang的时间戳打通,还够将BsonTime
类型的时间在toJson
的时候转为常用的时间字符串。
对api接口中传入的时间戳,可以使用mapstructure
和实现自定义的Decode
函数实现解析,将时间戳、事件字符串转为自定义的BsonTime
在线运行地址:https://play.golang.org/p/nciTC7rdYZC
自定义BsonTime,将golang的time.Time存入mongodb中
const timeFormart = "2006-01-02 15:04:05"
// 对struct增加 MarshalJSON ,UnmarshalJSON , String 方法,实现自定义json输出格式与打印方式
type BsonTime struct {
time.Time
}
func (t BsonTime) String() string {
return t.Time.Format(timeFormart)
}
// 实现它的json序列化方法
func (t BsonTime) MarshalJSON() ([]byte, error) {
var stamp = fmt.Sprintf("\"%s\"", t.Time.Format("2006-01-02 15:04:05"))
return []byte(stamp), nil
}
func (t *BsonTime) UnmarshalJSON(data []byte) error {
if err := t.Time.UnmarshalJSON(data); err != nil {
return err
}
return nil
}
func (t BsonTime) GetBSON() (interface{}, error) {
if t.IsZero() {
return nil, nil
}
return t.Time, nil
}
func (t *BsonTime) SetBSON(raw bson.Raw) error {
var tm time.Time
if err := raw.Unmarshal(&tm); err != nil {
return err
}
t.Time = tm
return nil
}
利用mapstructure库提供的自定义Decode函数,对struct中的BsonTime字段使用自定义的Decode方法。
func ToTimeHookFunc() mapstructure.DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if !reflect.DeepEqual(t, reflect.TypeOf(BsonTime{})) {
return data, nil
}
var tTime time.Time
var err error
switch f.Kind() {
case reflect.String:
tTime, err = time.Parse("2006-01-02 15:04:05", data.(string))
case reflect.Float64:
tTime, err = time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil
case reflect.Int64:
tTime, err = time.Unix(0, data.(int64)*int64(time.Millisecond)), nil
default:
return data, nil
}
if err != nil {
return nil, err
}
return BsonTime{
Time: tTime,
}, nil
}
}
// Map2StructWithBsonTime user `mapstructure` convert map to struct, with process time to BsonTime.
func Map2StructWithBsonTime(input map[string]interface{}, result interface{}) error {
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Metadata: nil,
DecodeHook: mapstructure.ComposeDecodeHookFunc(ToTimeHookFunc()),
Result: result,
TagName: "json",
})
if err != nil {
return err
}
if err := decoder.Decode(input); err != nil {
return err
}
return nil
}
使用
import (
"encoding/json"
"fmt"
"github.com/globalsign/mgo/bson"
"github.com/mitchellh/mapstructure"
"log"
"reflect"
"time"
)
var rawStr = `
{
"created_by": "7290310f-3574-4520-9ef1-79188bc7c997",
"created_time": 1563884700000,
"last_edited_time": 1563884700000,
"last_edited_by": "7290310f-3574-4520-9ef1-79188bc7c997",
"last_reply_at": "2019-07-01 12:34:56"
}
`
// AuthorTime must define `JSON TAG`.
type AuthorTime struct {
CreatedBy string `json:"created_by"`
CreatedAt BsonTime `json:"created_time"`
UpdatedBy string `json:"last_edited_by"`
UpdatedAt BsonTime `json:"last_edited_time"`
LastReplyAt BsonTime `json:"last_reply_at"`
}
func main() {
var paramsBind map[string]interface{} // 模拟gin的参数绑定
err := json.Unmarshal([]byte(rawStr), ¶msBind) //paramsBind需要是引用类型
if err != nil {
panic(err)
}
// 将参数转化为struct
var author AuthorTime
err = Map2StructWithBsonTime(paramsBind, &author)
if err != nil {
panic(err)
}
// 转为json
toJson, err := json.Marshal(&author)
if err != nil {
panic(err)
}
log.Println(string(toJson))
}