打印time.Time类型别名时出现意外输出

huangapple go评论79阅读模式
英文:

Unexpected output when printing time.Time type alias

问题

我正在尝试为自定义类型编写一个解组函数。考虑下面的代码(playground

package main

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

type Time time.Time

func (st *Time) UnmarshalJSON(b []byte) error {
	// "2021-05-21T03:10:20.958450" -> "2021-05-21T03:10:20.958450Z"
	s := strings.Trim(string(b), "\"")
	t, err := time.Parse(time.RFC3339, fmt.Sprintf("%s%s", s, "Z"))
	if err != nil {
		return fmt.Errorf("parse time: %w", err)
	}
	*st = Time(t)
	return nil
}

type User struct {
	Name string
	TS   Time
}

const data = `{"id":3, "name":"Name", "ts":"2021-05-21T03:10:20.958450"}`

func main() {
	user := new(User)
	json.Unmarshal([]byte(data), &user)
	fmt.Printf("%v\n", user)
}

我成功地从time.Parse()中获得了一个有效的time.Time值,但是我不太明白为什么*st = Time(t)会给出如此奇怪的值?

目前上面的代码输出为:

&{Name {958450000 63757163420 <nil>}}

但我希望输出更类似于:

&{Name 2021-05-21 03:10:20.95845 +0000 UTC}

我在这里理解错了什么?

英文:

I am trying to write a unmarshal function for a custom type.
Consider the below code (playground)

package main

import (
	&quot;encoding/json&quot;
	&quot;fmt&quot;
	&quot;strings&quot;
	&quot;time&quot;
)

type Time time.Time

func (st *Time) UnmarshalJSON(b []byte) error {
	// &quot;2021-05-21T03:10:20.958450&quot; -&gt; &quot;2021-05-21T03:10:20.958450Z&quot;
	s := strings.Trim(string(b), &quot;\&quot;&quot;)
	t, err := time.Parse(time.RFC3339, fmt.Sprintf(&quot;%s%s&quot;, s, &quot;Z&quot;))
	if err != nil {
		return fmt.Errorf(&quot;parse time: %w&quot;, err)
	}
	*st = Time(t)
	return nil
}

type User struct {
	Name string
	TS Time
}

const data = `{&quot;id&quot;:3, &quot;name&quot;:&quot;Name&quot;, &quot;ts&quot;:&quot;2021-05-21T03:10:20.958450&quot;}`

func main() {
	user := new(User)
	json.Unmarshal([]byte(data), &amp;user)
	fmt.Printf(&quot;%v\n&quot;, user)
}

I am successfully able to get a valid time.Time value from my time.Parse() but I am not quite grasping why *st = Time(t) gives such a wierd value?

Currently the above prints out:

&amp;{Name {958450000 63757163420 &lt;nil&gt;}}

But I would like to be more similar to:

&amp;{Name 2021-05-21 03:10:20.95845 +0000 UTC}

What am I missunderstanding here?

答案1

得分: 5

与time.Time相反,您的类型没有实现fmt.Stringer接口,因此fmt.Print*函数只能使用它们的默认格式化逻辑,这种情况下是打印包含在花括号中的底层time.Time值的字段。

为了获得所需的行为,请添加一个String方法,该方法委托给time.Time.String:

func (t Time) String() string {
    return time.Time(t).String()
}

https://go.dev/play/p/5PwOwa49B5X

或者,将您的Time类型更改为嵌入time.Time。这将自动提升String方法以及所有其他方法(例如Marshal*方法):

type Time struct {
    time.Time
}

func (st *Time) UnmarshalJSON(b []byte) error {
    // ...
    st.Time = t // 简单赋值,无需类型转换
    return nil
}

https://go.dev/play/p/0H5qyCO22gu

此外,您不应手动解析JSON。strings.Trim(string(b), "\"")不足以完全解码JSON字符串值。始终使用json.Unmarshal。您还可以使用time.ParseInLocation进行简化。

func (st *Time) UnmarshalJSON(b []byte) error {
    var s string
    if err := json.Unmarshal(b, &s); err != nil {
        return err
    }

    t, err := time.ParseInLocation("2006-01-02T15:04:05", s, time.UTC)
    if err != nil {
        return err
    }

    // ...
}
英文:

Contrary to time.Time your type does not implement fmt.Stringer, so the fmt.Print* functions have no choice but to use their default formatting logic, which in this case is to print the fields of the underlying time.Time value enclosed in curly braces.

Add a String method which delegates to time.Time.String to your type to get the desired behavior:

func (t Time) String() string {
    return time.Time(t).String()
}

https://go.dev/play/p/5PwOwa49B5X

Alternatively, change your Time type to embed time.Time. This will automatically promote the String method along with all other methods (such as the Marshal* methods):

type Time struct {
	time.Time
}

func (st *Time) UnmarshalJSON(b []byte) error {
	// ...
	st.Time = t // simple assignment without type conversion
	return nil
}

https://go.dev/play/p/0H5qyCO22gu

Also, you should never parse JSON by hand. strings.Trim(string(b), &quot;\&quot;&quot;) is not enough to fully decode a JSON string value. Always use json.Unmarshal. And you can simplify by using time.ParseInLocation.

func (st *Time) UnmarshalJSON(b []byte) error {
    var s string
    if err := json.Unmarshal(b, &amp;s); err != nil {
        return err
    }

    t, err := time.ParseInLocation(&quot;2006-01-02T15:04:05&quot;, s, time.UTC)
    if err != nil {
        return err
    }

    // ...
}

huangapple
  • 本文由 发表于 2022年6月27日 23:58:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/72775047.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定