FormValidation v0.8.1 is released, supports Bootstrap 4 alpha 3

Validating start and end datetimes

Examples

Some forms requires both start and end dates. The start date must be earlier than the end one. And vice versa, the end date must be later the the start one.

The next sections introduce the solutions for different usages. One is for comparing date, and one for comparing time.

Comparing dates

In order to compare dates, you can use the dynamic options min and max provided by the date validator:

$('#eventForm').formValidation({
    fields: {
        startDate: {
            validators: {
                date: {
                    max: 'endDate'
                    ...
                }
            }
        },
        endDate: {
            validators: {
                date: {
                    min: 'startDate'
                    ...
                }
            }
        }
    }
});

Because the start and end date depend on each other, whenever one of them is changed, we need to revalidate the other if it is not valid:

$('#eventForm')
    .formValidation(...)
    .on('success.field.fv', function(e, data) {
        // The success.field.fv is triggered when a field is valid
        // data.field ---> the field name
        // data.fv    ---> the plugin instance which you can call some APIs on

        if (data.field === 'startDate' && !data.fv.isValidField('endDate')) {
            // We need to revalidate the end date
            data.fv.revalidateField('endDate');
        }

        // Do the same check for the end date
        // ...
    });
You also can use the onSuccess callback as seen in the Validating fields that depend on each other example
The start and end date fields in example use the Bootstrap Datepicker plugin, but you can apply the same techniques in the case they do (or don't) use other date picker plugin
<!-- Include Bootstrap Datepicker -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.3.0/css/datepicker.min.css" />
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.3.0/css/datepicker3.min.css" />

<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.3.0/js/bootstrap-datepicker.min.js"></script>

<style type="text/css">
/**
 * Override feedback icon position
 * See http://formvalidation.io/examples/adjusting-feedback-icon-position/
 */
#eventForm .dateContainer .form-control-feedback {
    top: 0;
    right: -15px;
}
</style>

<form id="eventForm" method="post" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Event</label>
        <div class="col-xs-5">
            <input type="text" class="form-control" name="name" />
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">Start date</label>
        <div class="col-xs-5 dateContainer">
            <div class="input-group input-append date" id="startDatePicker">
                <input type="text" class="form-control" name="startDate" />
                <span class="input-group-addon add-on"><span class="glyphicon glyphicon-calendar"></span></span>
            </div>
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">End date</label>
        <div class="col-xs-5 dateContainer">
            <div class="input-group input-append date" id="endDatePicker">
                <input type="text" class="form-control" name="endDate" />
                <span class="input-group-addon add-on"><span class="glyphicon glyphicon-calendar"></span></span>
            </div>
        </div>
    </div>

    <div class="form-group">
        <div class="col-xs-5 col-xs-offset-3">
            <button type="submit" class="btn btn-default">Validate</button>
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    $('#startDatePicker')
        .datepicker({
            format: 'mm/dd/yyyy'
        })
        .on('changeDate', function(e) {
            // Revalidate the start date field
            $('#eventForm').formValidation('revalidateField', 'startDate');
        });

    $('#endDatePicker')
        .datepicker({
            format: 'mm/dd/yyyy'
        })
        .on('changeDate', function(e) {
            $('#eventForm').formValidation('revalidateField', 'endDate');
        });

    $('#eventForm')
        .formValidation({
            framework: 'bootstrap',
            icon: {
                valid: 'glyphicon glyphicon-ok',
                invalid: 'glyphicon glyphicon-remove',
                validating: 'glyphicon glyphicon-refresh'
            },
            fields: {
                name: {
                    validators: {
                        notEmpty: {
                            message: 'The name is required'
                        }
                    }
                },
                startDate: {
                    validators: {
                        notEmpty: {
                            message: 'The start date is required'
                        },
                        date: {
                            format: 'MM/DD/YYYY',
                            max: 'endDate',
                            message: 'The start date is not a valid'
                        }
                    }
                },
                endDate: {
                    validators: {
                        notEmpty: {
                            message: 'The end date is required'
                        },
                        date: {
                            format: 'MM/DD/YYYY',
                            min: 'startDate',
                            message: 'The end date is not a valid'
                        }
                    }
                }
            }
        })
        .on('success.field.fv', function(e, data) {
            if (data.field === 'startDate' && !data.fv.isValidField('endDate')) {
                // We need to revalidate the end date
                data.fv.revalidateField('endDate');
            }

            if (data.field === 'endDate' && !data.fv.isValidField('startDate')) {
                // We need to revalidate the start date
                data.fv.revalidateField('startDate');
            }
        });
});
</script>

Comparing times

The date validator requires the existence of all year, month and day parts. So it can't be used in this case.

To solve our problem, we can use a combination of regexp and callback validators. The first validator is used to check if the input matches with a pattern of time (HH:mm, for example), and the second one for comparing the time.

In the callback method, we respectively compare the start and end time. If start time is earlier than the end one, we need to set the end time as valid by calling the updateStatus() method:

$('#meetingForm').formValidation({
    fields: {
        startTime: {
            verbose: false,
            validators: {
                callback: {
                    callback: function(value, validator, $field) {
                        // Check if the start is earlier then the end one
                        if (...) {
                            // The end time is also valid
                            // So, we need to update its status
                            validator.updateStatus('endTime', validator.STATUS_VALID, 'callback');
                            return true;
                        }

                        return false;
                    }
                }
            }
        },
        endTime: {
            verbose: false,
            validators: {
                callback: {
                    callback: function(value, validator, $field) {
                        // Do the similar validation logic
                    }
                }
            }
        }
    }
});
The example uses the verbose: false option and place the callback validator at the end of list of validators. It ensures that the time in the callback method already passes other validators, therefore it matches with the time pattern provided by the regexp validator
<form id="meetingForm" method="post" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Start meeting time</label>
        <div class="col-xs-5">
            <input type="text" class="form-control" name="startTime" />
        </div>
    </div>

    <div class="form-group">
        <label class="col-xs-3 control-label">End meeting time</label>
        <div class="col-xs-5">
            <input type="text" class="form-control" name="endTime" />
        </div>
    </div>

    <div class="form-group">
        <div class="col-xs-5 col-xs-offset-3">
            <button type="submit" class="btn btn-default">Validate</button>
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    // The pattern of times that accepts moments between 09:00 to 17:59
    var TIME_PATTERN = /^(09|1[0-7]{1}):[0-5]{1}[0-9]{1}$/;

    $('#meetingForm').formValidation({
        framework: 'bootstrap',
        icon: {
            valid: 'glyphicon glyphicon-ok',
            invalid: 'glyphicon glyphicon-remove',
            validating: 'glyphicon glyphicon-refresh'
        },
        fields: {
            startTime: {
                verbose: false,
                validators: {
                    notEmpty: {
                        message: 'The start time is required'
                    },
                    regexp: {
                        regexp: TIME_PATTERN,
                        message: 'The start time must be between 09:00 and 17:59'
                    },
                    callback: {
                        message: 'The start time must be earlier then the end one',
                        callback: function(value, validator, $field) {
                            var endTime = validator.getFieldElements('endTime').val();
                            if (endTime === '' || !TIME_PATTERN.test(endTime)) {
                                return true;
                            }
                            var startHour    = parseInt(value.split(':')[0], 10),
                                startMinutes = parseInt(value.split(':')[1], 10),
                                endHour      = parseInt(endTime.split(':')[0], 10),
                                endMinutes   = parseInt(endTime.split(':')[1], 10);

                            if (startHour < endHour || (startHour == endHour && startMinutes < endMinutes)) {
                                // The end time is also valid
                                // So, we need to update its status
                                validator.updateStatus('endTime', validator.STATUS_VALID, 'callback');
                                return true;
                            }

                            return false;
                        }
                    }
                }
            },
            endTime: {
                verbose: false,
                validators: {
                    notEmpty: {
                        message: 'The end time is required'
                    },
                    regexp: {
                        regexp: TIME_PATTERN,
                        message: 'The end time must be between 09:00 and 17:59'
                    },
                    callback: {
                        message: 'The end time must be later then the start one',
                        callback: function(value, validator, $field) {
                            var startTime = validator.getFieldElements('startTime').val();
                            if (startTime == '' || !TIME_PATTERN.test(startTime)) {
                                return true;
                            }
                            var startHour    = parseInt(startTime.split(':')[0], 10),
                                startMinutes = parseInt(startTime.split(':')[1], 10),
                                endHour      = parseInt(value.split(':')[0], 10),
                                endMinutes   = parseInt(value.split(':')[1], 10);

                            if (endHour > startHour || (endHour == startHour && endMinutes > startMinutes)) {
                                // The start time is also valid
                                // So, we need to update its status
                                validator.updateStatus('startTime', validator.STATUS_VALID, 'callback');
                                return true;
                            }

                            return false;
                        }
                    }
                }
            }
        }
    });
});
</script>