Зависимые селекторы дат с использованием html выбирает и даты, чтобы иметь смысл

Я пытаюсь создать пользовательский селектор даты начала и окончания, но, к сожалению, из-за дизайна я не смогу использовать jQuery UI Datepicker, поэтому я застрял со старой модой разделения дат в 3 <select>s

чтобы сохранить эту функцию полезной, мы находим это - по крайней мере-сложные части:

  • пусть дни имеют смысл с каждым месяцем (не хочу 31 февраля по выбору,..)
  • установить следующий thay от начала до конца селектор

поэтому я подумал, что лучше делегировать вычисление даты объекту javascript Date (), чтобы, по крайней мере, я мог абстрагировать эту часть.

я почти на месте,

но некоторые, как объект Date () сообщает правильную дату, но оба селектора показывают каждый набор дней для предыдущего (например, лучшие 28 дней происходят в марте вместо февраля)

    $(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);
        });
    })

и здесь это может быть поиграл:

http://jsfiddle.net/96qyH/8/

есть идеи, что мне здесь не хватает?

7 ответов


Edit: Refactored некоторый код, чтобы удалить дополнительную логику

я использовал три функции утилиты для обновления выпадающего списка даты начала и окончания.

  1. daysInMonth - возвращает количество дней в указанном месяце года
  2. adjustDates - обновляет количество дней в раскрывающемся списке на основе выбранного месяца. Примечание: это просто добавит/удалить варианты, а не перестраивать с нуля.
  3. resetDates - сброс на начало месяца и год

я переписал ваш код и попытался достичь той же логики, что и в вашем демо-коде. Проверить мои демо и дайте мне знать, если это работает для вас.

демо:http://jsfiddle.net/ByWhz/1/

Полный Код:

$(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.com/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);
    });
});

первый: manyDays fn должен быть:

var days_in_month = new Date(year, month+1, 0);

потому что в противном случае он возвращается к предыдущему месяцу. Подробнее об этом можно прочитать в комментариях к самому популярному ответу здесь:получить количество дней в указанном месяце с помощью JavaScript?

второй: Я убрал

next_fecha.setMonth(fecha.getMonth() + (dia == dias_mes ? 1 : 0)   );

С

next_fecha.setDate(next_fecha.getDate()+1);

оно переключает месяц автоматически в конце месяц.

третий: эта часть имела dia вместо месяца:

/* 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);
        }

---- добавлено ----

четвертое: он также нуждается в проверке, чтобы увидеть, существует ли 31 февраля. А если нет, то день должен быть последним днем этого месяца. В противном случае объект Date не знал, что делать с датой и давал неверную дату.

просто нужно добавить проверку:

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;

надеюсь, что это решает его, проверьте его здесь: http://jsfiddle.net/96qyH/13/


если требуется исходный код

Я нашел некоторые ответы для вас! Это может тебе помочь! :) Я тоже создал плагин, но вам не нужно, чтобы он был единственным ответом. Итак, вот что вам поможет. Если вы хотите взглянуть на код, вы можете получить исходный код здесь: https://github.com/the-vs-organisation/vdatetimepicker

решение для первого

пусть дни имеют смысл с каждым месяцем (не хочу 31 из фев ..)

для этой проблемы я использовал значение из списка месяца как:

var month = $('#monthval').attr('id');
if(month == "February") {
  // hide the last few list items..
}

и так далее. Вы можете увидеть исходный код в моем плагине. Вы узнаете, как взаимодействовать с другими месяцами.

можно использовать id контейнера в качестве его значения, так как мы не будем использовать select потому что вы не можете стиль . Таким образом, вам потребуется пользовательский созданный datetimepicker.

Disabing любой из элемент в списке не будет хорошей идеей для пользовательского интерфейса, поэтому вам может потребоваться его скрыть. Скрытие его не будет беспокоить UI или самого пользователя. Поскольку предмет не будет доступен, ему не придется беспокоиться об этом.

второй


где Date вызывается как конструктор с более чем одним аргументом, если значения больше, чем их логический диапазон (например, 13 предоставляется как значение месяца или 70 для значения минуты), смежное значение будет будьте настроены. Е. Г. новая дата(2013,13,1) равносильно новому. Дата (2014,1,1), оба создают дату для 2014-02-01. Аналогично другие значения: новая дата (2013,2,1,0,70) эквивалентна новой Дата (2013,2,1,1,10), которые оба создают дату для 2013-03-01T01:10:00.

это из конструктора Note for date в MDN

проблема заключается в следующей функции

var manyDays = function( month, year ){
  var fecha = new Date(year, (month) , 0);
  return fecha.getDate();
}

при использовании 0 в качестве даты вы получаете предыдущий месяц

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

я не знаю, как правильно получить количество дней в месяц в Javascript. Знать хак, чтобы изменить ваш manyDays способ такой.

var manyDays = function( month, year ){
  // Note: month + 1
  var fecha = new Date(year, month+1 , 0);
  return fecha.getDate();
}

вы не нужно сделать следующее

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)  );

его можно заменить на just

next_fecha.setDate(next_fecha.getDate() + 1);

потому что добавление дня в объект date соответствующим образом обновит дату,месяц,год и т. д.

ответ на проблему в комментарии

цикл for обработки месяцев в этом версия есть ошибки. Вы используете неправильную переменную. Изменить dia и tmpdia to next_mes и tmpmes соответственно.

это должно быть так

$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);
}

ниже приведена обновленная версия с исправленной выше проблемой.

http://jsfiddle.net/96qyH/14/


зацените скрипка по мне, Надеюсь, это работает для вас

для 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>

И 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);

        }



    });

вот мое усилие-охватывает все крайние случаи

это надежный хорошо протестированный выбор даты к настоящему времени и отвечает на следующие вопросы:

  • пусть дни имеют смысл с каждым месяцем (не хочу 31 февраля по выбору,..)
  • установите следующий thay от начала до конца селектора

...

но некоторые, как объект Date() сообщает правильную дату, но оба селектора показать каждый набор дней для предыдущего (например, лучшие 28 дней произойдет в марте вместо февраля)

если вы посмотрите ниже, я позабочусь обо всем выше и больше, используя небольшие модульные функции:

  • maxDay() - учитывая месяц и год, он возвращает максимальное количество дней в этом месяце за этот год
  • createAry() - данный минимальный, максимальный и необязательный массив значений возвращает массив от min до max или часть массива значений между min и max индексы
  • дней() - данный мин день, и месяц и год использует maxDay() чтобы получить max day. Использует createAry() чтобы вернуть массив, готовый к использованию для заполнения дня опции
  • месяцев() - заданный минимальный месяц и массив текстовых значений месяца (например, "jan", " feb " и т. д.) использует createAry() чтобы вернуть массив, готовый к использованию для заполнения месяца опции
  • лет() - учитывая мин год использует MAXYEAR постоянные и createAry() чтобы вернуть массив, готовый к использованию для заполнения года опции
  • updateSelectOptions() - учитывая jQuery DOM элемент, массив опционных текстов из день(), (месяц) или год() и выбранный индекс опции, опорожняет затем заполняет ; со значениями массива и выбирает подходящий вариант.
  • updateTos() - обновляет Дата Окончания идем мимо Дата Начала при необходимости и делать мин месяц, день и год расчеты на основе Дата Начала значения. Звонки updateSelectOptions() за месяц, день и год давая jQuery элемент DOM и массивы, возвращаемые (месяц), день() и год() обновить параметры с соответствующими текстовыми значениями и выбор правильной текущей опции.
  • update () - обновления либо начать или закончить дату внутренне с новая дата() используя месяц, день и год из выбранных параметры. Для Дата Начала называет updateSelectOptions() используя дней() и maxDay() чтобы ограничить дни в соответствии с месяцем и годом и выбрать текущую опцию. Звонки updateTos() обновить Дата Окончания и параметры.

jsFiddle:

скрипка

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
}

пожалуйста, посмотрите на эту демонстрацию:http://jsfiddle.net/wared/XvEzz/. Я сожалею, что полностью переписал ваш код, но это был более простой способ для меня выяснить проблему. Приведенный ниже код должен помочь вам решить вашу проблему. Он расположен в строке 24 и выполняется, когда пользователь выбирает месяц. Я добавил несколько комментариев, чтобы объяснить этот процесс. Основная идея состоит в том, чтобы установить день на 1 временно, а затем восстановить последний выбранный день, если это возможно :

// 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());