【问题标题】:Date difference in Javascript (ignoring time of day)Javascript中的日期差异(忽略一天中的时间)
【发布时间】:2010-11-05 10:09:52
【问题描述】:

我正在编写一个设备租赁应用程序,其中根据租赁的持续时间(以天为单位)向客户收取租赁设备的费用。所以,基本上,(每日费用 * 天数)= 总费用。

对于客户端的即时反馈,我正在尝试使用 Javascript 来找出两个日历日期的差异。我四处寻找,但我发现的东西都不是我想要的。我见过的大多数解决方案都是以下形式:

function dateDiff1(startDate, endDate) {
    return ((endDate.getTime() - startDate.getTime()) / 1000*60*60*24);
}

我的问题是,在这两个日期内,可以在一天中的任何时间签出和退回设备,而无需支付额外费用。上面的代码是计算两个日期之间的 24 小时周期数,而我对日历天数很感兴趣。

例如,如果有人在 7 月 6 日早上 6 点签​​出设备,并在 7 月 7 日晚上 10 点返回设备,上面的代码将计算超过一个 24 小时的时间段,并返回 2。期望的结果是 1 ,因为只过了一个日历日期(即 6 日到 7 日)。

我找到的最接近的解决方案是这个函数:

function dateDiff2(startDate, endDate) {
    return endDate.getDate() - startDate.getDate();
}

这正是我想要的,只要两个日期在同一个月内。但是,由于 getDate() 只返回月份中的某天(即 1-31),因此当日期跨越多个月份时(例如 7 月 31 日至 8 月 1 日为 1 天,但上述计算为 1-31,或-29)。

在后端,在 PHP 中,我使用 gregoriantojd(),它似乎工作得很好(参见 this post 的例子)。我只是在 Javascript 中找不到等效的解决方案。

有人有什么想法吗?

【问题讨论】:

  • 如果设备在同一天检出并归还,这些解决方案中的每一个都是不正确的。如果您不提供免费的当日租赁服务,您可能希望在代码中的某处使用diff = Math.max(1, diff)! :]

标签: javascript date


【解决方案1】:

我知道这是一个部分解决方案,但您可以获取日期之间的天数(只要日期没有设置时间)。然后你就可以从那里开始工作了。

我根据您提出的问题来解决这个问题((每日费用 * 天数)= 总费用);不是实际的日期差异问题。这应该可以帮助您。

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

【讨论】:

    【解决方案2】:

    需要date.js,http://code.google.com/p/datejs/

    函数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);
    }
    

    【讨论】:

      【解决方案3】:

      只是出于好奇:

      如果您可以确认结果总是

      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,您应该使用它。

      【讨论】:

        【解决方案4】:

        我还建议看看令人难以置信的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() 转换为标准化时区。

        【讨论】:

        • 为什么在调用 startOf() 之前使用 clone()?
        • @SalmanHasratKhan 好问题——如果您不使用.clone,那么矩库将修改基础日期对象。
        【解决方案5】:

        +up 上面使用 UTC 构造函数的示例。如果不这样做,您将被夏令时/夏令时所困扰。

        如果您考虑其中一些差异,那么当您在一个方向穿过您所在时区的 DST 线时,它可能会缩短一个小时(例如,弹簧向前,意思是 2 月要求 4 月)。当你底 N+23/24 时,你最终会得到 N。

        另一个方向(10 月要求 12 月)应该没问题,你最终会提前一个小时让 floor() 正确处理。

        【讨论】:

          【解决方案6】:

          我曾经检查过 18 世纪使用 .getTime() 和 Excel 的 datediff 函数的结果。结果返回正确。我还有另一种计算不同天数的方法:

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

          【讨论】:

            【解决方案7】:

            要获得完整的天数,夏令时更改的日期间隔必须用圆形替换地板(天)。否则它是一个非常有用的功能。

            【讨论】:

              【解决方案8】:

              我刚刚遇到这个问题并在找到这个问题后解决了它,所以我回来发布这个。无论时间如何,这都会得到总天数。而且 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 日期。

              【讨论】:

              • 也使用了这种方法 - 需要确定两个日期之间的天数。这是最简单的,不考虑时间和地板/天花板。
              【解决方案9】:

              给定的解决方案中存在错误!

              这适用于忽略时间并且您需要整数结果(即整数天数)的日期差异。

              在上面的许多示例中,我们看到了 Math.floor 其他实例,我也在其他地方看到了 Math.ceil。这样做是为了将结果四舍五入到整数天数。问题是夏令时在秋季使用 Math.ceil 会给出错误的结果——你的结果会过大一天,或者在春季如果你使用 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)  );
              }
              

              【讨论】:

              • 我认为 OP 希望 1 月 1 日 23:59 和 1 月 2 日 00:01 相差 1 天。
              【解决方案10】:

              如果您只想要天数差异,请务必使用 UTC (GMT),否则如果夏令时在间隔期间开始或结束,则减法不会产生以秒为单位的整数天数。

              【讨论】:

                【解决方案11】:

                如果您想要一整天的学生相机租赁示例...

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

                【讨论】:

                • 记得在 Math.floor() 之前调用 Math.abs(),如果可能发生“第一”发生在“第二”之前。
                • 要考虑/允许负数,请将Math.floor() 换成条件语句:return second &gt; first ? Math.floor(days) : Math.ceil(days);
                • 抱歉,这不起作用。用这个值试试会失败:one = new Date(2014, 2, 29);二 = 新日期(2014, 2, 31);将返回 1 天而不是 2 天!
                • 解决方法是使用 Math.round 代替 ceil/floor。
                • 同意使用Math.floor 会导致错误,如果您处于Brasilia 等时区,DST 在午夜结束。有两种可能的解决方案:1) 改用Math.round,或2) 始终直接使用Date.UTC 进行计算,就像在sheriffpony 的solution 中所做的那样。
                【解决方案12】:

                使用 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;
                }
                

                【讨论】:

                  【解决方案13】:

                  由于儒略日实际上是自某个日期以来的天数(在某些情况下也是天数的一部分),因此它实际上与 UNIX 时间戳相同,但呈现方式不同。您可以像这样获得自 1970 年以来的天数:

                  Date.prototype.getWholeDays = function () {
                      return Math.floor(new Date() / 1000*60*60*24);
                  };
                  
                  function dateDiff(startDate, endDate) {
                      return endDate.getWholeDays() - startDate.getWholeDays();
                  }
                  

                  【讨论】:

                  • 如果 startDate 和 endDate 在 24 小时之内,我认为这不会起作用。例如。如果 startDate 是 7 月 6 日晚上 11:30,而 endDate 是 7 月 7 日上午 12:30,那会不会舍入到 0? (当然,这是一个极端的例子,但仍然是一个有效的担忧)。
                  【解决方案14】:

                  您可以将开始日期调整为同一天的午夜,然后计算两个日期之间的 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);
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    我要做的是将两个日期的时间设置为同一时间。例如,将 endDate 的时间设置为上午 12:00,将 startDate 的时间也设置为上午 12:00。然后得到它们之间的区别。

                    附带说明,由于我也从事租赁设备软件行业,因此您似乎因为不计算时间而损失了租金收入。根据您的示例,如果有人在 7 月 6 日早上 6 点拿起设备并在 7 月 7 日晚上 10 点归还设备。他们有整整​​两天的时间来使用设备,并且可能还会产生额外的电表费用......

                    【讨论】:

                    • 这是一个很好的观点。不过,我们是一个学生经营的电视台,所以租金更多是为了鼓励合理的租期,而不是为了赚钱。我们每天只收取大约 5 美元的相机费用。
                    • 为什么当这个答案没有解决问题的第二部分时,它被标记为正确,即。当日期在不同的月份?
                    猜你喜欢
                    • 1970-01-01
                    • 2017-02-15
                    • 1970-01-01
                    • 2021-06-27
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多