Skip to content

Add shorter format datetime-local defaults#761

Merged
azmeuk merged 4 commits intopallets-eco:masterfrom
russellfinlay:master
Jan 13, 2023
Merged

Add shorter format datetime-local defaults#761
azmeuk merged 4 commits intopallets-eco:masterfrom
russellfinlay:master

Conversation

@russellfinlay
Copy link
Copy Markdown
Contributor

Describe the issue you are attempting to fix

This issue was previously raised in #450.

The HTML standard for datetime-local inputs set the default step attribute to 60, meaning that the lowest denomination of input can be expressed in minutes, not seconds.

https://html.spec.whatwg.org/#local-date-and-time-state-(type=datetime-local)

The step attribute is expressed in seconds. The step scale factor is 1000 (which converts the seconds to milliseconds, as used in the other algorithms). The default step is 60 seconds.

In such cases, it seems that common browser behaviour is to then send the input value, without any seconds, on form submission, in the format -

2022-11-02T12:23

The current default of WTForms is to validate against two possible date formats, both that require seconds -

def __init__(self, *args, **kwargs):
        kwargs.setdefault("format", ["%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S"])

If seconds are not found, the validation will fail -

Not a valid datetime value.

Current fixes

This issue can be fixed by adding the correct format to a DateTimeLocalField() instance -

example_date_time = DateTimeLocalField('Example datetime', format="%Y-%m-%dT%H:%M")

or by change input step size to allow seconds (in HTML)**

<input type="datetime-local" step="1">

Proposal

Very simple PR to bring WTForms in line with the default behaviour of some browsers, by adding two formats (without seconds) to the default formats of DateTimeLocalField.

    def __init__(self, *args, **kwargs):
        kwargs.setdefault("format", ["%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%dT%H:%M"])

Allows validation of short datetime-local formats (%Y-%m-%dT%H:%M) by default, as it the default in common browsers (where step size is unchanged).

Relates to pallets-eco#450
@azmeuk
Copy link
Copy Markdown
Member

azmeuk commented Dec 24, 2022

Thank you for your contribution.
Indeed I suppose this field should work out-of-the-box with common browsers.
Can you add a CHANGELOG entry and fix the style tests?

@azmeuk
Copy link
Copy Markdown
Member

azmeuk commented Dec 25, 2022

Thank you. The style test is still failing though.

@russellfinlay
Copy link
Copy Markdown
Contributor Author

That should make the tox style testing pass now. It seems that black wanted to re-format that line.

I've also seen that two tests are failing from test_datetime.py. Would you like me to look at those before merge?

@azmeuk
Copy link
Copy Markdown
Member

azmeuk commented Dec 26, 2022

The GHA tests look OK. Are you seeing issues with test_datetime.py when you locally run the tests? What error message do you see?

@russellfinlay
Copy link
Copy Markdown
Contributor Author

Two tests failing when invoking pytest, one in tests\fields\test_datetime.py and the other in tests\fields\test_datetimelocal.py.

I've just re-run the tests without the changes from this pull request and I'm still seeing the same failures, so I imagine that it doesn't have anything to do with this commit.

tests\fields\test_datetime.py

__________________________________________________________________ test_basic ___________________________________________________________________ 

    def test_basic():
        d = datetime(2008, 5, 5, 4, 30, 0, 0)
        # Basic test with both inputs
        form = F(
            DummyPostData(
                a=["2008-05-05", "04:30:00"], b=["2008-05-05 04:30"], c=["5/5/2008 4:30"]
            )
        )
        assert form.a.data == d
        assert (
            form.a()
            == """<input id="a" name="a" type="datetime" value="2008-05-05 04:30:00">"""
        )
        assert form.b.data == d
        assert (
            form.b()
            == """<input id="b" name="b" type="datetime" value="2008-05-05 04:30">"""
        )
        assert form.c.data == d
        assert (
            form.c() == """<input id="c" name="c" type="datetime" value="5/5/2008 4:30">"""
        )
        assert form.validate()

        # Test with a missing input
        form = F(DummyPostData(a=["2008-05-05"]))
        assert not form.validate()
        assert form.a.errors[0] == "Not a valid datetime value."

        form = F(a=d, b=d, c=d)
        assert form.validate()
        assert form.a._value() == "2008-05-05 04:30:00"
        assert form.b._value() == "2008-05-05 04:30"
>       assert form.c._value() == "5/5/2008 4:30"

tests\fields\test_datetime.py:52:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <wtforms.fields.datetime.DateTimeField object at 0x0000018C16BD84D0>

    def _value(self):
        if self.raw_data:
            return " ".join(self.raw_data)
>       return self.data and self.data.strftime(self.format[0]) or ""
E       ValueError: Invalid format string

src\wtforms\fields\datetime.py:36: ValueError

tests\fields\test_datetimelocal.py

__________________________________________________________________ test_basic ___________________________________________________________________ 

    def test_basic():
        d = datetime(2008, 5, 5, 4, 30, 0, 0)
        # Basic test with both inputs
        form = F(
            DummyPostData(
                a=["2008-05-05", "04:30:00"], b=["2008-05-05 04:30"], c=["5/5/2008 4:30"]
            )
        )
        assert form.a.data == d
        assert (
            form.a()
            == '<input id="a" name="a" type="datetime-local" value="2008-05-05 04:30:00">'
        )
        assert form.b.data == d
        assert (
            form.b()
            == '<input id="b" name="b" type="datetime-local" value="2008-05-05 04:30">'
        )
        assert form.c.data == d
        assert (
            form.c()
            == '<input id="c" name="c" type="datetime-local" value="5/5/2008 4:30">'       
        )
        assert form.validate()

        assert form.validate()
        assert form.a._value() == "2008-05-05 04:30:00"
        assert form.b._value() == "2008-05-05 04:30"
>       assert form.c._value() == "5/5/2008 4:30"

tests\fields\test_datetimelocal.py:53:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <wtforms.fields.datetime.DateTimeLocalField object at 0x0000018C16A64250>    

    def _value(self):        if self.raw_data:
            return " ".join(self.raw_data)
>       return self.data and self.data.strftime(self.format[0]) or ""
E       ValueError: Invalid format string

src\wtforms\fields\datetime.py:36: ValueError

@azmeuk azmeuk merged commit 22e766c into pallets-eco:master Jan 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants