@@ -40,17 +40,18 @@ public MyScheduleActionHandler(
4040 public async Task < ChatbotResponse > HandleAsync ( ChatbotMessage message , ChatbotIntent intent , ChatbotSession session )
4141 {
4242 var culture = session . Culture ;
43+ var cultureInfo = CultureInfo . GetCultureInfo ( ChatbotResources . NormalizeCulture ( culture ) ) ;
4344 try
4445 {
4546 var department = await _departmentsService . GetDepartmentByIdAsync ( session . DepartmentId ) ;
4647 var nowLocal = DateTime . UtcNow . TimeConverter ( department ) ;
4748
4849 intent . Parameters . TryGetValue ( "day" , out var dayText ) ;
49- var targetDate = ParseDay ( dayText , nowLocal ) ;
50+ var targetDate = ParseDay ( dayText , nowLocal , cultureInfo ) ;
5051 if ( targetDate == null )
5152 return new ChatbotResponse { Text = ChatbotResources . Get ( "Sched_BadDate" , culture ) , Processed = false } ;
5253
53- var dateLabel = targetDate . Value . ToString ( "ddd M/d" , CultureInfo . InvariantCulture ) ;
54+ var dateLabel = targetDate . Value . ToString ( "ddd M/d" , cultureInfo ) ;
5455 var lines = new List < string > ( ) ;
5556
5657 // Shifts: shift days on the target date the user is assigned to (shift personnel) or has
@@ -73,7 +74,7 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
7374 continue ;
7475
7576 var shift = shiftDay . Shift ?? await _shiftsService . GetShiftByIdAsync ( shiftDay . ShiftId ) ;
76- var times = $ "{ shiftDay . Start : t } - { shiftDay . End : t } ";
77+ var times = $ "{ shiftDay . Start . ToString ( "t" , cultureInfo ) } - { shiftDay . End . ToString ( "t" , cultureInfo ) } ";
7778 lines . Add ( ChatbotResources . Get ( "Sched_ShiftLine" , culture , shift ? . Name ?? $ "Shift { shiftDay . ShiftId } ", times ) ) ;
7879 }
7980 }
@@ -95,7 +96,7 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
9596 if ( attendee == null || attendee . AttendeeType == ( int ) CalendarItemAttendeeTypes . NotAttending )
9697 continue ;
9798
98- var timeText = item . IsAllDay ? ChatbotResources . Get ( "Sched_AllDay" , culture ) : startLocal . ToString ( "t" ) ;
99+ var timeText = item . IsAllDay ? ChatbotResources . Get ( "Sched_AllDay" , culture ) : startLocal . ToString ( "t" , cultureInfo ) ;
99100 lines . Add ( ChatbotResources . Get ( "Sched_EventLine" , culture , timeText , item . Title ? . Truncate ( 50 ) ) ) ;
100101 }
101102
@@ -119,33 +120,41 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
119120
120121 // Accepts: empty (today), "today", "tomorrow", a weekday name (next occurrence, today included),
121122 // or a date ("7/22", "7/22/2026", "2026-07-22"). Returns null when unparseable.
122- private static DateTime ? ParseDay ( string dayText , DateTime nowLocal )
123+ private static DateTime ? ParseDay ( string dayText , DateTime nowLocal , CultureInfo cultureInfo )
123124 {
124125 if ( string . IsNullOrWhiteSpace ( dayText ) )
125126 return nowLocal . Date ;
126127
127- var text = dayText . Trim ( ) . TrimEnd ( '?' , '!' , '.' , ',' ) . ToLowerInvariant ( ) ;
128+ var text = dayText . Trim ( ) . TrimEnd ( '?' , '!' , '.' , ',' ) ;
128129
129- if ( text == "today" )
130+ if ( string . Equals ( text , "today" , StringComparison . OrdinalIgnoreCase ) )
130131 return nowLocal . Date ;
131- if ( text == "tomorrow" )
132+ if ( string . Equals ( text , "tomorrow" , StringComparison . OrdinalIgnoreCase ) )
132133 return nowLocal . Date . AddDays ( 1 ) ;
133134
134135 foreach ( DayOfWeek dow in Enum . GetValues ( typeof ( DayOfWeek ) ) )
135136 {
136- var name = dow . ToString ( ) . ToLowerInvariant ( ) ;
137- if ( text == name || text == name . Substring ( 0 , 3 ) )
137+ var englishName = dow . ToString ( ) ;
138+ var localizedName = cultureInfo . DateTimeFormat . GetDayName ( dow ) ;
139+ var localizedAbbreviation = cultureInfo . DateTimeFormat . GetAbbreviatedDayName ( dow ) . TrimEnd ( '.' ) ;
140+ if ( string . Equals ( text , englishName , StringComparison . OrdinalIgnoreCase )
141+ || string . Equals ( text , englishName . Substring ( 0 , 3 ) , StringComparison . OrdinalIgnoreCase )
142+ || cultureInfo . CompareInfo . Compare ( text , localizedName , CompareOptions . IgnoreCase ) == 0
143+ || cultureInfo . CompareInfo . Compare ( text , localizedAbbreviation , CompareOptions . IgnoreCase ) == 0 )
138144 {
139145 var daysAhead = ( ( int ) dow - ( int ) nowLocal . DayOfWeek + 7 ) % 7 ;
140146 return nowLocal . Date . AddDays ( daysAhead ) ;
141147 }
142148 }
143149
144- if ( DateTime . TryParse ( text , CultureInfo . InvariantCulture , DateTimeStyles . None , out var parsed ) )
150+ if ( DateTime . TryParse ( text , cultureInfo , DateTimeStyles . None , out var parsed ) )
145151 {
146152 // "7/22" parses with the current year; a date months in the past most likely means next
147153 // year (people ask about upcoming days).
148- if ( parsed . Date < nowLocal . Date . AddMonths ( - 1 ) && ! text . Any ( char . IsLetter ) && text . Count ( c => c == '/' ) == 1 )
154+ var dateSeparator = cultureInfo . DateTimeFormat . DateSeparator ;
155+ var hasSingleDateSeparator = ! string . IsNullOrEmpty ( dateSeparator )
156+ && text . Split ( new [ ] { dateSeparator } , StringSplitOptions . None ) . Length == 2 ;
157+ if ( parsed . Date < nowLocal . Date . AddMonths ( - 1 ) && ! text . Any ( char . IsLetter ) && hasSingleDateSeparator )
149158 parsed = parsed . AddYears ( 1 ) ;
150159 return parsed . Date ;
151160 }
0 commit comments