Подтвердить количество дней в данном месяце
Производительность имеет первостепенное значение для этих парней... Эта вещь должна быть молниеносной!
Как бы вы проверили количество дней в данном месяце?
Моя первая мысль заключалась в том, чтобы создать массив, содержащий дни данного месяца, с индексом, представляющим месяц:
var daysInMonth = [
31, // January
28, // February
31, // March
etc.
];
И затем сделайте что-то по строкам:
function validateDaysInMonth(days, month)
{
if (days < 1 || days > daysInMonth[month]) throw new Error("Frack!");
}
Но... Как насчет високосных лет? Как я могу реализовать проверку на високосные годы и поддерживать быстродействие функции?
Обновление:. Я бы хотел, чтобы вы, ребята, показали мне код, который выполняет дни проверки на месяц-високосный год.
Здесь блок-схема, описывающая используемую сегодня логику:
http://visualbasic.about.com/library/graphics/dykleapyr1-1.gif
Ответы
Ответ 1
function daysInMonth(m, y) { // m is 0 indexed: 0-11
switch (m) {
case 1 :
return (y % 4 == 0 && y % 100) || y % 400 == 0 ? 29 : 28;
case 8 : case 3 : case 5 : case 10 :
return 30;
default :
return 31
}
}
function isValid(d, m, y) {
return m >= 0 && m < 12 && d > 0 && d <= daysInMonth(m, y);
}
Ответ 2
Я делаю это с использованием объекта Date (при условии, что он скомпилирован и, следовательно, ослепительно быстрый по сравнению со сценарием).
Фокус в том, что если вы вводите слишком большое число для части даты, объект Date переносится в следующий месяц. Итак:
var year = 2009;
var month = 1;
var date = 29;
var presumedDate = new Date(year, month, date);
if (presumedDate.getDate() != date)
WScript.Echo("Invalid date");
else
WScript.Echo("Valid date");
Это будет означать "Недействительная дата", потому что presumedDate на самом деле 1 марта.
Это оставляет все проблемы високосных годов и т.д. объекту Date, где мне не нужно беспокоиться об этом.
Аккуратный трюк, а? Грязный, но для вас сценарий...
Ответ 3
Если месяц не в феврале, получите номер из массива. В противном случае проверьте, является ли год прыжком, чтобы вернуть 29, или верните 28. Есть ли проблема с этим?
Ответ 4
Это не будет работать так же, как и принятый ответ. Я бросил это здесь, потому что я думаю, что это самый простой код. Большинство людей не нуждаются в оптимизации этой функции.
function validateDaysInMonth(year, month, day)
{
if (day < 1 || day > 31 || (new Date(year, month, day)).getMonth() != month)
throw new Error("Frack!");
}
Он использует тот факт, что конструктор даты javascript будет выполнять арифметику даты в датах, которые находятся за пределами допустимого диапазона, например:
var year = 2001; //not a leap year!
var month = 1 //February
var day = 29; //not a valid date for this year
new Date(year, month, day);
объект вернется 1 марта 2001 года в качестве даты.
Ответ 5
Moment.js
Вы пробовали moment.js?
проверка довольно проста в использовании:
var m = moment("2015-11-32");
m.isValid(); // false
Я не знаю о спектаклях, но гул, проект смотрится 11 000 + раз на GitHub (вид гарантии качества).
Источник: http://momentjs.com/docs/#/parsing/is-valid/
Ответ 6
Я в основном соглашаюсь с Moayad. Я бы использовал поиск в таблице, с проверкой на февраль и год.
псевдокод:
Last_Day = Last_Day_Of_Month[Month];
Last_Day += (Month == February && Leap_Year(Year)) ? 1 : 0;
Обратите внимание, что Leap_Year() не может быть реализован просто как (Year % 4 == 0)
, потому что правила для високосных годов являются более сложными. Здесь приведен алгоритм из Википедии
bool Leap_Year (int year) {
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}
Ответ 7
function caldays(m,y)
{
if (m == 01 || m == 03 || m == 05 || m == 07 || m == 08 || m == 10 || m == 12)
{
return 31;
}
else if (m == 04 || m == 06 || m == 09 || m == 11)
{
return 30;
}
else
{
if ((y % 4 == 0) || (y % 400 == 0 && y % 100 != 0))
{
return 29;
}
else
{
return 28;
}
}
}
источник: http://www.dotnetspider.com/resources/20979-Javascript-code-get-number-days-perticuler-month-year.aspx
Ответ 8
Я согласен с Moayad и TED. Придерживайтесь таблицы поиска, если месяц не февраль. Если вам нужен алгоритм проверки високосных лет, wikipedia имеет два:
if year modulo 400 is 0 then leap
else if year modulo 100 is 0 then no_leap
else if year modulo 4 is 0 then leap
else no_leap
A more direct algorithm (terms may be grouped either way):
function isLeapYear (year):
if ((year modulo 4 is 0) and (year modulo 100 is not 0)) or (year modulo 400 is 0)
then true
else false
Ответ 9
вся эта логика уже встроена в механизм javascript... Зачем это перекодировать? Если вы не делаете это как упражнение, вы можете использовать объект Date javascript:
Вот так:
function daysInMonth(aDate) {
return new Date(aDate.getYear(), aDate.getMonth()+1, 0).getDate();
}
Ответ 10
Предполагая, что стандарт объекта JS Date, где месяцы пронумерованы от 0, и у вас есть свой массив daysInMonth:
var days = daysInMonth[month] + ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0)));
даст вам количество дней в месяце, при этом 28 увеличатся до 29, если месяц - февраль, а год - високосный год.
Ответ 11
Вы можете использовать DateTime, чтобы решить эту проблему:
new DateTime('20090901')->format('t'); // gives the days of the month
Ответ 12
В компьютерных терминах решения new Date()
и regular expression
медленны! Если вы хотите супер-быстрый (и супер-критический) однострочный, попробуйте этот (если m
находится в формате Jan=1
):
Единственная реальная конкуренция за скорость - от @GitaarLab, поэтому я создал JSPerf для головы для тестирования: http://jsperf.com/days-in-month-head-to-head/5
Я продолжаю пробовать разные изменения кода, чтобы получить лучшую производительность.
Текущая версия
Посмотрев на этот связанный вопрос проверку високосного года с помощью побитовых операторов (удивительная скорость) и узнав, что представляет собой 25 и 15 магическое число, я придумал это оптимизированный гибрид ответов:
function getDaysInMonth(m, y) {
return m===2 ? y & 3 || !(y%25) && y & 15 ? 28 : 29 : 30 + (m+(m>>3)&1);
}
JSFiddle: http://jsfiddle.net/TrueBlueAussie/H89X3/22/
Результаты JSPerf: http://jsperf.com/days-in-month-head-to-head/5
По какой-то причине (m+(m>>3)&1)
более эффективен, чем (5546>>m&1)
почти во всех браузерах.
Он работает на основе моего високосного года здесь: javascript, чтобы найти високосный год этот ответ здесь Високосный год с использованием побитового операторы (удивительная скорость), а также следующая двоичная логика.
Быстрый урок в двоичных месяцах:
Если вы интерпретируете индекс желаемых месяцев (Jan = 1) в двоичном формате, вы заметите, что месяцы с 31 днем либо имеют бит 3, либо бит, либо бит 0, либо бит 3 установлен, а бит 0 очищен.
Jan = 1 = 0001 : 31 days
Feb = 2 = 0010
Mar = 3 = 0011 : 31 days
Apr = 4 = 0100
May = 5 = 0101 : 31 days
Jun = 6 = 0110
Jul = 7 = 0111 : 31 days
Aug = 8 = 1000 : 31 days
Sep = 9 = 1001
Oct = 10 = 1010 : 31 days
Nov = 11 = 1011
Dec = 12 = 1100 : 31 days
Это означает, что вы можете сдвинуть значение 3 места с помощью >> 3
, XOR битов с исходным ^ m
и посмотреть, есть ли результат 1
или 0
в позиции бита 0 с помощью & 1
. Примечание. Оказывается, +
немного быстрее, чем XOR (^
), а (m >> 3) + m
дает тот же результат в бит 0.
Результаты JSPerf: http://jsperf.com/days-in-month-perf-test/6