// Package dateformat translates a small, user-friendly token pattern (e.g. // "DD.MM.YYYY HH:mm") into a Go time layout ("02.01.2006 15:04"). It is a // neutral package imported by both internal/api and internal/tenantstore so // they can share one validator without importing each other (which would be a // circular dependency). // // Admins may enter an arbitrary token string; anything that is not a known // token is preserved verbatim as literal text in the resulting layout. package dateformat import ( "fmt" "regexp" "unicode/utf8" ) // MaxPatternLen bounds the token pattern length, mirroring the scan-title // prefix limit so an oversized value can never be persisted. const MaxPatternLen = 40 // tokenLayout maps each supported token to its Go reference-time fragment. // Order matters at match time (longest first) so e.g. "YYYY" is not consumed // as "YY"+"YY"; the regex alternation below encodes that ordering explicitly. var tokenLayout = map[string]string{ "AM/PM": "PM", "YYYY": "2006", "YY": "06", "MM": "01", "DD": "02", "HH": "15", "hh": "03", "mm": "04", "ss": "05", "PM": "PM", } // tokenRE matches known tokens, longest alternatives first so a greedy leftmost // match never splits a long token into shorter ones. var tokenRE = regexp.MustCompile(`AM/PM|YYYY|YY|MM|DD|HH|hh|mm|ss|PM`) // Translate converts a token pattern into a Go time layout. It fails when the // pattern is empty, too long, or contains no recognised token (a pattern of // pure literal text would make time.Format return that literal unchanged, // which is never what the admin intends). func Translate(pattern string) (goLayout string, err error) { if pattern == "" { return "", fmt.Errorf("Format darf nicht leer sein") } if utf8.RuneCountInString(pattern) > MaxPatternLen { return "", fmt.Errorf("Format zu lang (max %d Zeichen)", MaxPatternLen) } matched := false layout := tokenRE.ReplaceAllStringFunc(pattern, func(tok string) string { matched = true return tokenLayout[tok] }) if !matched { return "", fmt.Errorf("Format muss mindestens einen Datums-/Zeit-Platzhalter enthalten") } return layout, nil }