Pull to refresh

Comments 37

отлично, спасибо!)
но я искал крохотный способ именно для описанной задачи.
Как показывает практика, если начинается работа с датами — то каким-то конкретным случаем она не ограничивается.
Ну и тот же Momentjs не такой уж и огромный.
более того к написанию поста меня сподвигла именно особенность Safari
UFO just landed and posted this here
Если поправят, то должно будет также работать
А не надежнее было сделать крохотный массив, а для февраля уж совсем не сложно посчитать.
да new Date(y,m,0).getDate() же…
Только
new Date(y,m+1,0).getDate()
если подразумевается, что y и m текущие год и месяц.
Тоже хороший способ! Пожалуй дополню им пост чуть позже.
в общем в пору переименовать слегка статью)
Хочется сказать не про сам способ, а про то, как вы предлагаете его применять:

> Используя прекрассную возможность прототипирования в JavaScript можно расширить встроенный объект языка Date собственным методом

Это называется манки-патчинг (monkey patching) и лучше его избегать, потому что код, который вы напишете, рассчитывая на наличие этого патча, может быть использован где-то на стороне (не исключено что даже вами) и вместо желаемого результата, человек получит ошибку.

Уж лучше написать старую добрую простую функцию или если вам так нравится идея работать с классами, вы можете отнаследовать новый класс от стандартного Date, назвав его, например, ImprovedDate и работать с ним. Тогда, в его прототип вы сможете добавлять какие угодно методы и перекрывать любые стандартные уже объявленные.
Monkey patching это будет называться в случае если daysInMonth уже определен в Date.
Нет большой разницы, изменять в ран-тайме уже существующие методы или добавлять новые. Вот, например, Википедия встает на мою сторону:

> A monkey patch is a way to extend or modify the run-time code of dynamic languages without altering the original source code.

Но я не о названии, а о том, что такой путь неправилен и применять его можно только для быстрой отладки в консоли, а для промышленных решений он не годится.
Как у тебя вообще с английским? Внимательно перечитай всю статью, а не первый попавшийся абзац. Там ни слова про «загрязнение» ранее объявленных классов-интерфейсов новыми методами, что является обычной практикой во всех динамических языках с наличием прототипов, модулей или traits, где код собирается по кускам
Может быть всему виной мое плохое знание английского или что-то другое, но я совершенно не могу понять почему добавление новых методов в прототип объекта не считается манки-патчингом и не попадает под определение: extend or modify the run-time code of dynamic languages.

И еще, я не могу понять откуда у взялось конкретное определение манки-патча как только изменение методов уже существующих классов-интерфейсов. Может быть виной всему то, что я вырываю куски из контекста и никогда не читаю ничего до конца, но вот я вырвал из статьи в вики кусок, в котором говорится «Monkey patching is used to… apply a patch at runtime to the objects in memory, instead of the source code on disk». Если я в прототипе Date объявлю дополнительное свойство, то оно появится у всех инстансов класса Date.

var myDate = new Date();
Date.prototype.isMonkeyPatched = true;
myDate.isMonkeyPatched; // true

Никакого противоречия — действительно код выше модификацирует объекты в памяти, поэтому я не могу понять, почему добавление свойства или метода является манки-патчингом, а изменение — нет.

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

Я изначально-то говорил не об этом. Мой комментарий был совершенно не про трактовку определений, а про то, что изменения прототипа стандартного класса «на лету» (как бы они ни назывались, манки-патчинг или «загрязнение» ранее объявленных классов-интерфейсов новыми методами) — плохая практика для JavaScript'а и лучше найти любой другой способ, недостатка в которых нет.
// monkey patching valueOf
Date.prototype.oldValueOf = Date.prototype.valueOf
Date.prototype.valueOf = function() {
return parseInt(this.oldValueOf() / 1000);
}

vs

// extending Date
Date.prototype.valueOfInSeconds = function() {
return parseInt(this.valueOf() / 1000);
}

Заметно разницу? Первый пример — классический манки-патч, влияющий на остальной код, который использует Date#valueOf. Второй пример — динамическое расширение прототипа Date _новым_ методом, которое никоим образом не влияет на остальные инстансы Date (поменялся прототип, а не поведение ранее объявленных методов).
А вот пример изменения в «рантайме» (то, что как раз имелось ввиду в статье):

var date = new Date()
date.valueOf() // 1366289455443
date.valueOf = function() {
return 1;
}
date.valueOf() // 1
Раз такой хороший тред тут образовался — может, кто-нибудь знает библиотеку для работы с датами, которая умеет прибавлять к дате N рабочих дней? А то сейчас приходится использовать самописный костыль (и он меня немного пугает):

// add numDays to oldDate and return resulting date
function add_days(oldDate, numDays) {
    var new_date = new Date(oldDate.getFullYear(),oldDate.getMonth(),oldDate.getDate()+parseInt(numDays));
    return new_date;
}

function add_working_days(to_date, days) {
    // to_date: starting date,
    // days = number of working days to add
    var temp_date = new Date();
    var i = 0;
    var days_to_add = 0;
    while (i < (days)){
        temp_date = add_days(to_date, days_to_add);
        //0 = Sunday, 6 = Saturday, if the date not equals a weekend day then increase by 1
        if ((temp_date.getDay() != 0) && (temp_date.getDay() != 6)) {
            i+=1;
        }
	days_to_add += 1;
    }
    return add_days(to_date, days_to_add);
}
Вполне нормальное решение, имхо.

Если, правда, вам не нужно проверять по производственному календарю праздники.
О, вот как называется календарь с нерабочими днями, спасибо за наводку :)
Главное — не забывать, что календарь нерабочих день разнится не только от государства к государству, но и даже внутри одного государства — бывают региональные и профессиональные праздники.
В итоге к одной простенькой функции получаем в добавок огромный список праздничных дней :)
Например:
function getDaysByWD(dayOfWeek, num) {
  // на каждые 5 рабочих дней - полная неделя
  var full = parseInt(num / 5) * 7;
  // считаем остаток
  var rest = num % 5;
  // корректировка по выходным, пока ноль
  var d = 0;
  
  // Если суббота - то один день
  if (dayOfWeek == 6) {
    d = 1;
  }
  // а для рабочих дней
  else if (dayOfWeek > 0) {
    // считаем сколько дней осталось до выходных
    daysToWeeknd = 6 - dayOfWeek - 1;
    // если в остатке больше, чем до выходных
    // то нужно посчитать ещё одну пару выходных
    if (rest > daysToWeeknd) {
      d = 2;
    }
  }

  return full + rest + d;
}

или компактная версия:
function getDaysByWD(w,n,r) {
  return parseInt(n/5)*7+(r=n%5)+(w>0?w<6?r>(5-w)?2:0:1:0);
}

Возможно требует корректировок.
Если не ошибаюсь, в вашем алгоритме есть ошибка: если к пятнице прибавить один рабочий день, то вернётся суббота. Логичней было бы вернуть понедельник.
А так разве не проще?

var first = new Date(2011, 3, 1).getTime(),
    second = new Date(2011, 4, 1).getTime();
console.log((second - first)/86400000);
Не каждые сутки равны 86400000. Есть переходы на/с летнего времени, в такие дни стуки либо на час длинее, либо на час короче! Нужно добавить округление к ближайшему целому.
В JS хорошо поддержана работа с датами — просто не надо выходить за пределы документированности. Никто не гарантирует, что new Date(Y, M, 33) будет работать.

Как сосчитать число дней в месяце? Берём время первого числа следующего месяца, вычитаем время 1 числа текущего, делим на число микросекунд в сутках.
var d = +new Date()
	, thYear = d.getFullYear(), thMonth = d.getMonth()
	, nextMonth = (thMonth +1) % 12, nextYear = thYear + (thMonth==11);
var daysInThMonth = (+new Date(nextYear, nextMonth, 1) - new Date(thYear, thMonth, 1))/ 86400000;

Тут можно сократить. Но суть в том, что так совершенно без трюков (не считая +new вместо getTime()) берётся число дней, пользуясь работой с датами.

А Вы в выводах написали 2 противоречащих утверждения: «Идеальная практика» и «Данный способ работает в Safari пока также хорошо». Способ, «работающий, пока», не может быть идеальным: ).
делим на число микросекунд в сутках.
27 октября 2013 у вас получится 31.041666666666668 дней в месяце, так как число микросекунд в сутках — не константа :)
Опечатался — миллисекунд.
Да, нужно Math.round снаружи, чтобы учесть любые переходы на зимнее-летнее время в разных странах.
я вот такое использую:

var _date:Date = new Date(year, month+1, 1);
_date.date--;
trace(_date.date);

Спасибо за статью. Очень помогли. Ранее расчет вел с помощью другого алгоритма, который имеет в себе ряд изъянов.
Sign up to leave a comment.

Articles