• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    golang xorm及time.Time自定义解决json日期格式的问题

    golang默认的time.Time类型在转为json格式时不是常用的2019-05-08 10:00:01这种格式,解决办法是自定义一个时间类型,例如

    type myTime time.Time ,然后针对myTime实现Marshaler接口的MarshalJSON方法,例如:

    package models 
    import (
     "database/sql/driver"
     "time"
    )
     
    const localDateTimeFormat string = "2006-01-02 15:04:05" 
    type LocalTime time.Time 
    func (l LocalTime) MarshalJSON() ([]byte, error) {
     b := make([]byte, 0, len(localDateTimeFormat)+2)
     b = append(b, '"')
     b = time.Time(l).AppendFormat(b, localDateTimeFormat)
     b = append(b, '"')
     return b, nil
    }
     
    func (l *LocalTime) UnmarshalJSON(b []byte) error {
     now, err := time.ParseInLocation(`"`+localDateTimeFormat+`"`, string(b), time.Local)
     *l = LocalTime(now)
     return err
    }

    上面的代码在网上随手一搜就能找到,没有什么困难的,接下来的才是本篇文章的重点,这玩意结合xorm使用时,特别是字段类型为*LocalTime的时候才需要折腾一番。

    下面是我的对应数据库表结构的struct 定义,

    type ServerInfo struct {
     ServerInfoId       string   `xorm:"varchar(32) pk server_info_id"`
     CreatedAt        LocalTime `xorm:"timestamp created"`
     UpdatedAt        LocalTime `xorm:"timestamp updated"`
     DeletedAt        *LocalTime `xorm:"timestamp deleted index"`
     OrgId          string   `xorm:"varchar(100) org_id" json:"orgId"`                        
     ServerIp         string   `xorm:"varchar(128) server_ip" json:"serverIp"`                     
     ServerNameDesc      string   `xorm:"varchar(500) server_name_desc" json:"serverNameDesc"`               
     ServerTimeNow      LocalTime `xorm:"timestamp server_time" json:"serverTime"`                     
     DataReceiveTime     LocalTime `xorm:"timestamp data_receive_time" sql:"DEFAULT:current_timestamp" json:"dataRecvTime"` 
     LastUploadDataTime    *LocalTime `xorm:"timestamp last_upload_data_time" json:"lastUploadDataTime"`            
     LastCheckTime      *LocalTime `xorm:"timestamp last_check_time" json:"lastCheckTime"`                 
     LastErrorTime      *LocalTime `xorm:"timestamp last_error_time" json:"lastErrorTime"`                 
    }

    注意上面的字段类型,既有LocalTime类型的,又有*LocalTime类型的,*LocalTime是考虑到有时候数据值可能为NULL,即字段值可能为空的情况。

    xorm不知道如何为LocalTime这个自定义类型进行赋值或者取值,因此需要实现xorm的core包中的Conversion接口,这个接口的定义如下:

    注意,坑已经隐藏在上面的接口定义中了,过一会说。

    整个完整的自定义时间类型的代码变成了下面的这样:

    package models 
    import (
     "database/sql/driver"
     "time"
    )
     
    const localDateTimeFormat string = "2006-01-02 15:04:05" 
    type LocalTime time.Time 
    func (l LocalTime) MarshalJSON() ([]byte, error) {
     b := make([]byte, 0, len(localDateTimeFormat)+2)
     b = append(b, '"')
     b = time.Time(l).AppendFormat(b, localDateTimeFormat)
     b = append(b, '"')
     return b, nil
    }
     
    func (l *LocalTime) UnmarshalJSON(b []byte) error {
     now, err := time.ParseInLocation(`"`+localDateTimeFormat+`"`, string(b), time.Local)
     *l = LocalTime(now)
     return err
    }
     
    func (l LocalTime) String() string {
     return time.Time(l).Format(localDateTimeFormat)
    }
     
    func (l LocalTime)Now()(LocalTime){
     return LocalTime(time.Now())
    }
     
    func (l LocalTime)ParseTime(t time.Time)(LocalTime){
     return LocalTime(t)
    }
     
    func (j LocalTime) format() string {
     return time.Time(j).Format(localDateTimeFormat)
    }
     
    func (j LocalTime) MarshalText() ([]byte, error) {
     return []byte(j.format()), nil
    }
     
    func (l *LocalTime) FromDB(b []byte) error {
     if nil == b || len(b) == 0 {
     l = nil
     return nil
     }
     var now time.Time
     var err error
     now, err = time.ParseInLocation(localDateTimeFormat, string(b), time.Local)
     if nil == err {
     *l = LocalTime(now)
     return nil
     }
     now, err = time.ParseInLocation("2006-01-02T15:04:05Z", string(b), time.Local)
     if nil == err {
     *l = LocalTime(now)
     return nil
     }
     panic("自己定义个layout日期格式处理一下数据库里面的日期型数据解析!")
     return err
    }
     
    //func (t *LocalTime) Scan(v interface{}) error {
    // // Should be more strictly to check this type.
    // vt, err := time.Parse("2006-01-02 15:04:05", string(v.([]byte)))
    // if err != nil {
    // return err
    // }
    // *t = LocalTime(vt)
    // return nil
    //}
     
    func (l *LocalTime) ToDB() ([]byte, error) {
     if nil == l {
     return nil,nil
     }
     return []byte(time.Time(*l).Format(localDateTimeFormat)), nil
    }
     
    func (l *LocalTime) Value() (driver.Value, error) {
     if nil==l {
     return nil, nil
     }
     return time.Time(*l).Format(localDateTimeFormat), nil
    }

    此时,要是数据库的字段内容都有值的话插入和更新应该是没有什么问题,但是*LocalTime字段的值为nil的话问题就开始出现了,上面说了,ToDB()方法的返回值类型为[]byte,当字段值为nil时,返回nil看上去一切正常,但是xorm打印出来的sql语句数据值是下面这个样子的:

    这个[]uint8(nil)就是*LocalTime值为nil时的情况,数据库驱动是不认可[]uint8(nil)这种数据去写给timestamp类型字段的,问题的根源就是ToDB方法的返回值类型为[]byte,既然是这样,就需要我们人为的把[]uint8(nil)这种类型改为interface(nil)类型,数据库驱动会识别interface(nil)为NULL值,修改代码xorm\statement.go第322行,把原来的val=data改成下面的样子:

    就是把val=data改为 if nil==data { val=nil } else {val=data} ,看上去逻辑没有什么变化,但是给val=nil赋值的时候,val的类型就从[]uint8(nil)变成了interface(nil)了,这样数据库驱动就可以正确处理空值了。

    除了需要修改xorm\statement.go文件的内容,还需要修改xorm\session_convert.go的第558行,增加以下代码:

    主要是增加下面的代码

    //fix when pointer type value is null,added by peihexian,2019-05-07
    if nil==data {
      return nil,nil
    }
    

    之所以加这个代码是因为xorm作者没有考虑指针类型字段值为nil的情况,xorm对有转换的字段要么当成数字,要么当成了字符串,这两种对于NULL类型的值都不适用,所以需要增加if nil==data return nil,nil这样的代码,还是把数据值组织成interface(nil)去给数据库驱动去处理。

    另外还有一个地方,是session_convert.go 第556行,同样需要增加

    if nil==data { //edit by peihexian 2019.06.19
      return nil,nil
    }

    下面是加完以后的样子

    到这里,对xorm做了几处小的修改,自定义日期的问题及json格式化问题完美解决。

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

    您可能感兴趣的文章:
    • golang 定时任务方面time.Sleep和time.Tick的优劣对比分析
    • 解决Golang time.Parse和time.Format的时区问题
    • 解决golang时间字符串转time.Time的坑
    • golang的时区和神奇的time.Parse的使用方法
    • 对Golang中的runtime.Caller使用说明
    • Golang中的time.Duration类型用法说明
    • golang time包做时间转换操作
    • golang time常用方法详解
    上一篇:golang xorm日志写入文件中的操作
    下一篇:golang time包做时间转换操作
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    golang xorm及time.Time自定义解决json日期格式的问题 golang,xorm,及,time.Time,自定义,