Validating start and end datetimes
← ejemplos
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
// ...
});
<!-- 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/ejemplos/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
}
}
}
}
}
});
<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>