user8039194
user8039194

Reputation:

Don't submit time past 11:59 pm

I have a form that creates a lesson with a date and time. I currently have validators to ensure that past dates can't be used, which are working perfectly. However, I am having trouble visualising how a validator that makes sure the time entered isn't past 11:59 pm would work. I inlucded a snippet of what I am trying to achieve (I know it doesn't work the way it is layed out, it is just there to provide context). I would appreaciate any help with this.

forms.py

def validate_date1(value):
    if value < timezone.now():
        raise ValidationError('Date cannot be in the past')

def validate_date2(value):
    if value < timezone.now():
        raise ValidationError('Date cannot be in the past')

def present_date1(value):
    if value > '11:59 pm':
        raise ValidationError('Time cannot be past 11:59 pm')

def present_date2(value):
    if value > '11:59 pm':
        raise ValidationError('Time cannot be past 11:59 pm')

class LessonForm(forms.ModelForm):
    lesson_instrument = forms.ChoiceField(choices=instrument_list, widget=forms.Select(attrs={'class' : 'form-control', 'required' : 'True'}))
    lesson_datetime_start = forms.DateTimeField(input_formats=['%Y-%m-%d %I:%M %p'], widget=forms.DateTimeInput(attrs={'class': 'form-control', 'placeholder':'YYYY-MM-DD Hour:Minute am/pm'}), validators=[validate_date1, present_date1])
    lesson_datetime_end = forms.DateTimeField(input_formats=['%Y-%m-%d %I:%M %p'], required=False, widget=forms.DateTimeInput(attrs={'class': 'form-control', 'placeholder':'YYYY-MM-DD Hour:Minute am/pm'}), validators=[validate_date2, present_date2])
    lesson_weekly = forms.BooleanField(required=False)

Upvotes: 1

Views: 150

Answers (3)

dirkgroten
dirkgroten

Reputation: 20692

You want to validate lesson_datetime_start and lesson_datetime_end together, not separately. Just checking that the time isn't greater than 11:59pm doesn't cut it, since that would make 2019-05-04 11:00pm - 2019-05-05 12:00am invalid even though it's a correct one hour interval starting at 11pm.

To do that, add a clean() method to your form:

def clean(self):
    cleaned_data = super().clean()
    if self.cleaned_data.get('lesson_datetime_start') \
            and self.cleaned_data.get('lesson_datetime_end') \
            and self.cleaned_data['lesson_datetime_start'] >= self.cleaned_data['lesson_datetime_end']:
        raise ValidationError({'lesson_datetime_end': "End time must be later than start time."})
    return cleaned_data

In the same way, you could add a validator that the duration of the lesson isn't greater than a certain expected time interval (e.g. cannot be longer than 4 hours), by subtracting the two datetime fields and comparing them to datetime.timedelta(hours=x).

You can also do it in your model, so assuming you have a Lesson model with fields lesson_start and lesson_end:

def clean(self):
    if self.lesson_start and self.lesson_end and self.lesson_start >= self.lesson_end:
        raise ValidationError({'lesson_end': "End time must be later than start time."})

Upvotes: 0

Matias Cicero
Matias Cicero

Reputation: 26281

So, the input date cannot be in the past and it cannot be after 23:59, so basically it needs to be within the rest of the present day.

How about:

import pytz

def date_is_not_past(dt):
    if dt < datetime.now(pytz.UTC):
        raise ValidationError('Date cannot be in the past')

def date_is_today(dt):
    if dt.date() != datetime.now(pytz.UTC).date():
        raise ValidationError('Date needs to be today')

Upvotes: 1

AKX
AKX

Reputation: 169051

The validators for a DateTimeField will get a datetime.datetime object, not a string.

Here we extract the time component out of the datetime and compare it to our constant last possible time.

import datetime

LAST_POSSIBLE_TIME = datetime.time(23, 59)

def validate_time(value):
    if value.time() > LAST_POSSIBLE_TIME:
        raise ValidationError('Time cannot be past 11:59 pm')

Upvotes: 2

Related Questions