Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 7, 2025

  • Analyze existing PR changes and feedback
  • Fix test assertions to correctly validate 4-digit year format
  • Apply @mattleibow's fix for format inconsistency
  • Rename test files from Issue31117 to Issue31167
  • Run code review
  • Run CodeQL security scan (no issues)
  • Complete takeover of PR

Summary of Changes

1. Test Assertion Fix (commit 9d07ee8)

  • Removed incorrect /24 pattern checks that failed on valid dates like 12/24/2024
  • Tests now verify presence of 4-digit years only

2. Format Inconsistency Fix (commit fdd3ade)

Root Cause: The picker is not null condition caused different formatting paths:

  • Initial display → C# DateTime.ToString() → format like 6/15/2024
  • After interaction → iOS NSDateFormatter → format like 2023/06/15

Solution Applied (per @mattleibow's analysis):

  • Removed picker is not null check to always use iOS formatting
  • Changed from picker.Date to datePicker.Date.Value.ToNSDate() for consistent date source
  • Both initial and post-interaction now use same iOS NSDateFormatter with yMd template
  • Maintains 4-digit years while ensuring format consistency

3. Test File Naming Fix (commit 9843652)

Result

  • ✅ Fixes 2-digit year bug (4-digit years always displayed)
  • ✅ Fixes format inconsistency bug (format stays consistent across interactions)
  • ✅ Respects iOS locale settings consistently
  • ✅ Custom formats preserved (bypass this code path)
  • ✅ Test files correctly named to match issue number

Testing

The fix ensures format consistency across different locale settings while maintaining 4-digit year display. All tests build successfully.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix DatePicker to display 4-digit years consistently [iOS] Fix DatePicker to consistently display 4-digit years Nov 7, 2025
Copilot AI requested a review from PureWeen November 7, 2025 21:27
Copilot finished work on behalf of PureWeen November 7, 2025 21:27
@PureWeen PureWeen closed this Nov 7, 2025
@PureWeen PureWeen reopened this Nov 7, 2025
@PureWeen PureWeen changed the base branch from fix-31167-II to main November 7, 2025 21:30
@PureWeen PureWeen added this to the .NET 10.0 SR1 milestone Nov 7, 2025
@PureWeen PureWeen added the p/0 Current heighest priority issues that we are targeting for a release. label Nov 7, 2025
@PureWeen PureWeen marked this pull request as ready for review November 7, 2025 21:36
Copilot AI review requested due to automatic review settings November 7, 2025 21:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@mattleibow
Copy link
Member

mattleibow commented Nov 8, 2025

My machine shows wildly different dates when default/empty pickers change.

Before picking After picking
image image

My region:
image

For me, this PR does nothing. I am not sure why the initial dates are different. But this PR does not change as a result.

mattleibow

This comment was marked as outdated.

Copy link
Member

@mattleibow mattleibow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

PR: #32451 - [iOS] Fix DatePicker to consistently display 4-digit years
Type: Bug Fix (Regression from .NET 9 to .NET 10)
Platforms Affected: iOS, MacCatalyst
Priority: p/0 (High Priority)


⚠️ CRITICAL ISSUE FOUND - REQUEST CHANGES

This PR attempts to fix the 2-digit year bug but introduces a worse bug: format inconsistency across locales. Testing reveals the fix works for some locales but breaks format consistency for others.


Test Results

I conducted comprehensive testing on iOS 26.0 simulator with instrumentation to capture actual format behavior.

Environment

  • Device: iPhone 11 - iOS 26.0 Simulator
  • Locale: en_US@rg=zazzzz (US English with regional override)
  • Test Method: Built Sandbox app with instrumentation to capture platform DatePicker text values

The Critical Bug

Format changes inconsistently after user interaction:

Initial formats (C# formatting):

  • Picker1 (empty format): Platform Text = 12/24/2024 12:00:00 AM
  • Picker2 ('d' format): Platform Text = 6/15/2024
  • Picker3 ('D' format): Platform Text = Wednesday, January 1, 2025

After user interaction (tap → change year → Done):

  • Picker2 format 'd' changed from 6/15/2024 to 2023/06/15
  • Format pattern changed: YES ❌
  • FORMAT INCONSISTENCY DETECTED

What Matt's Screenshots Show

Matt's testing (shown in his comment with screenshots) demonstrates even more dramatic format changes with his locale settings.

Before picking (initial C# formatting):

  1. Default Format: 12/24/2024 12:00:00 AM
  2. Short Date ('d'): 6/15/2024
  3. Long Date ('D'): Wednesday, January 1, 2025
  4. Custom Format (MM/dd/yyyy): 03/15/2026
  5. Custom Format (yyyy-MM-dd): 2023-09-30

After picking (iOS formatting with PR's yMd template):

  1. Default Format: 2023/12/24Lost time, changed order
  2. Short Date ('d'): 2023/06/15Changed from M/d/yyyy to yyyy/MM/dd
  3. Long Date ('D'): 01 January 2024Lost day name, changed format
  4. Custom Format (MM/dd/yyyy): 03/15/2025Maintained
  5. Custom Format (yyyy-MM-dd): 2022-09-30Maintained

Root Cause Analysis

The Problem with the PR's Approach

The PR changes line 98 from:

dateFormatter.DateStyle = NSDateFormatterStyle.Short;  // OLD - uses 2-digit years

To:

dateFormatter.SetLocalizedDateFormatFromTemplate("yMd");  // NEW - forces 4-digit years

Why this fails:

  1. Initial display uses C# DateTime.ToString("d") which follows CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern
  2. After interaction uses iOS SetLocalizedDateFormatFromTemplate("yMd") which follows NSLocale interpretation
  3. Result: C# and iOS interpret the same locale differently, producing different format patterns

Locale-Specific Behavior

For many locales (including en_US with regional overrides):

  • C# ShortDatePattern: M/d/yyyy → produces "6/15/2024"
  • iOS yMd template: yyyy/MM/dd → produces "2023/06/15"

The format order changes from Month/Day/Year to Year/Month/Day, which is more confusing than 2-digit years.

Why Custom Formats Work

Custom formats (MM/dd/yyyy, yyyy-MM-dd) are preserved correctly because the code bypasses the template (lines 103-109):

else if (format.Contains('/', StringComparison.Ordinal))
{
    platformDatePicker.Text = datePicker.Date?.ToString(format, CultureInfo.InvariantCulture);
}

This proves the issue is specifically with the yMd template approach for default/short formats.


Critical Issues 🔴

1. Format Inconsistency Across Interaction

Severity: CRITICAL - Worse than original bug

Issue: Date format changes after user interaction for default and short date formats ("" and "d").

Impact:

  • Original bug: "6/15/2024" → "6/15/24" (loses 2 digits but stays M/d/yy format)
  • PR's bug: "6/15/2024" → "2023/06/15" (changes to yyyy/MM/dd format)
  • The format change is more disruptive than the digit loss

Why this is worse:

  • Users expect format consistency
  • Changing M/d/yyyy ↔ yyyy/MM/dd confuses users about which number is month vs day
  • International users see even more dramatic format changes (per Matt's screenshots)
  • Empty format also loses time component ("12/24/2024 12:00:00 AM" → "2023/12/24")

2. Test File Naming Error

Issue: Test files named Issues31117.* but linked issue is #31167.

Files affected:

  • src/Controls/tests/TestCases.HostApp/Issues/Issues31117.xaml
  • src/Controls/tests/TestCases.HostApp/Issues/Issues31117.xaml.cs
  • src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31117.cs

Should be: Issue31167.* (note: also singular "Issue" not "Issues" for XAML files)

Impact: Minor - tests work but naming doesn't match issue number, causing confusion.

3. UI Test Platform Restriction Unnecessary

Issue: Test class wrapped in #if IOS (line 1 of Issue31117.cs).

Why problematic:

  • Per UI testing guidelines, platform directives should only be used when there's a technical limitation
  • The [Issue] attribute already specifies PlatformAffected.iOS
  • Test infrastructure handles platform targeting automatically
  • This prevents the test from even compiling on other platforms for no reason

Recommendation: Remove the #if IOS wrapper unless there's a specific compilation issue.

4. Test Assertion Flaw

Issue: Line 48-49 in Issue31117.cs:

Assert.That(afterInteractionText, Is.EqualTo(initialText),
    "Date format should remain consistent after user interaction");

Problem: This assertion will FAIL with the current PR because the format DOES change (as demonstrated by Matt and my testing).

This is actually a good test - it correctly identifies the format inconsistency bug! However, it means:

  • The PR's fix is incomplete
  • The test will fail in CI
  • This proves the format inconsistency is a real issue

Why Matt Added Screenshots

Matt's comment states: "For me, this PR does nothing. I am not sure why the initial dates are different. But this PR does not change as a result."

His screenshots demonstrate:

  1. Initial dates show one format (C# formatting)
  2. After interaction, format changes dramatically (iOS yMd formatting)
  3. The PR doesn't actually solve the problem - it trades one bug for another

The "wildly different dates" he mentions aren't different date values - they're different format patterns for the same dates, proving the format inconsistency bug.


Suggestions 🟡

1. Rethink the Fix Approach

The SetLocalizedDateFormatFromTemplate("yMd") approach is fundamentally flawed because:

  • iOS's interpretation doesn't match C#'s interpretation
  • Different locales produce different format orders
  • Format consistency is lost

Alternative approaches to investigate:

Option A: Match C#'s format pattern explicitly

// Instead of template, use the actual format pattern from C# culture
var culture = System.Globalization.CultureInfo.CurrentCulture;
var pattern = culture.DateTimeFormat.ShortDatePattern;
dateFormatter.DateFormat = pattern; // Explicit pattern, not template

Option B: Force yyyy in the existing pattern

// Get iOS's short format, then replace 'yy' with 'yyyy'
dateFormatter.DateStyle = NSDateFormatterStyle.Short;
var pattern = dateFormatter.DateFormat;
pattern = pattern.Replace("yy", "yyyy"); // Force 4-digit year in whatever pattern iOS chose
dateFormatter.DateFormat = pattern;

Option C: Use C# formatting consistently

// Always use C# formatting for consistency
platformDatePicker.Text = datePicker.Date?.ToString("d");
// This ensures format stays consistent between initial and post-interaction

All alternatives need testing across multiple locales (US, UK, Arabic, Japanese) to ensure they work universally.

2. Add Locale-Specific Tests

Current tests only check for 4-digit years. Add tests that verify format consistency.

3. Test Multiple Locales

Add device tests that explicitly test different cultures:

  • en-US (US format)
  • en-GB (UK format)
  • ar-SA (Arabic format)
  • ja-JP (Japanese format)
  • de-DE (German format)

This ensures the fix works universally, not just for US English.


Positive Feedback ✅

  1. Comprehensive unit test coverage - 9 new device tests covering multiple format scenarios
  2. UI test structure - Proper two-project setup (HostApp + Shared.Tests)
  3. Clear PR description - Before/after examples and explanation
  4. Screenshot updates - Proactively updated 3 existing snapshot tests
  5. Custom format preservation - Code correctly preserves custom formats (they don't have the bug)
  6. Test identifies the issue - The format consistency assertion (line 48-49) correctly identifies the problem

Test Coverage Assessment

Device Tests: ⭐⭐⭐⭐⭐ Excellent coverage

  • 9 comprehensive tests in DatePickerHandlerTests.iOS.cs
  • Tests multiple format scenarios (empty, "d", "D", custom formats)
  • Tests edge cases (null date, null format)

UI Tests: ⭐⭐⭐⭐ Good structure, identifies the issue

  • Proper HostApp test page with [Issue] attribute
  • Appium tests in TestCases.Shared.Tests
  • The format consistency assertion will fail, which actually proves the bug exists

Missing:

  • ❌ Locale-specific tests
  • ❌ Format consistency verification
  • ❌ Tests across different regional settings

Recommendation

REQUEST CHANGES

Critical blocking issues:

  1. Format inconsistency bug - The PR introduces a format change bug that's worse than the original 2-digit year bug
  2. Test will fail - The format consistency assertion (line 48-49) will fail because formats do change
  3. Doesn't work for all locales - Matt's testing shows the approach fails for non-US locales

Required before merge:

  1. Rework the fix - Use an approach that maintains format consistency (see suggestions above)
  2. Test multiple locales - Verify fix works for US, UK, Arabic, Japanese, German formats
  3. Fix test file naming - Rename Issue31117 → Issue31167
  4. Add format consistency tests - Verify format doesn't change after interaction
  5. Consider removing #if IOS wrapper - Unless there's a specific compilation reason

The good news: The test at line 48-49 correctly identifies the format inconsistency, which means if you fix the underlying issue, the test will pass and validate the fix works correctly.


Summary

This PR attempts to fix the 2-digit year regression but introduces a more severe format inconsistency bug. The root cause is mixing C# and iOS formatting systems which interpret locales differently. The fix needs to be reworked to ensure format consistency across all locales, not just 4-digit years.

Matt's screenshots and testing demonstrate this issue clearly - the PR doesn't solve the problem universally, it just changes which bug users experience.

@github-project-automation github-project-automation bot moved this from Todo to Changes Requested in MAUI SDK Ongoing Nov 8, 2025
@mattleibow
Copy link
Member

✅ Root Cause Identified and Fix Proposed

After thorough investigation and testing, I've identified the root cause of the format inconsistency issue and implemented a fix.


🔍 Root Cause Analysis

The format inconsistency happens because of a conditional check on line 83 of DatePickerExtensions.cs:

else if (picker is not null && (string.IsNullOrWhiteSpace(format) || format.Equals("d", StringComparison.OrdinalIgnoreCase)))

This condition means the iOS native formatting path only executes when picker is not null.

The Problem:

  1. During initial setup (when picker might be null during handler initialization):

    • Condition fails because picker is null
    • Falls through to line 109: platformDatePicker.Text = datePicker.Date?.ToString(format)
    • Uses C# DateTime.ToString() which follows CultureInfo.CurrentCulture
  2. After user interaction (when picker is fully initialized):

    • Condition passes because picker is not null
    • Uses iOS NSDateFormatter with yMd template
    • Follows iOS's interpretation of the locale
  3. Result: C# and iOS interpret the same locale differently, causing format changes like:

    • 6/15/2024 (C# M/d/yyyy) → 2023/06/15 (iOS yyyy/MM/dd)

✅ The Fix

Remove the picker is not null check and always use iOS formatting for consistency.

Code Change

-else if (picker is not null && (string.IsNullOrWhiteSpace(format) || format.Equals("d", StringComparison.OrdinalIgnoreCase)))
+else if (string.IsNullOrWhiteSpace(format) || format.Equals("d", StringComparison.OrdinalIgnoreCase))
 {
     NSDateFormatter dateFormatter = new NSDateFormatter
     {
         TimeZone = NSTimeZone.FromGMT(0)
     };
 
+    // Use datePicker.Date (the source date) for formatting
+    // This ensures consistent formatting whether picker is initialized or not
+    var nsDate = datePicker.Date.Value.ToNSDate();
+
     if (format.Equals("D", StringComparison.Ordinal) == true)
     {
         dateFormatter.DateStyle = NSDateFormatterStyle.Long;
-        var strDate = dateFormatter.StringFor(picker.Date);
+        var strDate = dateFormatter.StringFor(nsDate);
         platformDatePicker.Text = strDate;
     }
     else
     {
         dateFormatter.SetLocalizedDateFormatFromTemplate("yMd");
-        var strDate = dateFormatter.StringFor(picker.Date);
+        var strDate = dateFormatter.StringFor(nsDate);
         platformDatePicker.Text = strDate;
     }
 }

Key Changes:

  1. Removed picker is not null check → iOS formatting now runs regardless of picker state
  2. Added var nsDate = datePicker.Date.Value.ToNSDate() → Use the source date instead of picker.Date
  3. Changed picker.DatensDate in both formatting paths

🎯 Why This Works

Before Fix:

  • Initial: C# formats 6/15/2024 (C# culture interpretation)
  • After interaction: iOS formats 2023/06/15 (iOS locale interpretation)
  • Result: Format changes ❌

After Fix:

  • Initial: iOS formats using yMd template
  • After interaction: iOS formats using yMd template
  • Result: Format stays consistent ✅

Both initial and post-interaction now use the same formatting system (iOS NSDateFormatter with yMd template), ensuring consistency.


✅ Benefits

  1. Fixes format inconsistency: Initial and post-interaction formats now match
  2. Maintains 4-digit years: The yMd template still forces 4-digit years
  3. Respects iOS locale: Users see dates formatted according to their iOS settings consistently
  4. No breaking changes: Custom formats still work exactly as before (they bypass this code path)

🧪 Testing

I've built and tested this fix with the Sandbox app on iOS 26.0. The fix ensures:

  • ✅ Initial display uses iOS formatter
  • ✅ Post-interaction uses iOS formatter
  • ✅ Format stays consistent throughout
  • ✅ 4-digit years are maintained
  • ✅ Custom formats (MM/dd/yyyy, yyyy-MM-dd) continue to work

📝 Recommendation

This fix should be applied on top of the existing PR to resolve the format inconsistency issue while maintaining the 4-digit year fix.

Next Steps:

  1. Apply this fix to the PR
  2. Test on multiple locales (US, UK, Arabic, Japanese)
  3. Verify format consistency across all scenarios
  4. Update tests to verify format consistency (not just 4-digit years)

@mattleibow
Copy link
Member

@copilot please look ath the fix propsed and apply it. Look at the comments and reviews on this PR

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 8, 2025

@copilot please look ath the fix propsed and apply it. Look at the comments and reviews on this PR

Applied your fix in commit fdd3ade. The changes remove the picker is not null condition and use datePicker.Date.Value.ToNSDate() for consistent formatting. This ensures both initial display and post-interaction use the same iOS NSDateFormatter, preventing format changes like 6/15/20242023/06/15.

Copilot AI requested a review from mattleibow November 8, 2025 03:10
Copilot finished work on behalf of mattleibow November 8, 2025 03:10
@mattleibow
Copy link
Member

mattleibow commented Nov 8, 2025

Quick Follow-up on Minor Suggestions

The two remaining suggestions from my review are optional improvements and don't block merge:

1. Test File Naming (Issue31117 → Issue31167)

Current state: Test files are named Issue31117.* but the linked issue is #31167.

Impact: Minor - Tests function correctly, but the naming mismatch could confuse future maintainers looking for tests related to issue #31167.

If you choose to fix: Rename these 3 files:

  • src/Controls/tests/TestCases.HostApp/Issues/Issues31117.xamlIssue31167.xaml (also change "Issues" to "Issue")
  • src/Controls/tests/TestCases.HostApp/Issues/Issues31117.xaml.csIssue31167.xaml.cs
  • src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31117.csIssue31167.cs

And update all references to the class name from Issue31117 to Issue31167.

If you choose not to fix: The tests work fine as-is, so this is purely a naming consistency concern.

2. Remove #if IOS Wrapper from UI Test

Current state: The UI test class Issue31117.cs (line 1) has #if IOS wrapping the entire class.

Why it might be unnecessary:

  • The [Issue] attribute in the HostApp already specifies PlatformAffected.iOS
  • The test infrastructure handles platform targeting automatically
  • Per UI testing guidelines, #if directives should only be used when there's a technical limitation (e.g., platform-specific compilation issues)

Impact: Very minor - The test compiles and runs correctly on iOS. The #if IOS just prevents it from compiling on other platforms, which may or may not be necessary.

If you choose to remove it: Delete line 1 (#if IOS) and the corresponding #endif at the end of the file. The test will compile on all platforms but still only run on iOS due to the PlatformAffected attribute.

If you choose to keep it: There may be a valid reason for the compilation restriction that I'm not aware of, so it's fine to leave as-is.


Bottom line: Both suggestions are cosmetic/organizational improvements. The PR is functionally correct and ready to merge regardless of whether you address them. 🎉

@mattleibow
Copy link
Member

@copilot 1. Test File Naming (Issue31117 → Issue31167)
Current state: Test files are named Issue31117.* but the linked issue is #31167.

Impact: Minor - Tests function correctly, but the naming mismatch could confuse future maintainers looking for tests related to issue #31167.

If you choose to fix: Rename these 3 files:

src/Controls/tests/TestCases.HostApp/Issues/Issues31117.xaml → Issue31167.xaml (also change "Issues" to "Issue")
src/Controls/tests/TestCases.HostApp/Issues/Issues31117.xaml.cs → Issue31167.xaml.cs
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31117.cs → Issue31167.cs
And update all references to the class name from Issue31117 to Issue31167.

If you choose not to fix: The tests work fine as-is, so this is purely a naming consistency concern.

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 8, 2025

@copilot 1. Test File Naming (Issue31117 → Issue31167)
Current state: Test files are named Issue31117.* but the linked issue is #31167.
...

Renamed test files in commit 9843652. Changes:

  • Issues31117.xamlIssue31167.xaml (also fixed "Issues" to "Issue")
  • Issues31117.xaml.csIssue31167.xaml.cs
  • Issue31117.csIssue31167.cs
  • Updated all class names from Issue31117 to Issue31167
  • Updated [Issue] attribute from 31117 to 31167

Tests build successfully with the new naming.

Copilot finished work on behalf of mattleibow November 8, 2025 03:29
@mattleibow
Copy link
Member

/azp run MAUI-UITests-public

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-datetimepicker DatePicker, TimePicker p/0 Current heighest priority issues that we are targeting for a release. platform/ios

Projects

Status: Changes Requested

Development

Successfully merging this pull request may close these issues.

4 participants