1import datetime 
    2 
    3from wtforms import widgets 
    4from wtforms.fields.core import Field 
    5from wtforms.utils import clean_datetime_format_for_strptime 
    6 
    7__all__ = ( 
    8    "DateTimeField", 
    9    "DateField", 
    10    "TimeField", 
    11    "MonthField", 
    12    "DateTimeLocalField", 
    13    "WeekField", 
    14) 
    15 
    16 
    17class DateTimeField(Field): 
    18    """ 
    19    A text field which stores a :class:`datetime.datetime` matching one or 
    20    several formats. If ``format`` is a list, any input value matching any 
    21    format will be accepted, and the first format in the list will be used 
    22    to produce HTML values. 
    23    """ 
    24 
    25    widget = widgets.DateTimeInput() 
    26 
    27    def __init__( 
    28        self, label=None, validators=None, format="%Y-%m-%d %H:%M:%S", **kwargs 
    29    ): 
    30        super().__init__(label, validators, **kwargs) 
    31        self.format = format if isinstance(format, list) else [format] 
    32        self.strptime_format = clean_datetime_format_for_strptime(self.format) 
    33 
    34    def _value(self): 
    35        if self.raw_data: 
    36            return " ".join(self.raw_data) 
    37        format = self.format[0] 
    38        return self.data and self.data.strftime(format) or "" 
    39 
    40    def process_formdata(self, valuelist): 
    41        if not valuelist: 
    42            return 
    43 
    44        date_str = " ".join(valuelist) 
    45        for format in self.strptime_format: 
    46            try: 
    47                self.data = datetime.datetime.strptime(date_str, format) 
    48                return 
    49            except ValueError: 
    50                self.data = None 
    51 
    52        raise ValueError(self.gettext("Not a valid datetime value.")) 
    53 
    54 
    55class DateField(DateTimeField): 
    56    """ 
    57    Same as :class:`~wtforms.fields.DateTimeField`, except stores a 
    58    :class:`datetime.date`. 
    59    """ 
    60 
    61    widget = widgets.DateInput() 
    62 
    63    def __init__(self, label=None, validators=None, format="%Y-%m-%d", **kwargs): 
    64        super().__init__(label, validators, format, **kwargs) 
    65 
    66    def process_formdata(self, valuelist): 
    67        if not valuelist: 
    68            return 
    69 
    70        date_str = " ".join(valuelist) 
    71        for format in self.strptime_format: 
    72            try: 
    73                self.data = datetime.datetime.strptime(date_str, format).date() 
    74                return 
    75            except ValueError: 
    76                self.data = None 
    77 
    78        raise ValueError(self.gettext("Not a valid date value.")) 
    79 
    80 
    81class TimeField(DateTimeField): 
    82    """ 
    83    Same as :class:`~wtforms.fields.DateTimeField`, except stores a 
    84    :class:`datetime.time`. 
    85    """ 
    86 
    87    widget = widgets.TimeInput() 
    88 
    89    def __init__(self, label=None, validators=None, format="%H:%M", **kwargs): 
    90        super().__init__(label, validators, format, **kwargs) 
    91 
    92    def process_formdata(self, valuelist): 
    93        if not valuelist: 
    94            return 
    95 
    96        time_str = " ".join(valuelist) 
    97        for format in self.strptime_format: 
    98            try: 
    99                self.data = datetime.datetime.strptime(time_str, format).time() 
    100                return 
    101            except ValueError: 
    102                self.data = None 
    103 
    104        raise ValueError(self.gettext("Not a valid time value.")) 
    105 
    106 
    107class MonthField(DateField): 
    108    """ 
    109    Same as :class:`~wtforms.fields.DateField`, except represents a month, 
    110    stores a :class:`datetime.date` with `day = 1`. 
    111    """ 
    112 
    113    widget = widgets.MonthInput() 
    114 
    115    def __init__(self, label=None, validators=None, format="%Y-%m", **kwargs): 
    116        super().__init__(label, validators, format, **kwargs) 
    117 
    118 
    119class WeekField(DateField): 
    120    """ 
    121    Same as :class:`~wtforms.fields.DateField`, except represents a week, 
    122    stores a :class:`datetime.date` of the monday of the given week. 
    123    """ 
    124 
    125    widget = widgets.WeekInput() 
    126 
    127    def __init__(self, label=None, validators=None, format="%Y-W%W", **kwargs): 
    128        super().__init__(label, validators, format, **kwargs) 
    129 
    130    def process_formdata(self, valuelist): 
    131        if not valuelist: 
    132            return 
    133 
    134        time_str = " ".join(valuelist) 
    135        for format in self.strptime_format: 
    136            try: 
    137                if "%w" not in format: 
    138                    # The '%w' week starting day is needed. This defaults it to monday 
    139                    # like ISO 8601 indicates. 
    140                    self.data = datetime.datetime.strptime( 
    141                        f"{time_str}-1", f"{format}-%w" 
    142                    ).date() 
    143                else: 
    144                    self.data = datetime.datetime.strptime(time_str, format).date() 
    145                return 
    146            except ValueError: 
    147                self.data = None 
    148 
    149        raise ValueError(self.gettext("Not a valid week value.")) 
    150 
    151 
    152class DateTimeLocalField(DateTimeField): 
    153    """ 
    154    Same as :class:`~wtforms.fields.DateTimeField`, but represents an 
    155    ``<input type="datetime-local">``. 
    156    """ 
    157 
    158    widget = widgets.DateTimeLocalInput() 
    159 
    160    def __init__(self, *args, **kwargs): 
    161        kwargs.setdefault( 
    162            "format", 
    163            [ 
    164                "%Y-%m-%d %H:%M:%S", 
    165                "%Y-%m-%dT%H:%M:%S", 
    166                "%Y-%m-%d %H:%M", 
    167                "%Y-%m-%dT%H:%M", 
    168            ], 
    169        ) 
    170        super().__init__(*args, **kwargs)