【问题标题】:Relative Date: 1/1/1981 01:01:01 - 1/1/1980 = 1y 366d ect, want 1y 1d etc相对日期:1/1/1981 01:01:01 - 1/1/1980 = 1y 366d 等,想要 1y 1d 等
【发布时间】:2015-05-02 01:31:39
【问题描述】:

我正在对日期库进行一些增强,该库已经返回日期单位的差异,如本问题的标题所示。我希望它返回相对单位。我已经写了一些代码,但感觉好像我在计算错误的结果。

注意:忘记闰年和其他复杂的日期时间。这适用于不严格的应用程序。

这是正在考虑的代码。

// get ms between UTC dates and make into "difference" date
var iDiffMS = dt2.valueOf() - dt1.valueOf();
var dtDiff = new Date(iDiffMS);

// calc various diffs
var nYears  = dt2.getUTCFullYear() - dt1.getUTCFullYear();
var nMonths = dt2.getUTCMonth() - dt1.getUTCMonth() + (nYears!=0 ? nYears*12 : 0);
var nQuarters = parseInt(nMonths / 3);  //<<-- different than VBScript, which watches rollover not completion

// these are in absolute terms, ie totals of unit differences, 1/2/1981 - 1/1/1980 = 1y 366d
var nMilliseconds = iDiffMS;
var nSeconds = parseInt(iDiffMS / 1000);
var nMinutes = parseInt(nSeconds / 60);
var nHours = parseInt(nMinutes / 60);
var nDays  = parseInt(nHours / 24); // <-- now fixed for DST switch days
var nWeeks = parseInt(nDays / 7);
// save absolutes
var nYears0=nYears;
var nMonths0=nMonths;
var nQuarters0=nQuarters;
var nWeeks0=nWeeks;
var nDays0=nDays;
var nHours0=nHours;
var nMinutes0=nMinutes;
var nSeconds0=nSeconds;

// HERE! go from absolute to relative, 1/2/1981 - 1/1/1980 = 1y 1d etc, not 1y 366d etc
nQuarters     -=nYears0*4;
nMonths       -=nYears0*12;
nWeeks        -=parseInt((nYears0*52)+(nMonths0*(365.25/52)));
nCalWeeks     -=parseInt((nYears0*52)+(nMonths0*(365.25/52)));
nQuarters     -=nYears0*4;
nDays         -=parseInt((nYears0*365.25)               +(nMonths*(365.25/12)));
nHours        -=parseInt((nYears0*365.25*24)            +(nMonths*(365.25/12*24))           +(nDays*24));
nMinutes      -=parseInt((nYears0*365.25*24*60)         +(nMonths*(365.25/12*24*60))        +(nDays*24*60)         +(nHours*60));
nSeconds      -=parseInt((nYears0*365.25*24*60*60)      +(nMonths*(365.25/12*24*60*60))     +(nDays*24*60*60)      +(nHours*60*60)      +(nMinutes*60));
nMilliseconds -=parseInt((nYears0*365.25*24*60*60*1000) +(nMonths*(365.25/12*24*60*60*1000))+(nDays*24*60*60*1000) +(nHours*60*60*1000) +(nMinutes*60*1000) + (nSeconds*1000));
// END

【问题讨论】:

  • 在您想要的格式中,1981 年 3 月 3 日至 1980 年 1 月 1 日的正确答案是什么?你想要“1年2个月2天”吗?
  • 是的,只需以毫秒为单位(1970 年之后)将其转换回来
  • 是的,1 年、2 个月、2 天、x 小时等

标签: javascript algorithm date datetime time


【解决方案1】:

可以通过从较大的单元到较小的单元来实现这一点,但是从小到大的工作可能更容易,因为您不必事先将因子相乘。例如,您不必提前计算出一天是 24*60*60*1000 毫秒。相反,您可以在从最小单位到最大单位的过程中累积因子。

在第一步中,您知道必须将毫秒数除以 1000 才能得到秒数。在第二步中,您将秒除以 60 以获得分钟数。接下来,除以 60 得到小时数。接下来,除以 24 得到天数。

为确保您不会重复计算单位,您可以使用模运算符%。例如,如果分钟数为 135,则计算 135 % 60 得到 15,即减去整小时后剩余的分钟数。通常,在显示当前单位的数量之前,您需要对下一个更大的单位取模。

以下脚本实现了这种方法。

function getDifferenceInWords(dateStart, dateEnd) {
  var delta = dateEnd.valueOf() - dateStart.valueOf();
  if (delta < 0) {
    var describe = "earlier";
    delta = -delta;
  } else {
    var describe = "later";
  }
  var units = [{ name: 'second', multiple: 1000 },  
               { name: 'minute', multiple: 60 },    
               { name: 'hour', multiple: 60 },    
               { name: 'day', multiple: 24 },    
               { name: 'week', multiple: 7 },     
               { name: 'quarter', multiple: 13 },
               { name: 'year', multiple: 4 }],
      divisor = 1,
      parts = [];
  for (var i = 0; i < units.length; ++i) {
    var unit = units[i];
    divisor *= unit.multiple;
    if (divisor > delta) {
      break;
    }
    var count = Math.floor(delta / divisor);
    if (i+1 < units.length) {
      count %= units[i+1].multiple;
    }
    if (count != 0) {
      parts.push(count + ' ' + unit.name + (count == 1 ? '' : 's'));
    }
  }
  parts.reverse();
  return parts.join(', ') + ' ' + describe;
}

var dateStart = new Date('January 1, 1995 09:01:30'),
    dateEnd = new Date('May 1, 2015 23:05:15');

document.write(getDifferenceInWords(dateStart, dateEnd));

您可以通过缩写单位名称或更改单位顺序来根据自己的喜好调整代码。

例如,如果您可以确定月与周的固定比率,您可能希望在周和季度之间引入月作为中间单位。我更喜欢省略月份单位,因为不同的月份有不同的长度,这会导致结果不一致,具体取决于您处理的日期。

另一种修改单位的方法可能是添加几十年、世纪和千年。只要遵循units数据结构的模式,每个单元都是前一个单元的固定倍数,就不会出错。

【讨论】:

  • 我喜欢这种方法,尽管它将字符串输出与计算混合在一起。我确信可以通过某种反向计算来解决月份的问题,但我的大脑现在还没有做好。
  • 字符串根本不影响计算。我只是通过形成复数单位名称并省略零单位计数来美化输出。
【解决方案2】:

好的,这就是我一直在寻找的东西,请注意,当您下班后它会发生怎样的变化。继续使用t1-= 路径会由于闰年而导致时间错误。此外,必须以这种方式计算月份以纠正飞跃。我怀疑weeks 的计算存在问题。

var d1=new Date('1/1/1980 00:00:00:000');
var d2=new Date('2/8/1981 01:01:01:001');
//BUG "1y 1m 1w 1d 23h 59mins 59secs 999ms" should be 0d
//var d2=new Date('2/7/1981 23:59:59:999'); 

var t1=d2.valueOf() - d1.valueOf();
console.log(t1);
var nYears= parseInt(t1/(1000*60*60*24*365.25));
t1-=nYears*(1000*60*60*24*365.25);
var s='';
s+=nYears+'y';
var nMonths=d2.getUTCMonth() - d1.getUTCMonth();
nMonths=nMonths>0?nMonths:nMonths+12;
s+=' '+nMonths+'m'
t1-=nMonths*1000*60*60*24*(365.25/12);
var nWeeks=parseInt(t1/(1000*60*60*24*7));
s+=' '+nWeeks+'w'
t1-=nWeeks*(1000*60*60*24*7);
var nDays=parseInt(t1/(1000*60*60*24));
s+=' '+nDays+'d'
t1-=nDays*(1000*60*60*24);
var nHours=d2.getHours()-d1.getHours();
s+=' '+nHours+'h';
var nMinutes=d2.getMinutes()-d1.getMinutes();
s+=' '+nMinutes+'mins';
var nSeconds=d2.getSeconds()-d1.getSeconds();
s+=' '+nSeconds+'secs';
var nMilliseconds=d2.getMilliseconds()-d1.getMilliseconds();
s+=' '+nMilliseconds+'ms';
//console.log(d1.getHours());
console.log(s); //"1y 1m 1w 1d 1h 1mins 1secs 1ms"

【讨论】:

  • 此代码不起作用。试试var d1 = new Date('12/25/1980 10:10:00:000'); var d2 = new Date('2/8/1981 01:01:01:001');
  • 还是不行。如果你想使用内置的get...() 函数,你必须确保你是从过去到未来或者从未来到过去。或者您可以避免所有这些,只使用我在回答中显示的算术运算。
  • 您在我的示例中忽略了小时和分钟。此外,您还没有真正解决几个月的问题。考虑这些时间跨度:4/25/19805/5/19804/5/19804/25/19804/25/19794/5/1980。你看到发生了什么吗?现在将小时和分钟投入到混合中,看看进一步的复杂性。你需要的是一个系统的方法。你必须沿着时间的箭头严格地往一个方向前进。
猜你喜欢
  • 1970-01-01
  • 2020-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-22
相关资源
最近更新 更多