Skip to content

Commit cc80a72

Browse files
PyAgniclaude
andcommitted
fix: handle Unicode whitespace and UK/Indian date formats from AppleScript
macOS inserts a narrow no-break space (U+202F) before AM/PM in some locales, causing date parsing failures. Added whitespace normalization and UK/Indian locale date formats (day before month). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 47acc44 commit cc80a72

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

internal/applescript/parser.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,15 @@ var appleScriptDateFormats = []string{
129129
"1/2/2006, 3:04:05 PM",
130130
// Another common variant: "2 January 2006 at 15:04:05"
131131
"2 January 2006 at 15:04:05",
132+
// UK/Indian locale: "Monday, 2 January 2006 at 3:04:05 PM"
133+
"Monday, 2 January 2006 at 3:04:05 PM",
134+
// UK/Indian without day of week: "2 January 2006 at 3:04:05 PM"
135+
"2 January 2006 at 3:04:05 PM",
132136
// Date-only formats as fallback.
133137
"Monday, January 2, 2006",
134138
"January 2, 2006",
139+
"Monday, 2 January 2006",
140+
"2 January 2006",
135141
}
136142

137143
// ParseAppleScriptDate attempts to parse a date string from AppleScript output.
@@ -141,6 +147,9 @@ func ParseAppleScriptDate(s string) (time.Time, error) {
141147
s = strings.TrimSpace(s)
142148
// Strip "date " prefix that AppleScript sometimes includes.
143149
s = strings.TrimPrefix(s, "date ")
150+
// Normalize Unicode whitespace characters (e.g. narrow no-break space \u202f)
151+
// that macOS inserts before AM/PM in some locales.
152+
s = normalizeWhitespace(s)
144153

145154
for _, format := range appleScriptDateFormats {
146155
t, err := time.Parse(format, s)
@@ -152,6 +161,22 @@ func ParseAppleScriptDate(s string) (time.Time, error) {
152161
return time.Time{}, fmt.Errorf("unable to parse date %q: no matching format found", s)
153162
}
154163

164+
// normalizeWhitespace replaces Unicode whitespace characters (such as the
165+
// narrow no-break space \u202f that macOS inserts before AM/PM) with regular spaces.
166+
func normalizeWhitespace(s string) string {
167+
var b strings.Builder
168+
b.Grow(len(s))
169+
for _, r := range s {
170+
switch r {
171+
case '\u202f', '\u00a0', '\u2009', '\u200a':
172+
b.WriteRune(' ')
173+
default:
174+
b.WriteRune(r)
175+
}
176+
}
177+
return b.String()
178+
}
179+
155180
// truncate shortens a string to maxLen characters for display in error messages.
156181
func truncate(s string, maxLen int) string {
157182
if len(s) <= maxLen {

internal/applescript/parser_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,29 @@ func TestParseAppleScriptDate_WithoutDayOfWeek(t *testing.T) {
174174
assert.Equal(t, 18, d.Day())
175175
}
176176

177+
func TestParseAppleScriptDate_UKIndianLocale(t *testing.T) {
178+
d, err := ParseAppleScriptDate("Wednesday, 18 March 2026 at 4:39:41 PM")
179+
require.NoError(t, err)
180+
assert.Equal(t, 2026, d.Year())
181+
assert.Equal(t, 3, int(d.Month()))
182+
assert.Equal(t, 18, d.Day())
183+
assert.Equal(t, 16, d.Hour())
184+
}
185+
186+
func TestParseAppleScriptDate_NarrowNoBreakSpace(t *testing.T) {
187+
// macOS inserts \u202f (narrow no-break space) before AM/PM.
188+
d, err := ParseAppleScriptDate("Wednesday, 18 March 2026 at 4:39:41\u202fPM")
189+
require.NoError(t, err)
190+
assert.Equal(t, 2026, d.Year())
191+
assert.Equal(t, 16, d.Hour())
192+
}
193+
194+
func TestNormalizeWhitespace(t *testing.T) {
195+
assert.Equal(t, "4:39 PM", normalizeWhitespace("4:39\u202fPM"))
196+
assert.Equal(t, "no change", normalizeWhitespace("no change"))
197+
assert.Equal(t, "a b", normalizeWhitespace("a\u00a0b"))
198+
}
199+
177200
func TestParseAppleScriptDate_InvalidFormat(t *testing.T) {
178201
_, err := ParseAppleScriptDate("not a date at all")
179202
require.Error(t, err)

0 commit comments

Comments
 (0)