feat(quando-95w): implement custom layout formatting with i18n support
Add FormatLayout() method enabling custom date formatting using Go's standard layout format (Mon Jan 2 15:04:05 MST 2006) with full internationalization support for month and weekday names. Key features: - Custom layout strings using Go's time.Format() reference date - i18n support for EN and DE languages - Translates both full and abbreviated month/weekday names - Fast path for EN (direct passthrough, ~144-173 ns/op) - Optimized replacements using strings.NewReplacer (~7.3-7.5 µs/op for DE) - Comprehensive test coverage (97.6%) Implementation: - Added FormatLayout() method to format.go - Uses strings.NewReplacer for atomic replacements to avoid substring collisions - Added 280+ lines of tests covering all months, weekdays, and edge cases - Added 4 benchmark tests (all meet <10µs target) - Added 4 example tests demonstrating usage Performance: - EN (fast path): ~144-173 ns/op - DE (with i18n): ~7.3-7.5 µs/op - Both well under <10µs requirement Files changed: - format.go: Added FormatLayout() method with full godoc - format_test.go: Added comprehensive tests and benchmarks - example_test.go: Added 4 example functions
This commit is contained in:
parent
06ad5d67d3
commit
39397ea6df
4 changed files with 413 additions and 1 deletions
87
format.go
87
format.go
|
|
@ -2,6 +2,7 @@ package quando
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -132,6 +133,92 @@ func (d Date) formatLong() string {
|
|||
}
|
||||
}
|
||||
|
||||
// FormatLayout formats the date using a custom layout string with localized month/weekday names.
|
||||
//
|
||||
// The layout parameter uses Go's standard time layout format (Mon Jan 2 15:04:05 MST 2006).
|
||||
// Month and weekday names in the output are translated according to the Date's Lang setting.
|
||||
//
|
||||
// Supported layout components (see time.Format documentation for full details):
|
||||
// - "January" - Full month name (localized)
|
||||
// - "Jan" - Short month name (localized)
|
||||
// - "Monday" - Full weekday name (localized)
|
||||
// - "Mon" - Short weekday name (localized)
|
||||
// - All numeric components (year, day, hour, etc.) - not localized
|
||||
//
|
||||
// Language Support:
|
||||
// - EN (English) - Default, no translation
|
||||
// - DE (German) - Translates month/weekday names
|
||||
//
|
||||
// Performance: < 10 µs for typical layouts with i18n
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// date := quando.From(time.Date(2026, 2, 9, 14, 30, 0, 0, time.UTC))
|
||||
//
|
||||
// // English (default)
|
||||
// date.FormatLayout("Monday, 2. January 2006") // "Monday, 9. February 2026"
|
||||
//
|
||||
// // German
|
||||
// date.WithLang(quando.DE).FormatLayout("Monday, 2. January 2006") // "Montag, 9. Februar 2026"
|
||||
// date.WithLang(quando.DE).FormatLayout("Mon, 02 Jan 2006") // "Mo, 09 Feb 2026"
|
||||
func (d Date) FormatLayout(layout string) string {
|
||||
// Fast path: if lang is EN (or not set), just use Go's format directly
|
||||
lang := d.lang
|
||||
if lang == "" || lang == EN {
|
||||
return d.t.Format(layout)
|
||||
}
|
||||
|
||||
// Format using Go's time.Format (always in English)
|
||||
formatted := d.t.Format(layout)
|
||||
|
||||
// Build replacement pairs: old (English) -> new (localized)
|
||||
// Order matters: longest strings first to avoid partial matches
|
||||
// We use strings.Replacer which processes all replacements in a single pass
|
||||
var replacementPairs []string
|
||||
|
||||
// 1. Full month names first (e.g., "September" before "Sep")
|
||||
for m := time.January; m <= time.December; m++ {
|
||||
enFull := monthNames[EN][m-1]
|
||||
localFull := lang.MonthName(m)
|
||||
if enFull != localFull {
|
||||
replacementPairs = append(replacementPairs, enFull, localFull)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Full weekday names (e.g., "Wednesday" before "Wed")
|
||||
for wd := time.Sunday; wd <= time.Saturday; wd++ {
|
||||
enFull := weekdayNames[EN][wd]
|
||||
localFull := lang.WeekdayName(wd)
|
||||
if enFull != localFull {
|
||||
replacementPairs = append(replacementPairs, enFull, localFull)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Short month names
|
||||
for m := time.January; m <= time.December; m++ {
|
||||
enShort := monthNamesShort[EN][m-1]
|
||||
localShort := lang.MonthNameShort(m)
|
||||
if enShort != localShort {
|
||||
replacementPairs = append(replacementPairs, enShort, localShort)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Short weekday names
|
||||
for wd := time.Sunday; wd <= time.Saturday; wd++ {
|
||||
enShort := weekdayNamesShort[EN][wd]
|
||||
localShort := lang.WeekdayNameShort(wd)
|
||||
if enShort != localShort {
|
||||
replacementPairs = append(replacementPairs, enShort, localShort)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a replacer and apply all replacements in a single pass
|
||||
// This ensures that once a full name is replaced, the short name in the
|
||||
// replacement won't be affected (e.g., "Monday" -> "Montag", and "Mon" in "Montag" won't become "Mo")
|
||||
replacer := strings.NewReplacer(replacementPairs...)
|
||||
return replacer.Replace(formatted)
|
||||
}
|
||||
|
||||
// String returns the string representation of the Format type.
|
||||
// This is used for better test output and debugging.
|
||||
func (f Format) String() string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue