| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- package main
- import (
- "strings"
- "time"
- )
- // refTime is the canonical reference instant used to validate Go time layouts.
- var refTime = time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC)
- // parseDateLayout accepts either a Go time layout (e.g. "02.01.2006",
- // "2006-01-02") or a common placeholder format (e.g. "dd.MM.yyyy",
- // "yyyy-MM-dd HH:mm") and returns a Go time layout. An empty input yields
- // "" so the caller can apply its own default. Values that are neither a
- // valid Go layout nor a recognized placeholder string are returned
- // unchanged (the caller's Format call will then produce whatever Go does).
- func parseDateLayout(s string) string {
- s = strings.TrimSpace(s)
- if s == "" {
- return ""
- }
- // If it already behaves like a Go layout, keep it as-is.
- if isGoLayout(s) {
- return s
- }
- return placeholdersToGoLayout(s)
- }
- // isGoLayout reports whether s is a usable Go time layout by round-tripping
- // a reference instant through Format and Parse.
- func isGoLayout(s string) bool {
- formatted := refTime.Format(s)
- parsed, err := time.Parse(s, formatted)
- if err != nil {
- return false
- }
- return parsed.UTC().Equal(refTime)
- }
- // placeholdersToGoLayout converts common date/time placeholders into the
- // equivalent Go time layout. Tokens are matched case-sensitively, longest
- // first, so that "MM" (month) is not confused with "mm" (minute).
- func placeholdersToGoLayout(s string) string {
- // Ordered longest-first to avoid partial token matches.
- replacements := []struct{ from, to string }{
- {"yyyy", "2006"},
- {"MMMM", "January"},
- {"MMM", "Jan"},
- {"MM", "01"},
- {"dd", "02"},
- {"HH", "15"},
- {"mm", "04"},
- {"ss", "05"},
- {"yy", "06"},
- {"ZZZ", "MST"},
- {"ZZ", "Z07:00"},
- {"Z", "Z07:00"},
- {"zzz", "MST"},
- {"zz", "Z07:00"},
- {"z", "MST"},
- {"M", "1"},
- {"d", "2"},
- {"H", "3"},
- {"m", "4"},
- {"s", "5"},
- }
- // Walk the string, consuming the longest matching token at each position.
- var b strings.Builder
- i := 0
- for i < len(s) {
- matched := false
- for _, r := range replacements {
- if strings.HasPrefix(s[i:], r.from) {
- b.WriteString(r.to)
- i += len(r.from)
- matched = true
- break
- }
- }
- if !matched {
- b.WriteByte(s[i])
- i++
- }
- }
- return b.String()
- }
|