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

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

Unexpected output when printing time.Time type alias

问题

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

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strings"
  6. "time"
  7. )
  8. type Time time.Time
  9. func (st *Time) UnmarshalJSON(b []byte) error {
  10. // "2021-05-21T03:10:20.958450" -> "2021-05-21T03:10:20.958450Z"
  11. s := strings.Trim(string(b), "\"")
  12. t, err := time.Parse(time.RFC3339, fmt.Sprintf("%s%s", s, "Z"))
  13. if err != nil {
  14. return fmt.Errorf("parse time: %w", err)
  15. }
  16. *st = Time(t)
  17. return nil
  18. }
  19. type User struct {
  20. Name string
  21. TS Time
  22. }
  23. const data = `{"id":3, "name":"Name", "ts":"2021-05-21T03:10:20.958450"}`
  24. func main() {
  25. user := new(User)
  26. json.Unmarshal([]byte(data), &user)
  27. fmt.Printf("%v\n", user)
  28. }

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

目前上面的代码输出为:

  1. &{Name {958450000 63757163420 <nil>}}

但我希望输出更类似于:

  1. &{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)

  1. package main
  2. import (
  3. &quot;encoding/json&quot;
  4. &quot;fmt&quot;
  5. &quot;strings&quot;
  6. &quot;time&quot;
  7. )
  8. type Time time.Time
  9. func (st *Time) UnmarshalJSON(b []byte) error {
  10. // &quot;2021-05-21T03:10:20.958450&quot; -&gt; &quot;2021-05-21T03:10:20.958450Z&quot;
  11. s := strings.Trim(string(b), &quot;\&quot;&quot;)
  12. t, err := time.Parse(time.RFC3339, fmt.Sprintf(&quot;%s%s&quot;, s, &quot;Z&quot;))
  13. if err != nil {
  14. return fmt.Errorf(&quot;parse time: %w&quot;, err)
  15. }
  16. *st = Time(t)
  17. return nil
  18. }
  19. type User struct {
  20. Name string
  21. TS Time
  22. }
  23. const data = `{&quot;id&quot;:3, &quot;name&quot;:&quot;Name&quot;, &quot;ts&quot;:&quot;2021-05-21T03:10:20.958450&quot;}`
  24. func main() {
  25. user := new(User)
  26. json.Unmarshal([]byte(data), &amp;user)
  27. fmt.Printf(&quot;%v\n&quot;, user)
  28. }

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:

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

But I would like to be more similar to:

  1. &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:

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

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

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

  1. type Time struct {
  2. time.Time
  3. }
  4. func (st *Time) UnmarshalJSON(b []byte) error {
  5. // ...
  6. st.Time = t // 简单赋值,无需类型转换
  7. return nil
  8. }

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

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

  1. func (st *Time) UnmarshalJSON(b []byte) error {
  2. var s string
  3. if err := json.Unmarshal(b, &s); err != nil {
  4. return err
  5. }
  6. t, err := time.ParseInLocation("2006-01-02T15:04:05", s, time.UTC)
  7. if err != nil {
  8. return err
  9. }
  10. // ...
  11. }
英文:

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:

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

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):

  1. type Time struct {
  2. time.Time
  3. }
  4. func (st *Time) UnmarshalJSON(b []byte) error {
  5. // ...
  6. st.Time = t // simple assignment without type conversion
  7. return nil
  8. }

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.

  1. func (st *Time) UnmarshalJSON(b []byte) error {
  2. var s string
  3. if err := json.Unmarshal(b, &amp;s); err != nil {
  4. return err
  5. }
  6. t, err := time.ParseInLocation(&quot;2006-01-02T15:04:05&quot;, s, time.UTC)
  7. if err != nil {
  8. return err
  9. }
  10. // ...
  11. }

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:

确定