| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- package strftime
- import (
- "bytes"
- "strconv"
- "time"
- )
- // Format returns a textual representation of the time value
- // formatted according to the strftime format specification.
- func Format(fmt string, t time.Time) string {
- buf := buffer(fmt)
- return string(AppendFormat(buf, fmt, t))
- }
- // AppendFormat is like Format, but appends the textual representation
- // to dst and returns the extended buffer.
- func AppendFormat(dst []byte, fmt string, t time.Time) []byte {
- var parser parser
- parser.literal = func(b byte) error {
- dst = append(dst, b)
- return nil
- }
- parser.format = func(spec, flag byte) error {
- switch spec {
- case 'A':
- dst = append(dst, t.Weekday().String()...)
- return nil
- case 'a':
- dst = append(dst, t.Weekday().String()[:3]...)
- return nil
- case 'B':
- dst = append(dst, t.Month().String()...)
- return nil
- case 'b', 'h':
- dst = append(dst, t.Month().String()[:3]...)
- return nil
- case 'm':
- dst = appendInt2(dst, int(t.Month()), flag)
- return nil
- case 'd':
- dst = appendInt2(dst, int(t.Day()), flag)
- return nil
- case 'e':
- dst = appendInt2(dst, int(t.Day()), ' ')
- return nil
- case 'I':
- dst = append12Hour(dst, t, flag)
- return nil
- case 'l':
- dst = append12Hour(dst, t, ' ')
- return nil
- case 'H':
- dst = appendInt2(dst, t.Hour(), flag)
- return nil
- case 'k':
- dst = appendInt2(dst, t.Hour(), ' ')
- return nil
- case 'M':
- dst = appendInt2(dst, t.Minute(), flag)
- return nil
- case 'S':
- dst = appendInt2(dst, t.Second(), flag)
- return nil
- case 'L':
- dst = append(dst, t.Format(".000")[1:]...)
- return nil
- case 'f':
- dst = append(dst, t.Format(".000000")[1:]...)
- return nil
- case 'N':
- dst = append(dst, t.Format(".000000000")[1:]...)
- return nil
- case 'y':
- dst = t.AppendFormat(dst, "06")
- return nil
- case 'Y':
- dst = t.AppendFormat(dst, "2006")
- return nil
- case 'C':
- dst = t.AppendFormat(dst, "2006")
- dst = dst[:len(dst)-2]
- return nil
- case 'U':
- dst = appendWeekNumber(dst, t, flag, true)
- return nil
- case 'W':
- dst = appendWeekNumber(dst, t, flag, false)
- return nil
- case 'V':
- _, w := t.ISOWeek()
- dst = appendInt2(dst, w, flag)
- return nil
- case 'g':
- y, _ := t.ISOWeek()
- dst = year(y).AppendFormat(dst, "06")
- return nil
- case 'G':
- y, _ := t.ISOWeek()
- dst = year(y).AppendFormat(dst, "2006")
- return nil
- case 's':
- dst = strconv.AppendInt(dst, t.Unix(), 10)
- return nil
- case 'Q':
- dst = strconv.AppendInt(dst, t.UnixMilli(), 10)
- return nil
- case 'w':
- w := t.Weekday()
- dst = appendInt1(dst, int(w))
- return nil
- case 'u':
- if w := t.Weekday(); w == 0 {
- dst = append(dst, '7')
- } else {
- dst = appendInt1(dst, int(w))
- }
- return nil
- case 'j':
- if flag == '-' {
- dst = strconv.AppendInt(dst, int64(t.YearDay()), 10)
- } else {
- dst = t.AppendFormat(dst, "002")
- }
- return nil
- }
- if layout := goLayout(spec, flag, false); layout != "" {
- dst = t.AppendFormat(dst, layout)
- return nil
- }
- dst = append(dst, '%')
- if flag != 0 {
- dst = append(dst, flag)
- }
- dst = append(dst, spec)
- return nil
- }
- parser.parse(fmt)
- return dst
- }
- // Parse converts a textual representation of time to the time value it represents
- // according to the strptime format specification.
- func Parse(fmt, value string) (time.Time, error) {
- pattern, err := layout(fmt, true)
- if err != nil {
- return time.Time{}, err
- }
- return time.Parse(pattern, value)
- }
- // Layout converts a strftime format specification
- // to a Go time pattern specification.
- func Layout(fmt string) (string, error) {
- return layout(fmt, false)
- }
- func layout(fmt string, parsing bool) (string, error) {
- dst := buffer(fmt)
- var parser parser
- parser.literal = func(b byte) error {
- if '0' <= b && b <= '9' {
- return literalErr(b)
- }
- dst = append(dst, b)
- if b == 'M' || b == 'T' || b == 'm' || b == 'n' {
- switch {
- case bytes.HasSuffix(dst, []byte("Jan")):
- return literalErr("Jan")
- case bytes.HasSuffix(dst, []byte("Mon")):
- return literalErr("Mon")
- case bytes.HasSuffix(dst, []byte("MST")):
- return literalErr("MST")
- case bytes.HasSuffix(dst, []byte("PM")):
- return literalErr("PM")
- case bytes.HasSuffix(dst, []byte("pm")):
- return literalErr("pm")
- }
- }
- return nil
- }
- parser.format = func(spec, flag byte) error {
- if layout := goLayout(spec, flag, parsing); layout != "" {
- dst = append(dst, layout...)
- return nil
- }
- switch spec {
- default:
- return formatError{}
- case 'L', 'f', 'N':
- if bytes.HasSuffix(dst, []byte(".")) || bytes.HasSuffix(dst, []byte(",")) {
- switch spec {
- default:
- dst = append(dst, "000"...)
- case 'f':
- dst = append(dst, "000000"...)
- case 'N':
- dst = append(dst, "000000000"...)
- }
- return nil
- }
- return formatError{message: "must follow '.' or ','"}
- }
- }
- if err := parser.parse(fmt); err != nil {
- return "", err
- }
- return string(dst), nil
- }
- // UTS35 converts a strftime format specification
- // to a Unicode Technical Standard #35 Date Format Pattern.
- func UTS35(fmt string) (string, error) {
- const quote = '\''
- var quoted bool
- dst := buffer(fmt)
- var parser parser
- parser.literal = func(b byte) error {
- if b == quote {
- dst = append(dst, quote, quote)
- return nil
- }
- if !quoted && ('a' <= b && b <= 'z' || 'A' <= b && b <= 'Z') {
- dst = append(dst, quote)
- quoted = true
- }
- dst = append(dst, b)
- return nil
- }
- parser.format = func(spec, flag byte) error {
- if quoted {
- dst = append(dst, quote)
- quoted = false
- }
- if pattern := uts35Pattern(spec, flag); pattern != "" {
- dst = append(dst, pattern...)
- return nil
- }
- return formatError{}
- }
- if err := parser.parse(fmt); err != nil {
- return "", err
- }
- if quoted {
- dst = append(dst, quote)
- }
- return string(dst), nil
- }
- func buffer(format string) (buf []byte) {
- const bufSize = 64
- max := len(format) + 10
- if max < bufSize {
- var b [bufSize]byte
- buf = b[:0]
- } else {
- buf = make([]byte, 0, max)
- }
- return
- }
- func year(y int) time.Time {
- return time.Date(y, time.January, 1, 0, 0, 0, 0, time.UTC)
- }
- func appendWeekNumber(dst []byte, t time.Time, flag byte, sunday bool) []byte {
- offset := int(t.Weekday())
- if sunday {
- offset = 6 - offset
- } else if offset != 0 {
- offset = 7 - offset
- }
- return appendInt2(dst, (t.YearDay()+offset)/7, flag)
- }
- func append12Hour(dst []byte, t time.Time, flag byte) []byte {
- h := t.Hour()
- if h == 0 {
- h = 12
- } else if h > 12 {
- h -= 12
- }
- return appendInt2(dst, h, flag)
- }
- func appendInt1(dst []byte, i int) []byte {
- return append(dst, byte('0'+i))
- }
- func appendInt2(dst []byte, i int, flag byte) []byte {
- if flag == 0 || i >= 10 {
- return append(dst, smallsString[i*2:i*2+2]...)
- }
- if flag == ' ' {
- dst = append(dst, flag)
- }
- return appendInt1(dst, i)
- }
- const smallsString = "" +
- "00010203040506070809" +
- "10111213141516171819" +
- "20212223242526272829" +
- "30313233343536373839" +
- "40414243444546474849" +
- "50515253545556575859" +
- "60616263646566676869" +
- "70717273747576777879" +
- "80818283848586878889" +
- "90919293949596979899"
|