strftime.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. package strftime
  2. import (
  3. "bytes"
  4. "strconv"
  5. "time"
  6. )
  7. // Format returns a textual representation of the time value
  8. // formatted according to the strftime format specification.
  9. func Format(fmt string, t time.Time) string {
  10. buf := buffer(fmt)
  11. return string(AppendFormat(buf, fmt, t))
  12. }
  13. // AppendFormat is like Format, but appends the textual representation
  14. // to dst and returns the extended buffer.
  15. func AppendFormat(dst []byte, fmt string, t time.Time) []byte {
  16. var parser parser
  17. parser.literal = func(b byte) error {
  18. dst = append(dst, b)
  19. return nil
  20. }
  21. parser.format = func(spec, flag byte) error {
  22. switch spec {
  23. case 'A':
  24. dst = append(dst, t.Weekday().String()...)
  25. return nil
  26. case 'a':
  27. dst = append(dst, t.Weekday().String()[:3]...)
  28. return nil
  29. case 'B':
  30. dst = append(dst, t.Month().String()...)
  31. return nil
  32. case 'b', 'h':
  33. dst = append(dst, t.Month().String()[:3]...)
  34. return nil
  35. case 'm':
  36. dst = appendInt2(dst, int(t.Month()), flag)
  37. return nil
  38. case 'd':
  39. dst = appendInt2(dst, int(t.Day()), flag)
  40. return nil
  41. case 'e':
  42. dst = appendInt2(dst, int(t.Day()), ' ')
  43. return nil
  44. case 'I':
  45. dst = append12Hour(dst, t, flag)
  46. return nil
  47. case 'l':
  48. dst = append12Hour(dst, t, ' ')
  49. return nil
  50. case 'H':
  51. dst = appendInt2(dst, t.Hour(), flag)
  52. return nil
  53. case 'k':
  54. dst = appendInt2(dst, t.Hour(), ' ')
  55. return nil
  56. case 'M':
  57. dst = appendInt2(dst, t.Minute(), flag)
  58. return nil
  59. case 'S':
  60. dst = appendInt2(dst, t.Second(), flag)
  61. return nil
  62. case 'L':
  63. dst = append(dst, t.Format(".000")[1:]...)
  64. return nil
  65. case 'f':
  66. dst = append(dst, t.Format(".000000")[1:]...)
  67. return nil
  68. case 'N':
  69. dst = append(dst, t.Format(".000000000")[1:]...)
  70. return nil
  71. case 'y':
  72. dst = t.AppendFormat(dst, "06")
  73. return nil
  74. case 'Y':
  75. dst = t.AppendFormat(dst, "2006")
  76. return nil
  77. case 'C':
  78. dst = t.AppendFormat(dst, "2006")
  79. dst = dst[:len(dst)-2]
  80. return nil
  81. case 'U':
  82. dst = appendWeekNumber(dst, t, flag, true)
  83. return nil
  84. case 'W':
  85. dst = appendWeekNumber(dst, t, flag, false)
  86. return nil
  87. case 'V':
  88. _, w := t.ISOWeek()
  89. dst = appendInt2(dst, w, flag)
  90. return nil
  91. case 'g':
  92. y, _ := t.ISOWeek()
  93. dst = year(y).AppendFormat(dst, "06")
  94. return nil
  95. case 'G':
  96. y, _ := t.ISOWeek()
  97. dst = year(y).AppendFormat(dst, "2006")
  98. return nil
  99. case 's':
  100. dst = strconv.AppendInt(dst, t.Unix(), 10)
  101. return nil
  102. case 'Q':
  103. dst = strconv.AppendInt(dst, t.UnixMilli(), 10)
  104. return nil
  105. case 'w':
  106. w := t.Weekday()
  107. dst = appendInt1(dst, int(w))
  108. return nil
  109. case 'u':
  110. if w := t.Weekday(); w == 0 {
  111. dst = append(dst, '7')
  112. } else {
  113. dst = appendInt1(dst, int(w))
  114. }
  115. return nil
  116. case 'j':
  117. if flag == '-' {
  118. dst = strconv.AppendInt(dst, int64(t.YearDay()), 10)
  119. } else {
  120. dst = t.AppendFormat(dst, "002")
  121. }
  122. return nil
  123. }
  124. if layout := goLayout(spec, flag, false); layout != "" {
  125. dst = t.AppendFormat(dst, layout)
  126. return nil
  127. }
  128. dst = append(dst, '%')
  129. if flag != 0 {
  130. dst = append(dst, flag)
  131. }
  132. dst = append(dst, spec)
  133. return nil
  134. }
  135. parser.parse(fmt)
  136. return dst
  137. }
  138. // Parse converts a textual representation of time to the time value it represents
  139. // according to the strptime format specification.
  140. func Parse(fmt, value string) (time.Time, error) {
  141. pattern, err := layout(fmt, true)
  142. if err != nil {
  143. return time.Time{}, err
  144. }
  145. return time.Parse(pattern, value)
  146. }
  147. // Layout converts a strftime format specification
  148. // to a Go time pattern specification.
  149. func Layout(fmt string) (string, error) {
  150. return layout(fmt, false)
  151. }
  152. func layout(fmt string, parsing bool) (string, error) {
  153. dst := buffer(fmt)
  154. var parser parser
  155. parser.literal = func(b byte) error {
  156. if '0' <= b && b <= '9' {
  157. return literalErr(b)
  158. }
  159. dst = append(dst, b)
  160. if b == 'M' || b == 'T' || b == 'm' || b == 'n' {
  161. switch {
  162. case bytes.HasSuffix(dst, []byte("Jan")):
  163. return literalErr("Jan")
  164. case bytes.HasSuffix(dst, []byte("Mon")):
  165. return literalErr("Mon")
  166. case bytes.HasSuffix(dst, []byte("MST")):
  167. return literalErr("MST")
  168. case bytes.HasSuffix(dst, []byte("PM")):
  169. return literalErr("PM")
  170. case bytes.HasSuffix(dst, []byte("pm")):
  171. return literalErr("pm")
  172. }
  173. }
  174. return nil
  175. }
  176. parser.format = func(spec, flag byte) error {
  177. if layout := goLayout(spec, flag, parsing); layout != "" {
  178. dst = append(dst, layout...)
  179. return nil
  180. }
  181. switch spec {
  182. default:
  183. return formatError{}
  184. case 'L', 'f', 'N':
  185. if bytes.HasSuffix(dst, []byte(".")) || bytes.HasSuffix(dst, []byte(",")) {
  186. switch spec {
  187. default:
  188. dst = append(dst, "000"...)
  189. case 'f':
  190. dst = append(dst, "000000"...)
  191. case 'N':
  192. dst = append(dst, "000000000"...)
  193. }
  194. return nil
  195. }
  196. return formatError{message: "must follow '.' or ','"}
  197. }
  198. }
  199. if err := parser.parse(fmt); err != nil {
  200. return "", err
  201. }
  202. return string(dst), nil
  203. }
  204. // UTS35 converts a strftime format specification
  205. // to a Unicode Technical Standard #35 Date Format Pattern.
  206. func UTS35(fmt string) (string, error) {
  207. const quote = '\''
  208. var quoted bool
  209. dst := buffer(fmt)
  210. var parser parser
  211. parser.literal = func(b byte) error {
  212. if b == quote {
  213. dst = append(dst, quote, quote)
  214. return nil
  215. }
  216. if !quoted && ('a' <= b && b <= 'z' || 'A' <= b && b <= 'Z') {
  217. dst = append(dst, quote)
  218. quoted = true
  219. }
  220. dst = append(dst, b)
  221. return nil
  222. }
  223. parser.format = func(spec, flag byte) error {
  224. if quoted {
  225. dst = append(dst, quote)
  226. quoted = false
  227. }
  228. if pattern := uts35Pattern(spec, flag); pattern != "" {
  229. dst = append(dst, pattern...)
  230. return nil
  231. }
  232. return formatError{}
  233. }
  234. if err := parser.parse(fmt); err != nil {
  235. return "", err
  236. }
  237. if quoted {
  238. dst = append(dst, quote)
  239. }
  240. return string(dst), nil
  241. }
  242. func buffer(format string) (buf []byte) {
  243. const bufSize = 64
  244. max := len(format) + 10
  245. if max < bufSize {
  246. var b [bufSize]byte
  247. buf = b[:0]
  248. } else {
  249. buf = make([]byte, 0, max)
  250. }
  251. return
  252. }
  253. func year(y int) time.Time {
  254. return time.Date(y, time.January, 1, 0, 0, 0, 0, time.UTC)
  255. }
  256. func appendWeekNumber(dst []byte, t time.Time, flag byte, sunday bool) []byte {
  257. offset := int(t.Weekday())
  258. if sunday {
  259. offset = 6 - offset
  260. } else if offset != 0 {
  261. offset = 7 - offset
  262. }
  263. return appendInt2(dst, (t.YearDay()+offset)/7, flag)
  264. }
  265. func append12Hour(dst []byte, t time.Time, flag byte) []byte {
  266. h := t.Hour()
  267. if h == 0 {
  268. h = 12
  269. } else if h > 12 {
  270. h -= 12
  271. }
  272. return appendInt2(dst, h, flag)
  273. }
  274. func appendInt1(dst []byte, i int) []byte {
  275. return append(dst, byte('0'+i))
  276. }
  277. func appendInt2(dst []byte, i int, flag byte) []byte {
  278. if flag == 0 || i >= 10 {
  279. return append(dst, smallsString[i*2:i*2+2]...)
  280. }
  281. if flag == ' ' {
  282. dst = append(dst, flag)
  283. }
  284. return appendInt1(dst, i)
  285. }
  286. const smallsString = "" +
  287. "00010203040506070809" +
  288. "10111213141516171819" +
  289. "20212223242526272829" +
  290. "30313233343536373839" +
  291. "40414243444546474849" +
  292. "50515253545556575859" +
  293. "60616263646566676869" +
  294. "70717273747576777879" +
  295. "80818283848586878889" +
  296. "90919293949596979899"