Skip to content

Commit 638fb08

Browse files
committed
fix: enhance bracket handling in formatting and add concurrency test for internationalization
1 parent fca80ab commit 638fb08

File tree

2 files changed

+92
-7
lines changed

2 files changed

+92
-7
lines changed

pkg/day/day.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,30 @@ func nextStdChunk(from []rune) (to, suffix []rune) {
174174
case "a":
175175
old = "a"
176176
case "[":
177-
if len(from) >= 4 && string(from[:4]) == "[at]" {
178-
old = "[at]"
177+
// treat anything inside [] as literal text, e.g. [at], [a las], [o]
178+
end := -1
179+
for i := 1; i < len(from); i++ {
180+
if from[i] == ']' {
181+
end = i
182+
break
183+
}
184+
}
185+
if end != -1 {
186+
to = []rune(string(from[1:end]))
187+
suffix = from[end+1:]
188+
return
179189
}
190+
// no closing bracket, fall back to literal "["
191+
old = "["
180192
default:
181193
old = s
182194
}
183195

196+
if old == "" {
197+
// safety: always consume at least one rune
198+
old = s
199+
}
200+
184201
tos, ok := placeholder[old]
185202
if !ok {
186203
to = []rune(old)

pkg/day/day_test.go

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,85 @@
2020
package day
2121

2222
import (
23+
"os"
24+
"path/filepath"
25+
"runtime"
2326
"testing"
2427
"time"
2528

2629
"github.com/stretchr/testify/assert"
30+
"gopkg.in/yaml.v3"
2731
)
2832

2933
func TestFormat(t *testing.T) {
30-
sec := time.Now().Unix()
31-
tz := "Asia/Shanghai"
32-
actual := Format(sec, "YYYY-MM-DD HH:mm:ss", tz)
33-
_, _ = time.LoadLocation(tz)
34-
expected := time.Unix(sec, 0).Format("2006-01-02 15:04:05")
34+
sec := int64(1700000000)
35+
tz := "UTC"
36+
37+
actual := Format(sec, "MMM D, YYYY [a las] HH:mm", tz)
38+
expected := time.Unix(sec, 0).Format("Jan 2, 2006") + " a las " + time.Unix(sec, 0).Format("15:04")
3539
assert.Equal(t, expected, actual)
3640
}
41+
42+
func TestFormat_AllLanguagesNoHang(t *testing.T) {
43+
_, currentFile, _, ok := runtime.Caller(0)
44+
if !ok {
45+
t.Fatalf("unable to determine test file path")
46+
}
47+
i18nDir := filepath.Clean(filepath.Join(filepath.Dir(currentFile), "..", "..", "i18n"))
48+
entries, err := os.ReadDir(i18nDir)
49+
if err != nil {
50+
t.Fatalf("read i18n dir: %v", err)
51+
}
52+
53+
type datesConfig struct {
54+
LongDate string `yaml:"long_date"`
55+
LongDateWithYear string `yaml:"long_date_with_year"`
56+
LongDateWithTime string `yaml:"long_date_with_time"`
57+
}
58+
type uiConfig struct {
59+
Dates datesConfig `yaml:"dates"`
60+
}
61+
type fileConfig struct {
62+
UI uiConfig `yaml:"ui"`
63+
}
64+
65+
sec := int64(1700000000)
66+
tz := "UTC"
67+
68+
for _, entry := range entries {
69+
name := entry.Name()
70+
if entry.IsDir() || filepath.Ext(name) != ".yaml" || name == "i18n.yaml" {
71+
continue
72+
}
73+
data, err := os.ReadFile(filepath.Join(i18nDir, name))
74+
if err != nil {
75+
t.Fatalf("read %s: %v", name, err)
76+
}
77+
var cfg fileConfig
78+
if err := yaml.Unmarshal(data, &cfg); err != nil {
79+
t.Fatalf("parse %s: %v", name, err)
80+
}
81+
82+
formats := []string{
83+
cfg.UI.Dates.LongDate,
84+
cfg.UI.Dates.LongDateWithYear,
85+
cfg.UI.Dates.LongDateWithTime,
86+
}
87+
for _, format := range formats {
88+
if format == "" {
89+
continue
90+
}
91+
done := make(chan struct{})
92+
go func(f string) {
93+
_ = Format(sec, f, tz)
94+
close(done)
95+
}(format)
96+
97+
select {
98+
case <-done:
99+
case <-time.After(200 * time.Millisecond):
100+
t.Fatalf("format hang in %s: %q", name, format)
101+
}
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)