Repository: beeware/toga

Issue:
Winforms datepicker widget returning unexpected date format
**Describe the bug**
The winforms datepicker native widget returns a date format different to the expected format. A ValueError exception is raised. For example "time data 'Saturday, 1 January 2011' does not match format '%A, %B %d, %Y'".

**To Reproduce**
Steps to reproduce the behavior:
Run the following code and click the update button:

```
from toga import App, MainWindow, Label, Box, Button, DatePicker
from toga.constants import COLUMN
from toga.style import Pack


class Application(App):
    def startup(self):

        self.main_window = MainWindow(title=self.name)

        self.button = Button(
            "Update",
            on_press=self.update,
            style=Pack(flex=1, width=200),
        )

        self.box1 = Box(
            style=Pack(
                direction=COLUMN,
            )
        )

        self.date_input = DatePicker()

        self.box1.add(self.date_input)
        self.box1.add(self.button)
        self.main_window.content = self.box1

        self.main_window.content.refresh()
        self.main_window.show()

    def update(self, widget, **kwargs):
        print(self.date_input.value)


if __name__ == "__main__":
    app = Application("BugApp", "App")
    app.main_loop()
```
A traceback similiar to the folllowing will be generated:
```
Traceback (most recent call last):
  File "C:\Program Files\Python310\lib\_strptime.py", line 349, in _strptime
    raise ValueError("time data %r does not match format %r" %
  File "C:\Program Files\Python310\lib\_strptime.py", line 568, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
  File "C:\Users\gregc\python\toga\toga\winforms\src\toga_winforms\widgets\datepicker.py", line 15, in get_value
    return datetime.datetime.strptime(self.native.Text, "%A, %B %d, %Y").date()
  File "C:\Users\gregc\python\toga\toga\core\src\toga\widgets\datepicker.py", line 75, in value
    return self._impl.get_value()
  File "C:\Users\gregc\python\toga\test2.py", line 33, in update
    print(self.date_input.value)
  File "C:\Users\gregc\python\toga\toga\core\src\toga\handlers.py", line 64, in _handler
    result = handler(interface, *args, **kwargs)
  File "C:\Users\gregc\python\toga\toga\winforms\src\toga_winforms\widgets\button.py", line 19, in winforms_click
    self.interface.on_press(self.interface)
   at Python.Runtime.PythonException.ThrowLastAsClrException()
   at Python.Runtime.Dispatcher.TrueDispatch(Object[] args)
   at Python.Runtime.Dispatcher.Dispatch(Object[] args)
   at __System_EventHandlerDispatcher.Invoke(Object , EventArgs )
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
time data 'Friday, 2 February 3201' does not match format '%A, %B %d, %Y'
```

**Expected behavior**
The date should be printed to the console eg. "2011-02-01".

**Screenshots**
![datepicker](https://user-images.githubusercontent.com/22849618/204701755-5208d6cb-f5d1-4fdd-bfef-62b1ba80a29f.png)

**Environment:**
 - Operating System: Windows 11
 - Python version: 3.10.8
 - Software versions:
   - Toga: 0.3.0.dev39

**Additional context**
I have a proposed fix. I will submit a pull request when I can work out why CI is failing:
![ci](https://user-images.githubusercontent.com/22849618/204702429-3c17d686-d878-4d58-bd58-0d42cb0954a5.png)



Discussion:
Thanks for the report.

On the surface, this seems like a case of "how did this *ever* work"... except that I can see I'm definitely getting the extra comma in my display of dates:
<img width="330" alt="Screen Shot 2022-11-30 at 12 09 16 pm" src="https://user-images.githubusercontent.com/37345/204705576-b8ccac80-2596-48c2-b996-2be3684570b4.png">

Don't worry too much about that specific CI failure though; codecov has a nasty habit of failing occasionally for reasons that have nothing to do with the submitted code. If the rest of the tests are passing, we'll work around the codecov limitations at our end.

I suspect that the format of the date returned by the winforms native widget depends on the Windows date settings configured on the machine. It might be better to use something like dateutil to parse the date string instead of strptime. Another option might be to tell the winforms native widget to use a custom date format so it is independent of the machine settings. Any preference?
I'm not wild about the dateutil approach - while it's a great library, there are edge cases where the answer is inherently ambiguous (1/2/2022) and I've found dateutil's tendency to guess can be problematic.

A custom widget date format would seem the better approach. If it's entirely internally facing (i.e., it doesn't impact on what the user sees, it's just a data interchange format), then anything close to ISO8601 will be fine. If it impacts on what the user sees, then any format that is consistent and unambiguous will do for now. Longer term, we need to work out a localisation strategy for Toga apps.

Resolving patch:
diff --git a/winforms/src/toga_winforms/libs/__init__.py b/winforms/src/toga_winforms/libs/__init__.py
--- a/winforms/src/toga_winforms/libs/__init__.py
+++ b/winforms/src/toga_winforms/libs/__init__.py
@@ -9,6 +9,7 @@
     Bitmap,
     Color,
     Convert,
+    CultureInfo,
     Drawing2D,
     FillMode,
     FontFamily,
diff --git a/winforms/src/toga_winforms/libs/winforms.py b/winforms/src/toga_winforms/libs/winforms.py
--- a/winforms/src/toga_winforms/libs/winforms.py
+++ b/winforms/src/toga_winforms/libs/winforms.py
@@ -46,6 +46,7 @@
 )
 from System.Drawing.Imaging import ImageFormat  # noqa: F401, E402
 from System.Drawing.Text import PrivateFontCollection  # noqa: F401, E402
+from System.Globalization import CultureInfo  # noqa: F401, E402
 from System.IO import FileNotFoundException, MemoryStream  # noqa: F401, E402
 from System.Net import SecurityProtocolType, ServicePointManager  # noqa: F401, E402
 from System.Runtime.InteropServices import ExternalException  # noqa: F401, E402
diff --git a/winforms/src/toga_winforms/widgets/datepicker.py b/winforms/src/toga_winforms/widgets/datepicker.py
--- a/winforms/src/toga_winforms/widgets/datepicker.py
+++ b/winforms/src/toga_winforms/widgets/datepicker.py
@@ -2,7 +2,7 @@
 
 from travertino.size import at_least
 
-from toga_winforms.libs import WinDateTime, WinForms
+from toga_winforms.libs import CultureInfo, WinDateTime, WinForms
 
 from .base import Widget
 
@@ -12,7 +12,12 @@ def create(self):
         self.native = WinForms.DateTimePicker()
 
     def get_value(self):
-        return datetime.datetime.strptime(self.native.Text, "%A, %B %d, %Y").date()
+        return datetime.datetime.strptime(
+            self.native.Value.ToString(
+                "yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture
+            ),
+            "%Y-%m-%dT%H:%M:%S%z",
+        ).date()
 
     def set_value(self, value):
         self.native.Value = WinDateTime(value.year, value.month, value.day)


Test patch: