I Am trying to build a custom START and END date selector, but unfortunatelly because of the design I won't be able to use the jquery UI Datepicker, so I am stucked with the old fashionate of splitting the dates in 3 <select>
s
In order to keep this feature usable, we find this -at least- plicated parts:
- Let days make sense with each month (Dont want 31 of feb selectable , ..)
- Set next thay from START to the END selector
So I thought better to delegate the date calculation to the javascript Date() object so at least I can abstrat that part.
I am almost there,
But some how the Date() object tell the right date, but both selectors show each set of Days for the previous one (For example, top 28 days happen in March instead of FEB)
$(function(){
months = ['jan','feb','mar','apr','may','jun','jul','ago','sep','oct','nov','dec'];
/* Cachear selects */
var $ld = $('select[name=llegada-dia]');
var $lm = $('select[name=llegada-mes]');
var $ly = $('select[name=llegada-ano]');
var $sd = $('select[name=salida-dia]');
var $sm = $('select[name=salida-mes]');
var $sy = $('select[name=salida-ano]');
var manyDays = function( month, year ){
var fecha = new Date(year, (month) , 0);
return fecha.getDate();
}
var paintCals = function( day, month , year ){
if(day == '') day = 1;
if(month == '') month = 0;
if(year == '' ) year = 2013;
//month = month -1;
var fecha = new Date( year, month , day );
var dia = fecha.getDate();
var mes = fecha.getMonth();
var anyo = fecha.getFullYear();
var dias_mes = manyDays( mes,anyo );
/* Generate next date = fecha + 1 */
var next_fecha = fecha;
next_fecha.setDate(next_fecha.getDate() + 1);
next_fecha.setMonth(fecha.getMonth() + (dia == dias_mes ? 1 : 0) );
next_fecha.setFullYear(fecha.getFullYear() + (mes == 12 ? 1 : 0) );
var next_dia = next_fecha.getDate();
var next_mes = next_fecha.getMonth();
var next_anyo = next_fecha.getFullYear();
var next_dias_mes = manyDays( next_mes, next_anyo ) ;
$ld.empty();
for(var tmpdia = 1; tmpdia <= dias_mes; tmpdia++){
var doption = $('<option>').attr( 'value',tmpdia )
.text( tmpdia );
if(dia == tmpdia && dia != ''){
doption.attr('selected', 'selected');
}
$ld.append(doption);
}
/* Actualizar dias salida */
$sd.empty();
for(var tmpdia = next_dia; tmpdia <= next_dias_mes; tmpdia++){
var doption = $('<option>').attr( 'value' , tmpdia )
.text(tmpdia);
if(next_dia == tmpdia && next_dia != ''){
doption.attr('selected', 'selected');
}
$sd.append(doption);
}
/* Actualizar meses salida */
$sm.empty();
for(var tmpmes = next_mes ; tmpmes < 12; tmpmes++){
var doption = $('<option>').attr('value',tmpmes)
.text(months[tmpmes]);
if(dia == tmpdia && dia != ''){
doption.attr('selected', 'selected');
}
$sm.append(doption);
}
/* Actualizar anyos salida */
$sy.empty();
for(var tmpanyo = next_anyo; tmpanyo <= 2020; tmpanyo++){
var doption = $('<option>').attr('value',tmpanyo)
.text(tmpanyo);
if(next_anyo == tmpanyo && next_anyo != ''){
doption.attr('selected', 'selected');
}
$sy.append(doption);
}
}
$('.arrival select').on('change',function(){
var ldia = $ld.val();
var lmes = $lm.val();
var lano = $ly.val();
var ldias = paintCals(ldia,lmes,lano);
});
})
And here it can be fiddled:
/
Any idea what I'm missing here?
I Am trying to build a custom START and END date selector, but unfortunatelly because of the design I won't be able to use the jquery UI Datepicker, so I am stucked with the old fashionate of splitting the dates in 3 <select>
s
In order to keep this feature usable, we find this -at least- plicated parts:
- Let days make sense with each month (Dont want 31 of feb selectable , ..)
- Set next thay from START to the END selector
So I thought better to delegate the date calculation to the javascript Date() object so at least I can abstrat that part.
I am almost there,
But some how the Date() object tell the right date, but both selectors show each set of Days for the previous one (For example, top 28 days happen in March instead of FEB)
$(function(){
months = ['jan','feb','mar','apr','may','jun','jul','ago','sep','oct','nov','dec'];
/* Cachear selects */
var $ld = $('select[name=llegada-dia]');
var $lm = $('select[name=llegada-mes]');
var $ly = $('select[name=llegada-ano]');
var $sd = $('select[name=salida-dia]');
var $sm = $('select[name=salida-mes]');
var $sy = $('select[name=salida-ano]');
var manyDays = function( month, year ){
var fecha = new Date(year, (month) , 0);
return fecha.getDate();
}
var paintCals = function( day, month , year ){
if(day == '') day = 1;
if(month == '') month = 0;
if(year == '' ) year = 2013;
//month = month -1;
var fecha = new Date( year, month , day );
var dia = fecha.getDate();
var mes = fecha.getMonth();
var anyo = fecha.getFullYear();
var dias_mes = manyDays( mes,anyo );
/* Generate next date = fecha + 1 */
var next_fecha = fecha;
next_fecha.setDate(next_fecha.getDate() + 1);
next_fecha.setMonth(fecha.getMonth() + (dia == dias_mes ? 1 : 0) );
next_fecha.setFullYear(fecha.getFullYear() + (mes == 12 ? 1 : 0) );
var next_dia = next_fecha.getDate();
var next_mes = next_fecha.getMonth();
var next_anyo = next_fecha.getFullYear();
var next_dias_mes = manyDays( next_mes, next_anyo ) ;
$ld.empty();
for(var tmpdia = 1; tmpdia <= dias_mes; tmpdia++){
var doption = $('<option>').attr( 'value',tmpdia )
.text( tmpdia );
if(dia == tmpdia && dia != ''){
doption.attr('selected', 'selected');
}
$ld.append(doption);
}
/* Actualizar dias salida */
$sd.empty();
for(var tmpdia = next_dia; tmpdia <= next_dias_mes; tmpdia++){
var doption = $('<option>').attr( 'value' , tmpdia )
.text(tmpdia);
if(next_dia == tmpdia && next_dia != ''){
doption.attr('selected', 'selected');
}
$sd.append(doption);
}
/* Actualizar meses salida */
$sm.empty();
for(var tmpmes = next_mes ; tmpmes < 12; tmpmes++){
var doption = $('<option>').attr('value',tmpmes)
.text(months[tmpmes]);
if(dia == tmpdia && dia != ''){
doption.attr('selected', 'selected');
}
$sm.append(doption);
}
/* Actualizar anyos salida */
$sy.empty();
for(var tmpanyo = next_anyo; tmpanyo <= 2020; tmpanyo++){
var doption = $('<option>').attr('value',tmpanyo)
.text(tmpanyo);
if(next_anyo == tmpanyo && next_anyo != ''){
doption.attr('selected', 'selected');
}
$sy.append(doption);
}
}
$('.arrival select').on('change',function(){
var ldia = $ld.val();
var lmes = $lm.val();
var lano = $ly.val();
var ldias = paintCals(ldia,lmes,lano);
});
})
And here it can be fiddled:
http://jsfiddle/96qyH/8/
Any idea what I'm missing here?
Share Improve this question edited Oct 17, 2013 at 18:49 Toni Michel Caubet asked Oct 17, 2013 at 18:20 Toni Michel CaubetToni Michel Caubet 20.2k58 gold badges217 silver badges387 bronze badges 8- I know the problem is that I'm starting/getting with 1 instead of 0 or the other way somewhere in the code.. Any good eyes around? I'm not able to see it.. – Toni Michel Caubet Commented Oct 17, 2013 at 20:37
- If the jquery UI Datepicker is what you would like why don't you tweak it to your needs? – Jonathan Commented Oct 21, 2013 at 22:19
- I will start working on a project for this thing, I will post it in some time! :) You can have the code for that..wait.. – Afzaal Ahmad Zeeshan Commented Oct 22, 2013 at 19:34
- You may have the bounty for 5 days ^^ @AfzaalAhmadZeeshan – Toni Michel Caubet Commented Oct 22, 2013 at 19:50
- @Jonathan I tried a little bit at first, but I just couldn't see how can it help :? – Toni Michel Caubet Commented Oct 22, 2013 at 19:50
7 Answers
Reset to default 2First: manyDays fn needs to be:
var days_in_month = new Date(year, month+1, 0);
Because otherwise it goes back to previous month. You can read more about it in the ments of the most popular answer here: Get number days in a specified month using javascript?
Second: I removed
next_fecha.setMonth(fecha.getMonth() + (dia == dias_mes ? 1 : 0) );
Since you already have
next_fecha.setDate(next_fecha.getDate()+1);
It switches month automatically in the end of month.
Third: this part had dia instead of month:
/* Actualizar meses salida */
$sm.empty();
for(var tmpmes = next_mes ; tmpmes < 12; tmpmes++){
var doption = $('<option>').attr('value',tmpmes).text(months[tmpmes]);
if(next_mes == tmpmes && next_mes != ''){
console.log('fired');
doption.attr('selected', 'selected');
}
$sm.append(doption);
}
---- ADDED ----
Fourth: It also needs a check to see if Feb 31 exists. And if it is not, day should be the last day of that month. Otherwise the Date object didn't know what to do with the date and was giving wrong date.
Just needs to add a check:
var check_fecha = new Date( year, month );
check_mes = check_fecha.getMonth();
check_anyo = check_fecha.getFullYear();
var check_dias_mes = manyDays( check_mes, check_anyo );
if(day > check_dias_mes)
day = check_dias_mes;
Hope it solves it, check it out here: http://jsfiddle/96qyH/13/
If source code is required
I have found some answers for you! That might help you out! :) I have created a plugin too, but you won't need it to be the only answer. So here is what would help you. If you want to have a look at the code, you can get the source code here: https://github./the-vs-organisation/vdatetimepicker
Solution to the first one
Let days make sense with each month (Dont want 31 of feb selectable , ..)
For this issue, I used the value from the list of month as:
var month = $('#monthval').attr('id');
if(month == "February") {
// hide the last few list items..
}
And then so on. You can see the source code in my plugin. You will find out how to interact with the other months too.
You can use id
of the container as its value, as we won't use select
Because you cannot style the style
attribute. So you will require a custom created datetimepicker.
Disabing any of the item in the list won't be a good idea for the UI, so you might need to hide it. Hiding it won't bother UI or user himself. As the item won't be available he won't have to worry about the thing.
Second one
I am not sure what you wanted in this one, so I will leave it instead of giving a vague answer.
My Assumptions
If you want to get the time remaining or if you want to pare them, then you can use this:
var time = "some_string_of_time";
var curTime = new Date();
// now time to get the seconds between it..
var seconds = Math.floor((curTime - Time) / (1000));
// now write them..
if (seconds > 0 && seconds < 60) {// seconds..
$('element') = seconds + " seconds ago";
}
Hiding the values
But some how the Date() object tell the right date, but both selectors show each set of Days for the previous one (For example, top 28 days happen in March instead of FEB)
This can be minimized in select
as well as in my code. All you need to use is a simple class or id and on the click event for the month selector. Hide the excessive list items if any. Otherwise show. In my plugin, January and December will always have 31 days, Feb will have 28. It all depends on the list item selected in the Month dropdown. You can update the code in other div using jQuery.
If you don't care about UI
If you don't want to style the UI, have you tried using the input tool?
<input type="date" name="datetime" />
This is a browser generated one. You can always use it when nothing else gets into handy.
How to access data from my plugin.
You can always try to get the data from the .vdtpckr
once you have the fields filled by using their ID for example:
var month = $('.vmonthval').attr('id');
This way, you can get the values from this.
If you need any thing more, ment below! :) Also try to check out the source code from github. You will get many ideas from the code.
Edit: Refactored some code to remove extra logic
I have used three utility function to update the start and end date drop down.
daysInMonth
- gets the number of days in a specified month of a yearadjustDates
- updates the number of days in the drop down based on the selected month. Note: This simply appends/remove options instead of rebuilding from scratch.resetDates
- Reset the start month and year
I rewrote your code and tried to achieve the same logic you had in your demo code. Check out my demo and let me know if it works for you.
DEMO: http://jsfiddle/ByWhz/1/
Full Code:
$(function () {
months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dec'];
/* Cachear selects */
var $ld = $('select[name=llegada-dia]');
var $lm = $('select[name=llegada-mes]');
var $ly = $('select[name=llegada-ano]');
var $sd = $('select[name=salida-dia]');
var $sm = $('select[name=salida-mes]');
var $sy = $('select[name=salida-ano]');
//http://stackoverflow./a/1184359/297641
function daysInMonth(month, year) {
return new Date(year, month, 0).getDate();
}
function adjustDates(selMonthDates, $sel) {
var $options = $sel.find('option');
var dates = $options.length;
//append/remove missing dates
if (dates > selMonthDates) { //remove
$options.slice(selMonthDates).remove();
} else { //append
var dateOptions = [];
for (var date = dates + 1; date <= selMonthDates; date++) {
dateOptions.push('<option value="' + date + '">' + date + '</option>');
}
$sel.append(dateOptions.join('')); //reduces DOM call
}
}
function resetDates() {
$lm.val(function (i, v) {
return (v == '') ? '0' : v;
});
$ly.val(function (i, v) {
return (v == '') ? '2013' : v;
});
}
var paintCals = function (day, month, year) {
resetDates();
//adjust start date
var selMonthDates = daysInMonth((parseInt($lm.val(), 10) + 1), $ly.val());
adjustDates(selMonthDates, $ld);
//If current day selection > number of days in selected month then set the day to max allowed day
if (day > selMonthDates) {
day = selMonthDates;
$ld.val(day); //update selection
}
//selected start date
var selectedDate = new Date(year, month, day);
//next day from start date
var nextDay = new Date(selectedDate.getTime() + 86400000);
//lets build the end year drop down
var tmpArr = [];
for (var yrs = parseInt(nextDay.getFullYear(), 10); yrs <= 2020; yrs++) {
tmpArr.push('<option value="' + yrs + '">' + yrs + '</option>');
}
$sy.empty().append(tmpArr.join('')); //set the YEARS
//simply set the month
$sm.val(nextDay.getMonth()); //set the month
//adjust end date
selMonthDates = daysInMonth(parseInt(nextDay.getMonth(), 10) + 1, nextDay.getFullYear());
adjustDates(selMonthDates, $sd);
$sd.val(nextDay.getDate()); //set the date
$('#log').empty().append('Fecha: ' + selectedDate).append('<br>');
$('#log').append('Siguiente: ' + nextDay);
}
$('.arrival select').on('change', function () {
var ldia = $ld.val();
var lmes = $lm.val();
var lano = $ly.val();
//console.log('lD/lM/lY:'+ldia,lmes,lano);
var ldias = paintCals(ldia, lmes, lano);
});
});
Where Date is called as a constructor with more than one argument, if values are greater than their logical range (e.g. 13 is provided as the month value or 70 for the minute value), the adjacent value will be adjusted. E.g. new Date(2013,13,1) is equivalent to new Date(2014,1,1), both create a date for 2014-02-01. Similarly for other values: new Date(2013,2,1,0,70) is equivalent to new Date(2013,2,1,1,10) which both create a date for 2013-03-01T01:10:00.
That's from the Note for date constructor at MDN
The issue is with the following function
var manyDays = function( month, year ){
var fecha = new Date(year, (month) , 0);
return fecha.getDate();
}
When you use 0 as the date you get the previous month
for example
var date1 = new Date(2013,2,0)
console.log(date1.getMonth()); // will print 1 = Feb
var date2 = new Date(2013,2,1)
console.log(date2.getMonth()); // will print 2 = March
var date3 = new Date(2013,2,32)
console.log(date3.getMonth()); // will print 3 = April
I don't know of any proper way to get the number of days of a month in Javascript. A know hack is to modify your manyDays
method like this.
var manyDays = function( month, year ){
// Note: month + 1
var fecha = new Date(year, month+1 , 0);
return fecha.getDate();
}
You dont need to do the following
next_fecha.setDate(next_fecha.getDate() + 1);
next_fecha.setMonth(fecha.getMonth() + (dia == dias_mes ? 1 : 0) );
next_fecha.setFullYear(fecha.getFullYear() + (mes == 12 ? 1 : 0) );
It can be replace with just
next_fecha.setDate(next_fecha.getDate() + 1);
Because adding a day to the date object will update date,month,year etc appropriately.
Answer to the problem in ment
The for loop handling the months in this version has an error. You are using the wrong variable. Change dia
and tmpdia
to next_mes
and tmpmes
respectively.
it should be like this
$sm.empty();
for(var tmpmes = next_mes ; tmpmes < 12; tmpmes++){
var doption = $('<option>').attr('value',tmpmes).text(months[tmpmes]);
if(next_mes == tmpmes && next_mes != ''){
doption.attr('selected', 'selected');
}
$sm.append(doption);
}
The following is the updated version with the above problem fixed.
http://jsfiddle/96qyH/14/
check out this fiddle by me, hope this works for you
For HTML
<form name="myForm" id="myForm">
<fieldset id='dbOne'>
<legend>Start Date</legend>
<label for="yearOne">Year</label>
<select name="yearOne" id="yearOne" title="Year" class='year'></select>
<label for="monthOne">Month</label>
<select name="monthOne" id="monthOne" title="Month" class='month'></select>
<label for="dayOne">Day</label>
<select name="dayOne" id="dayOne" title="Day" class='day'></select>
</fieldset>
<fieldset id='dbTwo'>
<legend>End Date</legend>
<label for="yearTwo">Year</label>
<select name="yearTwo" id="yearTwo" title="Year" class='year'></select>
<label for="monthTwo">Month</label>
<select name="monthTwo" id="monthTwo" title="Month" class='month'></select>
<label for="dayTwo">Day</label>
<select name="dayTwo" id="dayTwo" title="Day" class='day'></select>
</fieldset>
</form>
AND JS
$(function () {
// get days in month
function daysInMonth(month, year) {
month = parseInt(month,10)+1;
month=month.toString();
return new Date(year, month, 0).getDate();
}
//creates an array of number, a = array size, b starting num
var numberArray = function (a, b) {
c = [];
for (var i = 0; i < a; i++) {
c[i] = i + b;
}
return c;
};
//generates numeric drop down
function createOptions(parent, options) {
var l = options.length;
for (var i = 0; i < l; i++) {
var val = options[i];
var text = options[i];
var node = document.createElement("option");
node.textContent = text;
node.value = val;
if(i===0) node.selected='selected';
parent.appendChild(node);
}
}
//generates drop down with numeric value string text
function getOptionFromMap(parent, map) {
for (var i = 0; i < map.length; i++) {
var x = map[i];
var val = x.key;
var text = x.val;
var node = document.createElement("option");
node.textContent = text;
node.value = val;
if(i===0) node.selected='selected';
parent.appendChild(node);
}
}
var years = numberArray(20, 2000);
var days = numberArray(31, 1);
var months = [{
key: 00,
val: "jan"
}, {
key: 01,
val: "feb"
}, {
key: 02,
val: "mar"
}, {
key: 03,
val: "apr"
}, {
key: 04,
val: "may"
}, {
key: 05,
val: "jun"
}, {
key: 06,
val: "jul"
}, {
key: 07,
val: "aug"
}, {
key: 08,
val: "sep"
}, {
key: 09,
val: "oct"
}, {
key: 10,
val: "nov"
}, {
key: 11,
val: "dec"
}];
createOptions(document.getElementById('dayOne'), days);
createOptions(document.getElementById('dayTwo'), days);
createOptions(document.getElementById('yearOne'), years);
createOptions(document.getElementById('yearTwo'), years);
getOptionFromMap(document.getElementById('monthOne'), months);
getOptionFromMap(document.getElementById('monthTwo'), months);
$(".year,.month").bind({
change:function(){
var dInMonth = daysInMonth($('.month',$(this).parent()).val(), $('.year',$(this).parent()).val());
$('.day',$(this).parent()).children().each(function(){
var cEle = $(this);
var cValue = parseInt(cEle.html(),10);
if(!cEle.is(':disabled') && (cValue>dInMonth)) {
cEle.attr({disabled:true});
} else if(cEle.is(':disabled') && (cValue<=dInMonth)) {
cEle.attr({disabled:false});
}
});
}
});
$('select',$('#dbOne,#dbTwo')).bind({
change:function(){
var dBoxOne = $('#dbOne');
var dBoxtwo = $('#dbTwo');
var dtOne = getDateString(dBoxOne);
var dOne = new Date(dtOne.y,dtOne.m,dtOne.d);
var dtTwo = getDateString(dBoxtwo);
var dTwo = new Date(dtTwo.y,dtTwo.m,dtTwo.d);
if(dOne>dTwo) {
var nextDay = parseInt(dtOne.d,10)+1;
//alert('a');
dTwo = new Date(dtOne.y,dtOne.m,nextDay.toString());
setDate(dBoxtwo,dTwo);
} else {
}
}
});
function getDateString(dateBox){
var year = $('.year',dateBox).val();
var month = $('.month',dateBox).val();
var day = $('.day',dateBox).val();
var da = year+"/"+month+"/"+day;
var d = {y:year,m:month,d:day};
return d;
}
function setDate(dateBox,dateObj){
var year = dateObj.getFullYear();
var month = dateObj.getMonth();
var day = dateObj.getDate();
$('.year',dateBox).val(year);
$('.month',dateBox).val(month);
$('.day',dateBox).val(day);
}
});
Here is my effort - Covers all Edge Cases
It is a robust well-tested date picker by now and answers the following questions:
- Let days make sense with each month (Dont want 31 of feb selectable , ..)
- Set next thay from START to the END selector
...
But some how the Date() object tell the right date, but both selectors show each set of Days for the previous one (For example, top 28 days happen in March instead of FEB)
If you look below I take care of all the above and more using small modular functions:
- maxDay() - given a month and year, it returns the max numbers of days in that month for that year
- createAry() - given min, max and optional array of values returns an array from min to max or portion of values array between min and max indices
- days() - given min day and month and year uses maxDay() to get max day. Uses createAry() to return an array ready to use to populate day <select> options
- months() - given min month and an array of month text values (like 'jan', 'feb', etc.) uses createAry() to return an array ready to use to populate month <select> options
- years() - given min year uses MAXYEAR constant and createAry() to return an array ready to use to populate year <select> options
- updateSelectOptions() - given a jQuery DOM <select> element, an array of option texts from day(), month() or year() and selected option index, empties then fills <select>; with array values and selects appropriate option.
- updateTos() - updates the End Date rolling past Start Date if necessary and doing min month, day and year calculations based on Start Date values. Calls updateSelectOptions() for month, day and year giving jQuery <select> DOM element and arrays returned by month(), day() and year() to update <select> options with appropriate text values and selecting correct current option.
- update() - updates either start or end date internally with new Date() using month, day and year from selected <select> options. For Start Date, calls updateSelectOptions() using days() and maxDay() to limit days according to month and year and select current option. Calls updateTos() to update End Date and its <select> options.
jsFiddle:
FIDDLE
HTML:
<form>
<fieldset class="dateField">
<legend>Start Date</legend>
<label for="fromMonth">Month</label>
<select id="fromMonth" class='monthSelect'></select>
<label for="fromDay">Day</label>
<select id="fromDay" class='daySelect'></select>
<label for="fromYear">Year</label>
<select id="fromYear" class='yearSelect'></select>
</fieldset>
<fieldset class="dateField">
<legend>End Date</legend>
<label for="toMonth">Month</label>
<select id="toMonth" class='monthSelect'></select>
<label for="toDay">Day</label>
<select id="toDay" class='daySelect'></select>
<label for="toYear">Year</label>
<select id="toYear" class='yearSelect'></select>
</fieldset>
</form>
<input type="button" id="reset" class="resetButton" value="Reset" />
JavaScript:
'use strict';
var fromDay = $('#fromDay'),
fromMonth = $('#fromMonth'),
fromYear = $('#fromYear'),
toDay = $('#toDay'),
toMonth = $('#toMonth'),
toYear = $('#toYear'),
reset = $('#reset'),
CURDATE = new Date(),
curFromDate,
curToDate,
MINYEAR = 2000,
NUMYEARS = 40,
MAXYEAR = MINYEAR + NUMYEARS - 1,
MAXDATE = new Date(MAXYEAR, 11, 31),
MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
function maxDay(params) { // {month, year}
params.month = params.month || 0;
params.year = params.year || MINYEAR;
return new Date(params.year, (params.month - 0) + 1, 0).getDate();
}
function createAry(params) { // { min, max, values }
var i,
ary = [];
params.values = params.values || [];
if (params.values.length !== 0) {
return params.values.slice(params.min, params.max + 1);
}
for (i = 0; i < params.max - params.min + 1; i++) {
ary[i] = params.min + i;
}
return ary;
}
function days(params) { // {minDay, month, year}
var max;
params.month = params.month || 0;
params.year = params.year || MINYEAR;
max = maxDay({
month: params.month,
year: params.year
});
params.minDay = params.minDay || 1;
return createAry({
min: params.minDay,
max: max
});
}
function months(params) { // {minMonth, months}
params.minMonth = params.minMonth || 0;
return createAry({
min: params.minMonth,
max: 11,
values: MONTHS
})
}
function years(params) { // {minYear}
params.minYear = params.minYear || MINYEAR;
return createAry({
min: params.minYear,
max: MAXYEAR
});
}
function updateSelectOptions(params) { // {select, options, current]
params.select.empty();
params.options.forEach(function (e, i) {
params.select.append($('<option></option>').prop("selected", i === params.current).text(e));
});
}
function updateTos() {
var minDay = 1,
minMonth = 0,
minYear = curFromDate.getFullYear();
if (curToDate <= curFromDate) {
curToDate = new Date(curFromDate.getFullYear(), curFromDate.getMonth(), curFromDate.getDate() + 1);
minYear = curToDate.getFullYear();
if (minYear === curFromDate.getFullYear()) {
minMonth = curToDate.getMonth();
}
if (curFromDate.getMonth() === curToDate.getMonth()) {
minDay = curToDate.getDate();
}
} else if (curFromDate.getFullYear() === curToDate.getFullYear()) {
minMonth = curFromDate.getMonth();
if (curFromDate.getMonth() === curToDate.getMonth()) {
minDay = curFromDate.getDate() + 1;
}
} else if (curFromDate.getDate() === 31 && curFromDate.getMonth() === 11) {
minYear++;
}
updateSelectOptions({
select: toDay,
options: days({
minDay: minDay,
month: curToDate.getMonth(),
year: curToDate.getFullYear()
}),
current: curToDate.getDate() - minDay
});
updateSelectOptions({
select: toMonth,
options: months({
minMonth: minMonth,
months: MONTHS
}),
current: curToDate.getMonth() - minMonth
});
updateSelectOptions({
select: toYear,
options: years({
minYear: minYear
}),
current: curToDate.getFullYear() - minYear
});
}
function update(params) { // {toOrFrom}
var day,
month,
year,
max,
date;
if (params.toOrFrom === 'from') {
day = fromDay.find("option:selected").text();
month = MONTHS.indexOf(fromMonth.find("option:selected").text());
year = fromYear.find("option:selected").text();
} else {
day = toDay.find("option:selected").text();
month = MONTHS.indexOf(toMonth.find("option:selected").text());
year = toYear.find("option:selected").text();
}
max = maxDay({
month: month,
year: year
});
if (day > max) {
day = max;
}
date = new Date(year, month, day);
if (params.toOrFrom === 'from') {
if (date >= MAXDATE) {
alert('The date you entered is later than the latest allowed date. Please enter a different date.');
fromDay.prop("selectedIndex", curFromDate.getDate() - 1);
fromMonth.prop("selectedIndex", curFromDate.getMonth());
fromYear.prop("selectedIndex", curFromDate.getFullYear() - MINYEAR);
return;
}
curFromDate = date;
updateSelectOptions({
select: fromDay,
options: days({
minDay: 1,
month: month,
year: year
}),
current: day - 1
});
} else {
curToDate = date;
}
updateTos();
}
function onFromChange() {
update({
toOrFrom: 'from'
});
}
function onToChange() {
update({
toOrFrom: 'to'
});
}
function init() {
curFromDate = new Date(CURDATE.getFullYear(), CURDATE.getMonth(), CURDATE.getDate());
curToDate = new Date(CURDATE.getFullYear(), CURDATE.getMonth(), CURDATE.getDate());
updateSelectOptions({
select: fromDay,
options: days({
minDay: 1,
month: curFromDate.getMonth(),
year: curFromDate.getFullYear()
}),
current: curFromDate.getDate() - 1
});
updateSelectOptions({
select: fromMonth,
options: months({
months: MONTHS
}),
current: curFromDate.getMonth()
});
updateSelectOptions({
select: fromYear,
options: years({
minYear: MINYEAR
}),
current: curFromDate.getFullYear() - MINYEAR
});
updateTos();
}
fromDay.change(onFromChange);
fromMonth.change(onFromChange);
fromYear.change(onFromChange);
toDay.change(onToChange);
toMonth.change(onToChange);
toYear.change(onToChange);
reset.click(init);
init();
CSS:
fieldset.dateField {
width: 375px;
}
select.daySelect {
width: 45px;
margin: 5px 10px 0 0;
}
select.monthSelect {
width: 55px;
margin: 5px 10px 0 10px;
}
select.yearSelect {
width: 65px;
margin: 5px 10px 0 0;
}
input.resetButton {
margin: 10px 20px 0 0
}
Please have a look at this demo : http://jsfiddle/wared/XvEzz/. I'm sorry for having rewritten your code entirely but that was an easier way for me to figure out the issue. The code below should help you to resolve your problem. It's located at line 24 and executed when the user chooses a month. I've added some ments here in order to explain the process. The basic idea is to set the day to 1 temporarily, then to restore last selected day if possible :
// saves last selected day
day = date.getDate();
// sets day to 1 in order to prevent the month from changing
date.setDate(1);
// sets selected month
date.setMonth(this.selectedIndex);
// retrieves number of days for this month
days = getDays(date);
// restores last selected day if included in this month
if (day <= days) date.setDate(day);
// refreshes days options
feedCombo(0, getRange(1, days));
// refreshes selected day
$select.eq(idx - 1).val(date.getDate());