Date formatting.

This commit is contained in:
Nuno Cruces 2022-03-30 01:28:19 +01:00
parent ef114d19a8
commit 681f5703ca
4 changed files with 84 additions and 25 deletions

View file

@ -522,7 +522,7 @@ func lstResult(l []string, err error) {
func calResult(d time.Time, err error) { func calResult(d time.Time, err error) {
errResult(err) errResult(err)
os.Stdout.WriteString(d.Format(zenutil.Strftime(zenutil.DateFormat))) os.Stdout.WriteString(zenutil.Strftime(zenutil.DateFormat, d))
os.Stdout.WriteString(zenutil.LineBreak) os.Stdout.WriteString(zenutil.LineBreak)
} }

View file

@ -27,5 +27,6 @@ func calendar(text string, opts options) (time.Time, error) {
if err != nil { if err != nil {
return time.Time{}, err return time.Time{}, err
} }
return time.Parse(zenutil.Strftime(zenutil.DateFormat), str) layout := zenutil.StrftimeLayout(zenutil.DateFormat)
return time.Parse(layout, str)
} }

View file

@ -27,5 +27,6 @@ func calendar(text string, opts options) (time.Time, error) {
if err != nil { if err != nil {
return time.Time{}, err return time.Time{}, err
} }
return time.Parse(zenutil.Strftime(zenutil.DateFormat), str) layout := zenutil.StrftimeLayout(zenutil.DateFormat)
return time.Parse(layout, str)
} }

View file

@ -1,11 +1,31 @@
package zenutil package zenutil
import "strings" import (
"strings"
"time"
)
// Strftime is internal. // Strftime is internal.
func Strftime(fmt string) string { func Strftime(fmt string, t time.Time) string {
var res strings.Builder
writeLit := res.WriteByte
writeFmt := func(fmt string) (int, error) {
return res.WriteString(t.Format(fmt))
}
strftimeGo(fmt, writeLit, writeFmt)
return res.String()
}
// StrftimeLayout is internal.
func StrftimeLayout(fmt string) string {
var res strings.Builder
strftimeGo(fmt, res.WriteByte, res.WriteString)
return res.String()
}
func strftimeGo(fmt string, writeLit func(byte) error, writeFmt func(string) (int, error)) {
// https://strftime.org/ // https://strftime.org/
return strftime(fmt, map[byte]string{ fmts := map[byte]string{
'B': "January", 'B': "January",
'b': "Jan", 'b': "Jan",
'h': "Jan", 'h': "Jan",
@ -41,19 +61,23 @@ func Strftime(fmt string) string {
'%': "%", '%': "%",
't': "\t", 't': "\t",
'n': LineBreak, 'n': LineBreak,
}, map[byte]string{ }
unpaded := map[byte]string{
'm': "1", 'm': "1",
'd': "2", 'd': "2",
'I': "3", 'I': "3",
'M': "4", 'M': "4",
'S': "5", 'S': "5",
}) }
parser(fmt, fmts, unpaded, writeLit, writeFmt)
} }
// StrftimeUTS35 is internal. // StrftimeUTS35 is internal.
func StrftimeUTS35(fmt string) string { func StrftimeUTS35(fmt string) string {
// https://nsdateformatter.com/ // https://nsdateformatter.com/
return strftime(fmt, map[byte]string{ fmts := map[byte]string{
'B': "MMMM", 'B': "MMMM",
'b': "MMM", 'b': "MMM",
'h': "MMM", 'h': "MMM",
@ -91,7 +115,9 @@ func StrftimeUTS35(fmt string) string {
'%': "%", '%': "%",
't': "\t", 't': "\t",
'n': LineBreak, 'n': LineBreak,
}, map[byte]string{ }
unpaded := map[byte]string{
'm': "M", 'm': "M",
'd': "d", 'd': "d",
'j': "D", 'j': "D",
@ -99,12 +125,42 @@ func StrftimeUTS35(fmt string) string {
'I': "h", 'I': "h",
'M': "m", 'M': "m",
'S': "s", 'S': "s",
})
} }
func strftime(fmt string, formats, unpadded map[byte]string) string { const quote = '\''
var literal bool
var res strings.Builder var res strings.Builder
res.Grow(len(fmt))
writeLit := func(b byte) error {
if b == quote {
res.WriteByte(quote)
return res.WriteByte(quote)
}
if !literal && ('a' <= b && b <= 'z' || 'A' <= b && b <= 'Z') {
literal = true
res.WriteByte(quote)
}
return res.WriteByte(b)
}
writeFmt := func(s string) (int, error) {
if literal {
literal = false
res.WriteByte(quote)
}
return res.WriteString(s)
}
parser(fmt, fmts, unpaded, writeLit, writeFmt)
writeFmt("")
return res.String()
}
func parser(
fmt string,
formats, unpadded map[byte]string,
writeLit func(byte) error, writeFmt func(string) (int, error)) {
const ( const (
initial = iota initial = iota
@ -119,7 +175,7 @@ func strftime(fmt string, formats, unpadded map[byte]string) string {
if b == '%' { if b == '%' {
state = special state = special
} else { } else {
res.WriteByte(b) writeLit(b)
} }
case special: case special:
@ -128,21 +184,22 @@ func strftime(fmt string, formats, unpadded map[byte]string) string {
continue continue
} }
if s, ok := formats[b]; ok { if s, ok := formats[b]; ok {
res.WriteString(s) writeFmt(s)
} else { } else {
res.WriteByte('%') writeLit('%')
res.WriteByte(b) writeLit(b)
} }
state = initial state = initial
case padding: case padding:
if s, ok := unpadded[b]; ok { if s, ok := unpadded[b]; ok {
res.WriteString(s) writeFmt(s)
} else if s, ok := formats[b]; ok { } else if s, ok := formats[b]; ok {
res.WriteString(s) writeFmt(s)
} else { } else {
res.WriteString("%-") writeLit('%')
res.WriteByte(b) writeLit('-')
writeLit(b)
} }
state = initial state = initial
} }
@ -150,9 +207,9 @@ func strftime(fmt string, formats, unpadded map[byte]string) string {
switch state { switch state {
case padding: case padding:
res.WriteString("%-") writeLit('%')
fallthrough
case special: case special:
res.WriteByte('%') writeLit('-')
} }
return res.String()
} }