From 681f5703caaa288459793d9e3af96d603c597117 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Wed, 30 Mar 2022 01:28:19 +0100 Subject: [PATCH] Date formatting. --- cmd/zenity/main.go | 2 +- date_darwin.go | 3 +- date_unix.go | 3 +- internal/zenutil/date.go | 101 ++++++++++++++++++++++++++++++--------- 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/cmd/zenity/main.go b/cmd/zenity/main.go index 4eb63fd..9257913 100644 --- a/cmd/zenity/main.go +++ b/cmd/zenity/main.go @@ -522,7 +522,7 @@ func lstResult(l []string, err error) { func calResult(d time.Time, err error) { errResult(err) - os.Stdout.WriteString(d.Format(zenutil.Strftime(zenutil.DateFormat))) + os.Stdout.WriteString(zenutil.Strftime(zenutil.DateFormat, d)) os.Stdout.WriteString(zenutil.LineBreak) } diff --git a/date_darwin.go b/date_darwin.go index 2de3344..11f6999 100644 --- a/date_darwin.go +++ b/date_darwin.go @@ -27,5 +27,6 @@ func calendar(text string, opts options) (time.Time, error) { if err != nil { return time.Time{}, err } - return time.Parse(zenutil.Strftime(zenutil.DateFormat), str) + layout := zenutil.StrftimeLayout(zenutil.DateFormat) + return time.Parse(layout, str) } diff --git a/date_unix.go b/date_unix.go index 366beb1..80c8117 100644 --- a/date_unix.go +++ b/date_unix.go @@ -27,5 +27,6 @@ func calendar(text string, opts options) (time.Time, error) { if err != nil { return time.Time{}, err } - return time.Parse(zenutil.Strftime(zenutil.DateFormat), str) + layout := zenutil.StrftimeLayout(zenutil.DateFormat) + return time.Parse(layout, str) } diff --git a/internal/zenutil/date.go b/internal/zenutil/date.go index 46a5f9f..e540abe 100644 --- a/internal/zenutil/date.go +++ b/internal/zenutil/date.go @@ -1,11 +1,31 @@ package zenutil -import "strings" +import ( + "strings" + "time" +) // 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/ - return strftime(fmt, map[byte]string{ + fmts := map[byte]string{ 'B': "January", 'b': "Jan", 'h': "Jan", @@ -41,19 +61,23 @@ func Strftime(fmt string) string { '%': "%", 't': "\t", 'n': LineBreak, - }, map[byte]string{ + } + + unpaded := map[byte]string{ 'm': "1", 'd': "2", 'I': "3", 'M': "4", 'S': "5", - }) + } + + parser(fmt, fmts, unpaded, writeLit, writeFmt) } // StrftimeUTS35 is internal. func StrftimeUTS35(fmt string) string { // https://nsdateformatter.com/ - return strftime(fmt, map[byte]string{ + fmts := map[byte]string{ 'B': "MMMM", 'b': "MMM", 'h': "MMM", @@ -91,7 +115,9 @@ func StrftimeUTS35(fmt string) string { '%': "%", 't': "\t", 'n': LineBreak, - }, map[byte]string{ + } + + unpaded := map[byte]string{ 'm': "M", 'd': "d", 'j': "D", @@ -99,12 +125,42 @@ func StrftimeUTS35(fmt string) string { 'I': "h", 'M': "m", 'S': "s", - }) + } + + const quote = '\'' + var literal bool + var res strings.Builder + + 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 strftime(fmt string, formats, unpadded map[byte]string) string { - var res strings.Builder - res.Grow(len(fmt)) +func parser( + fmt string, + formats, unpadded map[byte]string, + writeLit func(byte) error, writeFmt func(string) (int, error)) { const ( initial = iota @@ -119,7 +175,7 @@ func strftime(fmt string, formats, unpadded map[byte]string) string { if b == '%' { state = special } else { - res.WriteByte(b) + writeLit(b) } case special: @@ -128,21 +184,22 @@ func strftime(fmt string, formats, unpadded map[byte]string) string { continue } if s, ok := formats[b]; ok { - res.WriteString(s) + writeFmt(s) } else { - res.WriteByte('%') - res.WriteByte(b) + writeLit('%') + writeLit(b) } state = initial case padding: if s, ok := unpadded[b]; ok { - res.WriteString(s) + writeFmt(s) } else if s, ok := formats[b]; ok { - res.WriteString(s) + writeFmt(s) } else { - res.WriteString("%-") - res.WriteByte(b) + writeLit('%') + writeLit('-') + writeLit(b) } state = initial } @@ -150,9 +207,9 @@ func strftime(fmt string, formats, unpadded map[byte]string) string { switch state { case padding: - res.WriteString("%-") + writeLit('%') + fallthrough case special: - res.WriteByte('%') + writeLit('-') } - return res.String() }