Разница даты в Javascript (игнорирование времени суток)
Я пишу заявку на аренду оборудования, где клиентам взимается плата за аренду оборудования в зависимости от продолжительности (в днях) аренды. Итак, в основном, (ежедневная плата * количество дней) = общая плата.
Для мгновенной обратной связи на стороне клиента я пытаюсь использовать Javascript для определения разницы в двух календарных датах. Я искал вокруг, но ничего, что я нашел, совсем не то, что я ищу. Большинство решений, которые я видел, имеют форму:
function dateDiff1(startDate, endDate) {
return ((endDate.getTime() - startDate.getTime()) / 1000*60*60*24);
}
Моя проблема заключается в том, что оборудование может быть проверено и возвращено в в любое время суток в течение этих двух дат без дополнительной оплаты. Вышеприведенный код вычисляет количество 24-часовых периодов между двумя датами, когда меня действительно интересует количество календарных дней.
Например, если кто-то проверил оборудование в 6 утра 6 июля и вернул его в 10 вечера 7 июля, то приведенный выше код рассчитал бы, что прошло более одного 24-часового периода, и вернется 2. Желаемый результат: 1, поскольку прошло только одна календарная дата (т.е. с 6-го по 7-й).
Самое близкое решение, которое я нашел, это эта функция:
function dateDiff2(startDate, endDate) {
return endDate.getDate() - startDate.getDate();
}
который делает именно то, что я хочу, пока две даты находятся в пределах того же месяца. Однако, поскольку getDate() возвращает день месяца (т.е. 1-31), он не работает, если даты охватывают несколько месяцев (например, с 31 июля по 1 августа - 1 день, но приведенные выше расчеты 1 - 31, или -29).
На бэкэнде, в PHP, я использую gregoriantojd(), который, кажется, работает очень хорошо (см. этот пост для примера). Я просто не могу найти эквивалентное решение в Javascript.
У кого-нибудь есть идеи?
Ответы
Ответ 1
Если вам нужны целые дни для примера проката студенческой камеры...
function daysBetween(first, second) {
// Copy date parts of the timestamps, discarding the time parts.
var one = new Date(first.getFullYear(), first.getMonth(), first.getDate());
var two = new Date(second.getFullYear(), second.getMonth(), second.getDate());
// Do the math.
var millisecondsPerDay = 1000 * 60 * 60 * 24;
var millisBetween = two.getTime() - one.getTime();
var days = millisBetween / millisecondsPerDay;
// Round down.
return Math.floor(days);
}
Ответ 2
У меня была эта проблема, и я решил ее найти, поэтому я вернулся, чтобы опубликовать это. Это даст общее количество дней независимо от времени. И DST не испортит это:
date1 = Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate());
date2 = Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate());
var ms = Math.abs(date1-date2);
return Math.floor(ms/1000/60/60/24); //floor should be unnecessary, but just in case
Трюк преобразуется в дату UTC, которая даже не знает о временах исходных дат.
Ответ 3
То, что я сделал бы, это установить два времени даты в одно и то же время. Например, установите время endDate до 12:00 am и startDate до 12:00. Затем получите разницу между ними.
С другой стороны, поскольку я тоже в индустрии программного обеспечения для прокатного оборудования, похоже, вы теряете доход от аренды, не считая часы. На ваш пример, если кто-то взял оборудование 6 июля в 6 утра и вернул его 7 июля в 10 вечера. У них было два полных дня, чтобы использовать оборудование и, возможно, взять на себя чрезмерную плату за счет тоже...
Ответ 4
В данных решениях есть ошибка!
Это относится к различиям даты, когда время игнорируется. И вы хотите получить целочисленный результат, то есть целое число дней.
Во многих из приведенных выше примеров мы видим Math.floor другие экземпляры, которые я видел в Math.ceil и в других местах. Это делается для округления результата до целого числа дней. Проблема заключается в том, что переход на летнее время приведет к неправильному результату падения с использованием Math.ceil. Ваш результат будет слишком большим или в spring, если вы используете Math.floor, вы будете отключены на один день слишком мало. Просто используйте Math.round, потому что 1 час в любом случае не будет искажать результат.
function dateDiff(dateEarlier, dateLater) {
var one_day=1000*60*60*24
return ( Math.round((dateLater.getTime()-dateEarlier.getTime())/one_day) );
}
Ответ 5
Я также предложил бы взглянуть на невероятную библиотеку moment.js. Он имеет универсальную diff
функцию, которую вы можете использовать для решения приведенного выше примера следующим образом:
function is_same_date(startDate, endDate) {
var startMoment = moment(startDate).clone().startOf('day'),
endMoment = moment(endDate).clone().startOf('day');
return startMoment.diff(endMoment, 'days') == 0;
}
Вот несколько примеров, использующих moment.js
diff:
> d1 = new Date(2012,04,04,0,0)
Fri May 04 2012 00:00:00 GMT-0400 (EDT)
> d2 = new Date(2012,04,03,23,0)
Thu May 03 2012 23:00:00 GMT-0400 (EDT)
> d3 = new Date(2012,04,05,23,0)
Sat May 05 2012 23:00:00 GMT-0400 (EDT)
> moment(d2).diff(d1, 'days')
0
> moment(d1).diff(d2, 'days')
0
> moment(d1).diff(d3, 'days') // uh oh. this is why we use `startOf`
-2
> moment(d2).diff(d3, 'days')
-2
> moment(d3).startOf('day') // this modified d3, sets the time to 00:00:00.0000
> d3
Sat May 05 2012 00:00:00 GMT-0400 (EDT)
Если часовые пояса вызывают озабоченность, вы также можете использовать moment.utc()
для преобразования в нормализованный часовой пояс.
Ответ 6
Поскольку юлианский день - это фактически количество дней (а в некоторых случаях и доля числа дней) с определенной даты, оно практически совпадает с временной отметкой UNIX, представленной по-разному. Вы можете получить количество целых дней с 1970 года так:
Date.prototype.getWholeDays = function () {
return Math.floor(new Date() / 1000*60*60*24);
};
function dateDiff(startDate, endDate) {
return endDate.getWholeDays() - startDate.getWholeDays();
}
Ответ 7
Если вы хотите только разницу в днях, обязательно используйте UTC (GMT), или вычитание не приведет к целым числам дней в секундах, если в течение интервала начнется или закончится переход на летнее время.
Ответ 8
Раньше я проверял результат функции датирования, который использует .getTime() с Excel в 18 веке. Результат вернется правильно. У меня есть другой способ вычисления разных дней:
function DaysOfYear(nYear) {
if ((nYear % 4) == 0) {
return 366;
}
else {
return 365;
}
}
function DiffDays(fDate, tDate) {
var fYear = fDate.getFullYear();
var tYear = tDate.getFullYear();
var fMonth = fDate.getMonth();
var tMonth = tDate.getMonth();
var fDay = fDate.getDate();
var tDay = tDate.getDate();
var nDays = 0;
if (tYear > fYear) { // different year
// remain days of from year
nDays = DaysOfYear(fYear) - YTD_Days(fDate);
// days of between fYear to tYear
for (y = (fYear + 1); y < tYear; y++) {
nDays = nDays + DaysOfYear(y);
}
// days from the beginning of tYear
nDays = nDays + YTD_Days(tDate);
}
else { // in the same year
nDays = YTD_Days(tDate) - YTD_Days(fDate);
};
return nDays;
}
function YTD_Days(dDate) {
var y = dDate.getFullYear();
var m = dDate.getMonth();
var nDays = 0
for (i = 0; i < m; i++) {
switch (i) {
case 0: // January
nDays = nDays + 31;
break;
case 1: // February
if ((y % 4) == 0) {
nDays = nDays + 29;
}
else {
nDays = nDays + 28;
};
break;
case 2: // March
nDays = nDays + 31;
break;
case 3: // April
nDays = nDays + 30;
break;
case 4: // May
nDays = nDays + 31;
break;
case 5: // June
nDays = nDays + 30;
break;
case 6: // July
nDays = nDays + 31;
break;
case 7: // August
nDays = nDays + 31;
break;
case 8: // September
nDays = nDays + 30;
break;
case 9: // October
nDays = nDays + 31;
break;
case 10: // November
nDays = nDays + 30;
break;
case 11: // December
nDays = nDays + 31;
break;
}
}
nDays = nDays + dDate.getDate();
return nDays;
}
Ответ 9
Вы можете настроить дату начала до полуночи того же дня, а затем рассчитать количество 24-часовых периодов между датами. Затем вы возьмете потолок или пол этого номера в зависимости от того, хотите ли вы считать какую-то часть дня целым днем или нет.
function dateDiff(startDate, endDate) {
// set startDate to the beginning of the day
startDate = new Date(
startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate());
return Math.floor((endDate - startDate) / 1000*60*60*24);
}
Ответ 10
использовать метод setHours(), считая, что количество дней никогда не может быть равно нулю:)
function dateDiff1(startDate, endDate) {
endDate.setHours(0);
startDate.setHours(0);
//assuming days cannt be 0.
var x = ((endDate.getTime() - startDate.getTime()) / 1000*60*60*24);
if (x <1 && x>=0) {
return 1;
}
return x;
}
Ответ 11
Чтобы получить полные дни, интервал дат в течение летнего времени меняет пол (дни), который должен быть заменен раундом. В противном случае это очень полезная функция.
Ответ 12
+ выше пример выше, который использует конструкторы UTC. Без этого вы будете сбиты с помощью летнего времени/летних часов.
Если вы примете различия в некоторых из них, это может привести к короткому замыканию на час при пересечении линии DST для вашего часового пояса в одном направлении (spring -forward, что означает, например, февраль, просящий в апреле). Когда вы поланете N + 23/24, вы получите N.
В другом направлении (в октябре с просьбой к декабрю) должно быть хорошо, вы закончите на час вперед, который пол() будет иметь дело правильно.
Ответ 13
Просто из любопытства:
Если вы можете утверждать, что результат будет ВСЕГДА быть < 31 день, то также является решением:
var deltaDays = new Date(endDate - startDate).getDate()-1
Почему?
Хорошо разложив предыдущую строку:
var msecs = endDate - startDate; // difference in milliseconds
var dt = new Date(msecs); // will give us a date object pointing to a time
// somewhere in Jan 1970 (given the precondition above)
var days = dt.getDate(); // Take the day part of it
var deltaDays = days - 1; // since the numbering starts from 1, we have to
// substract 1 (Ex. if the diff in msecs is 0. The
// Date will point to 1st of Jan 1970)
Но, очевидно, вы должны использовать НЕ, если ваши результаты могут быть > 31.
Ответ 14
Нужна дата .js, http://code.google.com/p/datejs/
function CalculateDuration (startDate, endDate) {
var sdt = Date.parse(startDate);
var edt = Date.parse(endDate);
var sdtYear = sdt.getYear();
var edtYear = edt.getYear();
var sdtMonth = sdt.getMonth();
var edtMonth = edt.getMonth();
var sdtDay = sdt.getDate();
var edtDay = edt.getDate();
var StartDateMonthDays = Date.getDaysInMonth(sdtYear, sdtMonth);
var EndDateMonthDays = Date.getDaysInMonth(edtYear, edtMonth);
if (edtMonth < sdtMonth) { //Means the ending month is earlier, which invalidates the operation
alert("Ending month cannot be earlier than the starting month")
}
//Months are equal, if month is the next month of the start date month,
//the operation fails, thus we need to calculate month difference first
else if (edtMonth > sdtMonth) {
//Need to count how many days are left to finish the month
var daysToMonthFinish = StartDateMonthDays - sdtDay;
var calculatedDuration = daysToMonthFinish + edtDay;
$("#hdn_duration").val(calculatedDuration);
}
//If months are the same, it is safe to just calculate the end day minus the start day
else {
var calcDuration = edtDay - sdtDay;
$("#hdn_duration").val(calcDuration);
}
Ответ 15
Я знаю, что это частичное решение, но вы можете получить дни между датами (если даты не установлены). Тогда вы можете работать оттуда.
Я основываю это решение на проблеме, которую вы представили ((ежедневная плата * количество дней) = общий сбор); а не вопрос о фактической дате. Это должно помочь вам.
var Main = function() {
var startDate = new Date('08/20/2014');
var endDate = new Date('08/22/2014');
var totalCharge = monthlyFee * daysBetween(startDate, endDate);
}
var daysBetween = function(startDate, endDate) {
return Math.round(Math.abs((startDate.getTime() - endDate.getTime())/(24*60*60*1000)));
};