Source code for lback.forms.fields_datetime

import logging
from typing import Any, Optional, List
from datetime import date, datetime, time

from .widgets_datetime import DateInput, TimeInput, DateTimeInput
from .validation import ValidationError
from .fields import Field

logger = logging.getLogger(__name__)

[docs] class DateField(Field): """ A field that handles date input. Validates that the input can be converted to a Python date object. """ widget = DateInput
[docs] def __init__( self, input_formats: Optional[List[str]] = None, empty_value: Optional[date] = None, **kwargs: Any ): """ Initializes a DateField. Args: input_formats: A list of date format strings to try when parsing the input. Defaults to common formats (e.g., '%Y-%m-%d'). empty_value: The cleaned value to return if the input is empty and not required. Defaults to None. **kwargs: Additional arguments for the base Field class. """ self.input_formats = input_formats if input_formats is not None else [ '%Y-%m-%d', '%m/%d/%Y', '%d/%m/%Y', '%Y/%m/%d', '%m-%d-%Y', '%d-%m-%Y', ] self.empty_value = empty_value super().__init__(**kwargs) self.error_messages.update({ 'invalid': 'Enter a valid date.', 'invalid_format': 'Enter a valid date in one of the formats: %(formats)s.', })
[docs] def to_python(self, value: Any) -> Optional[date]: """ Converts the input value to a Python date object and performs validation. """ if value is None or (isinstance(value, str) and value.strip() == ''): return self.empty_value if not self.required else None if isinstance(value, (date, datetime)): return value.date() if isinstance(value, datetime) else value if not isinstance(value, str): raise ValidationError(self.error_messages['invalid'], code='invalid') for fmt in self.input_formats: try: cleaned_value = datetime.strptime(value.strip(), fmt).date() logger.debug(f"DateField: Successfully parsed date '{value}' using format '{fmt}'.") return cleaned_value except (ValueError, TypeError): continue format_list_str = ", ".join([f"'{fmt}'" for fmt in self.input_formats]) raise ValidationError( self.error_messages['invalid_format'], code='invalid_date_format', params={'formats': format_list_str} )
[docs] class TimeField(Field): """ A field that handles time input. Validates that the input can be converted to a Python time object. """ widget = TimeInput
[docs] def __init__( self, input_formats: Optional[List[str]] = None, empty_value: Optional[time] = None, **kwargs: Any ): """ Initializes a TimeField. Args: input_formats: A list of time format strings to try when parsing the input. Defaults to common formats (e.g., '%H:%M', '%H:%M:%S'). empty_value: The cleaned value to return if the input is empty and not required. Defaults to None. **kwargs: Additional arguments for the base Field class. """ self.input_formats = input_formats if input_formats is not None else [ '%H:%M:%S', '%H:%M', '%I:%M:%S %p', '%I:%M %p', ] self.empty_value = empty_value super().__init__(**kwargs) self.error_messages.update({ 'invalid': 'Enter a valid time.', 'invalid_format': 'Enter a valid time in one of the formats: %(formats)s.', })
[docs] def to_python(self, value: Any) -> Optional[time]: """ Converts the input value to a Python time object and performs validation. """ if value is None or (isinstance(value, str) and value.strip() == ''): return self.empty_value if not self.required else None if isinstance(value, (time, datetime)): return value.time() if isinstance(value, datetime) else value if not isinstance(value, str): raise ValidationError(self.error_messages['invalid'], code='invalid') for fmt in self.input_formats: try: cleaned_value = datetime.strptime(value.strip(), fmt).time() logger.debug(f"TimeField: Successfully parsed time '{value}' using format '{fmt}'.") return cleaned_value except (ValueError, TypeError): continue format_list_str = ", ".join([f"'{fmt}'" for fmt in self.input_formats]) raise ValidationError( self.error_messages['invalid_format'], code='invalid_time_format', params={'formats': format_list_str} )
[docs] class DateTimeField(Field): """ A field that handles datetime input. Validates that the input can be converted to a Python datetime object. """ widget = DateTimeInput
[docs] def __init__( self, input_formats: Optional[List[str]] = None, empty_value: Optional[datetime] = None, **kwargs: Any ): """ Initializes a DateTimeField. Args: input_formats: A list of datetime format strings to try when parsing the input. Defaults to common formats (e.g., '%Y-%m-%dT%H:%M'). empty_value: The cleaned value to return if the input is empty and not required. Defaults to None. **kwargs: Additional arguments for the base Field class. """ self.input_formats = input_formats if input_formats is not None else [ '%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M', '%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', '%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M', ] self.empty_value = empty_value super().__init__(**kwargs) self.error_messages.update({ 'invalid': 'Enter a valid date and time.', 'invalid_format': 'Enter a valid date and time in one of the formats: %(formats)s.', })
[docs] def to_python(self, value: Any) -> Optional[datetime]: """ Converts the input value to a Python datetime object and performs validation. """ if value is None or (isinstance(value, str) and value.strip() == ''): return self.empty_value if not self.required else None if isinstance(value, datetime): return value if not isinstance(value, str): raise ValidationError(self.error_messages['invalid'], code='invalid') for fmt in self.input_formats: try: cleaned_value = datetime.strptime(value.strip(), fmt) logger.debug(f"DateTimeField: Successfully parsed datetime '{value}' using format '{fmt}'.") return cleaned_value except (ValueError, TypeError): continue format_list_str = ", ".join([f"'{fmt}'" for fmt in self.input_formats]) raise ValidationError( self.error_messages['invalid_format'], code='invalid_datetime_format', params={'formats': format_list_str} )