zenity/internal/strftime/strftime.go

169 lines
3.7 KiB
Go
Raw Normal View History

2022-03-30 10:36:50 -04:00
package strftime
import (
"errors"
"strconv"
"strings"
"time"
)
func Format(fmt string, t time.Time) string {
var res strings.Builder
var parser parser
parser.fmt = fmt
parser.goSpecifiers()
parser.writeLit = res.WriteByte
parser.writeFmt = func(fmt string) error {
switch fmt {
case "000", "000000", "000000000":
res.WriteString(t.Format("." + fmt)[1:])
2022-03-30 14:27:54 -04:00
default:
res.WriteString(t.Format(fmt))
2022-03-30 10:36:50 -04:00
}
return nil
}
parser.fallback = func(spec byte, pad bool) error {
switch spec {
default:
return errors.New("strftime: unsupported specifier: %" + string(spec))
case 'C':
s := t.Format("2006")
res.WriteString(s[:len(s)-2])
case 'g':
y, _ := t.ISOWeek()
res.WriteString(time.Date(y, 1, 1, 0, 0, 0, 0, time.UTC).Format("06"))
case 'G':
y, _ := t.ISOWeek()
res.WriteString(time.Date(y, 1, 1, 0, 0, 0, 0, time.UTC).Format("2006"))
case 'V':
_, w := t.ISOWeek()
if w < 10 && pad {
res.WriteByte('0')
}
res.WriteString(strconv.Itoa(w))
case 'w':
w := int(t.Weekday())
res.WriteString(strconv.Itoa(w))
case 'u':
if w := int(t.Weekday()); w == 0 {
res.WriteByte('7')
} else {
res.WriteString(strconv.Itoa(w))
}
}
return nil
}
parser.parse()
return res.String()
}
func Parse(fmt, value string) (time.Time, error) {
layout, err := Layout(fmt)
if err != nil {
return time.Time{}, err
}
return time.Parse(layout, value)
}
func Layout(fmt string) (string, error) {
var res strings.Builder
var parser parser
parser.fmt = fmt
parser.goSpecifiers()
parser.writeLit = func(b byte) error {
2022-03-30 21:11:14 -04:00
if '0' <= b && b <= '9' {
return errors.New("strftime: unsupported literal digit: '" + string(b) + "'")
2022-03-30 10:36:50 -04:00
}
res.WriteByte(b)
2022-03-30 21:11:14 -04:00
if b == 'M' || b == 'T' || b == 'm' || b == 'n' {
cur := res.String()
switch {
case strings.HasSuffix(cur, "Jan"):
return errors.New("strftime: unsupported literal: 'Jan'")
case strings.HasSuffix(cur, "Mon"):
return errors.New("strftime: unsupported literal: 'Mon'")
case strings.HasSuffix(cur, "MST"):
return errors.New("strftime: unsupported literal: 'MST'")
case strings.HasSuffix(cur, "PM"):
return errors.New("strftime: unsupported literal: 'PM'")
case strings.HasSuffix(cur, "pm"):
return errors.New("strftime: unsupported literal: 'pm'")
}
}
2022-03-30 10:36:50 -04:00
return nil
}
2022-03-30 14:27:54 -04:00
parser.writeFmt = func(fmt string) error {
switch fmt {
case "000", "000000", "000000000":
2022-03-30 21:11:14 -04:00
if cur := res.String(); !(strings.HasSuffix(cur, ".") || strings.HasSuffix(cur, ",")) {
2022-03-30 14:27:54 -04:00
return errors.New("strftime: unsupported specifier: fractional seconds must follow '.' or ','")
}
}
res.WriteString(fmt)
2022-03-30 10:36:50 -04:00
return nil
}
parser.fallback = func(spec byte, pad bool) error {
return errors.New("strftime: unsupported specifier: %" + string(spec))
}
if err := parser.parse(); err != nil {
return "", err
}
parser.writeFmt("")
return res.String(), nil
}
func UTS35(fmt string) (string, error) {
var parser parser
parser.fmt = fmt
parser.uts35Specifiers()
const quote = '\''
var literal bool
var res strings.Builder
parser.writeLit = func(b byte) error {
if b == quote {
res.WriteByte(quote)
res.WriteByte(quote)
return nil
}
if !literal && ('a' <= b && b <= 'z' || 'A' <= b && b <= 'Z') {
literal = true
res.WriteByte(quote)
}
res.WriteByte(b)
return nil
}
2022-03-30 14:27:54 -04:00
parser.writeFmt = func(fmt string) error {
2022-03-30 10:36:50 -04:00
if literal {
literal = false
res.WriteByte(quote)
}
2022-03-30 14:27:54 -04:00
res.WriteString(fmt)
2022-03-30 10:36:50 -04:00
return nil
}
parser.fallback = func(spec byte, pad bool) error {
return errors.New("strftime: unsupported specifier: %" + string(spec))
}
if err := parser.parse(); err != nil {
return "", err
}
parser.writeFmt("")
return res.String(), nil
}