time.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright (c) 2012, 2013 Ugorji Nwoke. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license found in the LICENSE file.
  3. package codec
  4. import (
  5. "time"
  6. )
  7. var (
  8. timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
  9. )
  10. // encodeTime encodes a time.Time as a []byte, including
  11. // information on the instant in time and UTC offset.
  12. func encodeTime(t time.Time) []byte {
  13. //t := rv.Interface().(time.Time)
  14. tsecs, tnsecs := t.Unix(), t.Nanosecond()
  15. var (
  16. bd byte
  17. btmp [8]byte
  18. bs [16]byte
  19. i int = 1
  20. )
  21. l := t.Location()
  22. if l == time.UTC {
  23. l = nil
  24. }
  25. if tsecs != 0 {
  26. bd = bd | 0x80
  27. bigen.PutUint64(btmp[:], uint64(tsecs))
  28. f := pruneSignExt(btmp[:])
  29. bd = bd | (byte(7-f) << 2)
  30. copy(bs[i:], btmp[f:])
  31. i = i + (8 - f)
  32. }
  33. if tnsecs != 0 {
  34. bd = bd | 0x40
  35. bigen.PutUint32(btmp[:4], uint32(tnsecs))
  36. f := pruneSignExt(btmp[:4])
  37. bd = bd | byte(3-f)
  38. copy(bs[i:], btmp[f:4])
  39. i = i + (4 - f)
  40. }
  41. if l != nil {
  42. bd = bd | 0x20
  43. // Note that Go Libs do not give access to dst flag.
  44. _, zoneOffset := t.Zone()
  45. //zoneName, zoneOffset := t.Zone()
  46. zoneOffset /= 60
  47. z := uint16(zoneOffset)
  48. bigen.PutUint16(btmp[:2], z)
  49. // clear dst flags
  50. bs[i] = btmp[0] & 0x3f
  51. bs[i+1] = btmp[1]
  52. i = i + 2
  53. }
  54. bs[0] = bd
  55. return bs[0:i]
  56. }
  57. // decodeTime decodes a []byte into a time.Time,
  58. // and sets into passed reflectValue.
  59. func decodeTime(bs []byte) (tt time.Time, err error) {
  60. bd := bs[0]
  61. var (
  62. tsec int64
  63. tnsec uint32
  64. tz uint16
  65. i byte = 1
  66. i2 byte
  67. n byte
  68. )
  69. if bd&(1<<7) != 0 {
  70. var btmp [8]byte
  71. n = ((bd >> 2) & 0x7) + 1
  72. i2 = i + n
  73. copy(btmp[8-n:], bs[i:i2])
  74. //if first bit of bs[i] is set, then fill btmp[0..8-n] with 0xff (ie sign extend it)
  75. if bs[i] & (1 << 7) != 0 {
  76. copy(btmp[0:8-n], bsAll0xff)
  77. //for j,k := byte(0), 8-n; j < k; j++ { btmp[j] = 0xff }
  78. }
  79. i = i2
  80. tsec = int64(bigen.Uint64(btmp[:]))
  81. }
  82. if bd&(1<<6) != 0 {
  83. var btmp [4]byte
  84. n = (bd & 0x3) + 1
  85. i2 = i + n
  86. copy(btmp[4-n:], bs[i:i2])
  87. i = i2
  88. tnsec = bigen.Uint32(btmp[:])
  89. }
  90. if bd&(1<<5) == 0 {
  91. tt = time.Unix(tsec, int64(tnsec)).UTC()
  92. return
  93. }
  94. // In stdlib time.Parse, when a date is parsed without a zone name, it uses "" as zone name.
  95. // However, we need name here, so it can be shown when time is printed.
  96. // Zone name is in form: UTC-08:00.
  97. // Note that Go Libs do not give access to dst flag, so we ignore dst bits
  98. i2 = i + 2
  99. tz = bigen.Uint16(bs[i:i2])
  100. i = i2
  101. var tzname = []byte("UTC+00:00")
  102. // sign extend sign bit into top 2 MSB (which were dst bits):
  103. if tz&(1<<13) == 0 { // positive
  104. tz = tz & 0x3fff //clear 2 MSBs: dst bits
  105. } else { // negative
  106. tz = tz | 0xc000 //set 2 MSBs: dst bits
  107. tzname[3] = '-'
  108. }
  109. tzint := (int16(tz))
  110. //tzname := fmt.Sprintf("UTC%s%02d:%02d", tzsign, tz/60, tz%60) //perf issue using Sprintf. inline below.
  111. //tzhr, tzmin := tz/60, tz%60 //faster if u convert to int first
  112. var tzhr, tzmin int16
  113. if tzint < 0 {
  114. tzhr, tzmin = -tzint/60, (-tzint)%60
  115. } else {
  116. tzhr, tzmin = tzint/60, tzint%60
  117. }
  118. tzname[4] = timeDigits[tzhr/10]
  119. tzname[5] = timeDigits[tzhr%10]
  120. tzname[7] = timeDigits[tzmin/10]
  121. tzname[8] = timeDigits[tzmin%10]
  122. tt = time.Unix(tsec, int64(tnsec)).In(time.FixedZone(string(tzname), int(tzint)*60))
  123. return
  124. }