123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- package strftime
- import (
- "bytes"
- "fmt"
- "io"
- "strconv"
- "strings"
- "time"
- )
- // These are all of the standard, POSIX compliant specifications.
- // Extensions should be in extensions.go
- var (
- fullWeekDayName = StdlibFormat("Monday")
- abbrvWeekDayName = StdlibFormat("Mon")
- fullMonthName = StdlibFormat("January")
- abbrvMonthName = StdlibFormat("Jan")
- centuryDecimal = AppendFunc(appendCentury)
- timeAndDate = StdlibFormat("Mon Jan _2 15:04:05 2006")
- mdy = StdlibFormat("01/02/06")
- dayOfMonthZeroPad = StdlibFormat("02")
- dayOfMonthSpacePad = StdlibFormat("_2")
- ymd = StdlibFormat("2006-01-02")
- twentyFourHourClockZeroPad = &hourPadded{twelveHour: false, pad: '0'}
- twelveHourClockZeroPad = &hourPadded{twelveHour: true, pad: '0'}
- dayOfYear = AppendFunc(appendDayOfYear)
- twentyFourHourClockSpacePad = &hourPadded{twelveHour: false, pad: ' '}
- twelveHourClockSpacePad = &hourPadded{twelveHour: true, pad: ' '}
- minutesZeroPad = StdlibFormat("04")
- monthNumberZeroPad = StdlibFormat("01")
- newline = Verbatim("\n")
- ampm = StdlibFormat("PM")
- hm = StdlibFormat("15:04")
- imsp = hmsWAMPM{}
- secondsNumberZeroPad = StdlibFormat("05")
- hms = StdlibFormat("15:04:05")
- tab = Verbatim("\t")
- weekNumberSundayOrigin = weeknumberOffset(0) // week number of the year, Sunday first
- weekdayMondayOrigin = weekday(1)
- // monday as the first day, and 01 as the first value
- weekNumberMondayOriginOneOrigin = AppendFunc(appendWeekNumber)
- eby = StdlibFormat("_2-Jan-2006")
- // monday as the first day, and 00 as the first value
- weekNumberMondayOrigin = weeknumberOffset(1) // week number of the year, Monday first
- weekdaySundayOrigin = weekday(0)
- natReprTime = StdlibFormat("15:04:05") // national representation of the time XXX is this correct?
- natReprDate = StdlibFormat("01/02/06") // national representation of the date XXX is this correct?
- year = StdlibFormat("2006") // year with century
- yearNoCentury = StdlibFormat("06") // year w/o century
- timezone = StdlibFormat("MST") // time zone name
- timezoneOffset = StdlibFormat("-0700") // time zone ofset from UTC
- percent = Verbatim("%")
- )
- // Appender is the interface that must be fulfilled by components that
- // implement the translation of specifications to actual time value.
- //
- // The Append method takes the accumulated byte buffer, and the time to
- // use to generate the textual representation. The resulting byte
- // sequence must be returned by this method, normally by using the
- // append() builtin function.
- type Appender interface {
- Append([]byte, time.Time) []byte
- }
- // AppendFunc is an utility type to allow users to create a
- // function-only version of an Appender
- type AppendFunc func([]byte, time.Time) []byte
- func (af AppendFunc) Append(b []byte, t time.Time) []byte {
- return af(b, t)
- }
- type appenderList []Appender
- type dumper interface {
- dump(io.Writer)
- }
- func (l appenderList) dump(out io.Writer) {
- var buf bytes.Buffer
- ll := len(l)
- for i, a := range l {
- if dumper, ok := a.(dumper); ok {
- dumper.dump(&buf)
- } else {
- fmt.Fprintf(&buf, "%#v", a)
- }
- if i < ll-1 {
- fmt.Fprintf(&buf, ",\n")
- }
- }
- if _, err := buf.WriteTo(out); err != nil {
- panic(err)
- }
- }
- // does the time.Format thing
- type stdlibFormat struct {
- s string
- }
- // StdlibFormat returns an Appender that simply goes through `time.Format()`
- // For example, if you know you want to display the abbreviated month name for %b,
- // you can create a StdlibFormat with the pattern `Jan` and register that
- // for specification `b`:
- //
- // a := StdlibFormat(`Jan`)
- // ss := NewSpecificationSet()
- // ss.Set('b', a) // does %b -> abbreviated month name
- func StdlibFormat(s string) Appender {
- return &stdlibFormat{s: s}
- }
- func (v stdlibFormat) Append(b []byte, t time.Time) []byte {
- return t.AppendFormat(b, v.s)
- }
- func (v stdlibFormat) str() string {
- return v.s
- }
- func (v stdlibFormat) canCombine() bool {
- return true
- }
- func (v stdlibFormat) combine(w combiner) Appender {
- return StdlibFormat(v.s + w.str())
- }
- func (v stdlibFormat) dump(out io.Writer) {
- fmt.Fprintf(out, "stdlib: %s", v.s)
- }
- type verbatimw struct {
- s string
- }
- // Verbatim returns an Appender suitable for generating static text.
- // For static text, this method is slightly favorable than creating
- // your own appender, as adjacent verbatim blocks will be combined
- // at compile time to produce more efficient Appenders
- func Verbatim(s string) Appender {
- return &verbatimw{s: s}
- }
- func (v verbatimw) Append(b []byte, _ time.Time) []byte {
- return append(b, v.s...)
- }
- func (v verbatimw) canCombine() bool {
- return canCombine(v.s)
- }
- func (v verbatimw) combine(w combiner) Appender {
- if _, ok := w.(*stdlibFormat); ok {
- return StdlibFormat(v.s + w.str())
- }
- return Verbatim(v.s + w.str())
- }
- func (v verbatimw) str() string {
- return v.s
- }
- func (v verbatimw) dump(out io.Writer) {
- fmt.Fprintf(out, "verbatim: %s", v.s)
- }
- // These words below, as well as any decimal character
- var combineExclusion = []string{
- "Mon",
- "Monday",
- "Jan",
- "January",
- "MST",
- "PM",
- "pm",
- }
- func canCombine(s string) bool {
- if strings.ContainsAny(s, "0123456789") {
- return false
- }
- for _, word := range combineExclusion {
- if strings.Contains(s, word) {
- return false
- }
- }
- return true
- }
- type combiner interface {
- canCombine() bool
- combine(combiner) Appender
- str() string
- }
- // this is container for the compiler to keep track of appenders,
- // and combine them as we parse and compile the pattern
- type combiningAppend struct {
- list appenderList
- prev Appender
- prevCanCombine bool
- }
- func (ca *combiningAppend) Append(w Appender) {
- if ca.prevCanCombine {
- if wc, ok := w.(combiner); ok && wc.canCombine() {
- ca.prev = ca.prev.(combiner).combine(wc)
- ca.list[len(ca.list)-1] = ca.prev
- return
- }
- }
- ca.list = append(ca.list, w)
- ca.prev = w
- ca.prevCanCombine = false
- if comb, ok := w.(combiner); ok {
- if comb.canCombine() {
- ca.prevCanCombine = true
- }
- }
- }
- func appendCentury(b []byte, t time.Time) []byte {
- n := t.Year() / 100
- if n < 10 {
- b = append(b, '0')
- }
- return append(b, strconv.Itoa(n)...)
- }
- type weekday int
- func (v weekday) Append(b []byte, t time.Time) []byte {
- n := int(t.Weekday())
- if n < int(v) {
- n += 7
- }
- return append(b, byte(n+48))
- }
- type weeknumberOffset int
- func (v weeknumberOffset) Append(b []byte, t time.Time) []byte {
- yd := t.YearDay()
- offset := int(t.Weekday()) - int(v)
- if offset < 0 {
- offset += 7
- }
- if yd < offset {
- return append(b, '0', '0')
- }
- n := ((yd - offset) / 7) + 1
- if n < 10 {
- b = append(b, '0')
- }
- return append(b, strconv.Itoa(n)...)
- }
- func appendWeekNumber(b []byte, t time.Time) []byte {
- _, n := t.ISOWeek()
- if n < 10 {
- b = append(b, '0')
- }
- return append(b, strconv.Itoa(n)...)
- }
- func appendDayOfYear(b []byte, t time.Time) []byte {
- n := t.YearDay()
- if n < 10 {
- b = append(b, '0', '0')
- } else if n < 100 {
- b = append(b, '0')
- }
- return append(b, strconv.Itoa(n)...)
- }
- type hourPadded struct {
- pad byte
- twelveHour bool
- }
- func (v hourPadded) Append(b []byte, t time.Time) []byte {
- h := t.Hour()
- if v.twelveHour && h > 12 {
- h = h - 12
- }
- if v.twelveHour && h == 0 {
- h = 12
- }
- if h < 10 {
- b = append(b, v.pad)
- b = append(b, byte(h+48))
- } else {
- b = unrollTwoDigits(b, h)
- }
- return b
- }
- func unrollTwoDigits(b []byte, v int) []byte {
- b = append(b, byte((v/10)+48))
- b = append(b, byte((v%10)+48))
- return b
- }
- type hmsWAMPM struct{}
- func (v hmsWAMPM) Append(b []byte, t time.Time) []byte {
- h := t.Hour()
- var am bool
- if h == 0 {
- b = append(b, '1')
- b = append(b, '2')
- am = true
- } else {
- switch {
- case h == 12:
- // no op
- case h > 12:
- h = h - 12
- default:
- am = true
- }
- b = unrollTwoDigits(b, h)
- }
- b = append(b, ':')
- b = unrollTwoDigits(b, t.Minute())
- b = append(b, ':')
- b = unrollTwoDigits(b, t.Second())
- b = append(b, ' ')
- if am {
- b = append(b, 'A')
- } else {
- b = append(b, 'P')
- }
- b = append(b, 'M')
- return b
- }
|