在Javascript中计算2个SQLite日期时间之间的差异

3
我有一个存储在 SQLite 数据库中的 startDate 和 endDate,需要使用 JavaScript 计算出两个日期时间之间的分钟和秒数差异。
例如:
startDate = 2012-10-07 11:01:13
endDate = 2012-10-07 12:42:13

我在SO上阅读了许多类似的问题,但只找到了与计算选择部分相关的内容。


你是否确实需要“小时和秒”这种格式?而不是更常见的“小时,分钟,秒”格式? - manzoid
是的,抱歉打错了 - 应该是分钟和秒。 - Jimothey
4个回答

2

将字符串转换为JS Date 对象,减去日期,并使用一些算术运算从结果中计算小时/秒。类似这样:

function convertMS(ms) {
  var d, h, m, s, ts, tm, th;
  s = ts = Math.floor(ms / 1000);
  m = tm = Math.floor(s / 60);
  s = s % 60;
  h = th = Math.floor(m / 60);
  m = m % 60;
  d = Math.floor(h / 24);
  h = h % 24;
  return { d: d, h: h, m: m, s: s, tm: tm, th: th, ts: ts};
};
var start = new Date('2012-10-07 11:01:13'.split('-').join('/'))
   ,end   = new Date('2012-10-07 12:42:13'.split('-').join('/'))
   ,dif   = convertMS(end - start);
console.log(dif.h+':'+dif.m);​​​​​​ //=> ​1:41
console.log('total minutes dif: '+dif.tm);​​​​​​ //=> total ​minutes dif: 101
console.log('total seconds dif: '+dif.ts);​​​​​​ //=> ​total seconds dif: 6060

[基于评论的编辑]
在提供的格式中,手动解析日期字符串:

Date.tryParse = function(ds){
  var  arr =  ds.match(/\d+/g)
      ,err = 'Expected at least yyyy'
      ,dat = arr.length && String(arr[0]).length === 4
              ? doParse.apply(null,arr) : err;
  return dat;

  function doParse(y,m,d,h,mi,s,ms){
   var dat = new Date(+y,(+m-1)||0,+d||1,+h||0,+mi||0,+s||0,+ms||0);
   return isNaN(dat) ? new Date : dat;
  }
}
var start = Date.tryParse('2012-10-07 11:01:13'); //=> Sun Oct 07 2012 11:01:13
var test  = Date.tryParse('2009');                //=> Sun Jan 01 2009 00:00:00

OP的日期字符串不符合ES5中Date.parse指定的格式。最好手动解析该字符串。 - RobG
据我所知,OP正在询问将总时间差转换为分钟和秒数,而不仅仅是将时间差转换成天、小时、分钟和秒后的分钟和秒部分,这就是你目前回答所提供的内容。换句话说,如果两个时间相差3天8小时3分钟15秒,你的代码片段将给出"3分钟和15秒"作为时间差。 - manzoid
非常感谢大家提供的详细答案。由于这个回答似乎是最深入和详细的,我已经开始将其添加到我的代码中进行测试。再次感谢你们。 - Jimothey

1

关于将日期转换为纪元时间的毫秒数,减去这些毫秒数,并将结果从毫秒转换回所需的单位,所有答案通常都是正确的(尽管其他地方提供的具体实现目前并不能完全满足您的要求 - 即仅在两个日期时间之间给出分钟和秒数的数量)。

但是,请注意...

new Date('2012-10-07 12:42:13')

... 这不是一种可靠的方法来构造来自SQLite日期字符串的日期。

当您将字符串输入Date对象的构造函数时,实际上是调用了Date.parse()。它在不同的浏览器上的行为是不同的。

看看这个:

> new Date('1-1-2012');
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)

> new Date('01-01-2012');
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)

> new Date('2012-1-1');
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)

看起来很不错,对吧?但这是在Chrome上。

现在看看在最新版本的Firefox中会发生什么,使用完全相同的调用:

> new Date('1-1-2012');
Date {Invalid Date}

> new Date('01-01-2012');
Date {Invalid Date}

> new Date('2012-1-1');
Date {Invalid Date}

> new Date('2012-01-01');
Date {Sat Dec 31 2011 16:00:00 GMT-0800 (PST)}

此外,请看看这个行为,在两个浏览器中:
> new Date('2012-01-01');
Sat Dec 31 2011 16:00:00 GMT-0800 (PST)

在月份和日期数字前简单地添加零会导致时间错乱!您必须设置时间和时区(对我来说是PST)才能解决这个问题:

> new Date('2012-01-01T00:00:00-08:00')
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)

基本上,处理日期字符串解析是非常头疼的。你不想去消化和考虑像this, this, 和 this这样的规格。

所以,这里有一个更好的选择 - 将您的日期时间部分作为单独的参数传递给Date对象的构造函数。 这将可靠地为您创建日期,使得您的后续比较是有效的。

new Date(year, month, day [, hour, minute, second, millisecond])

以下是您的情况可能看起来的初始化:

// Extract month, day, year from SQLite format, 'trimming' whitespace.
var sqlLiteSampleStr = "2012-10-07 11:01:13";
var re = /^\s*(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\s*$/;
var match = re.exec(sqlLiteSampleStr);
if (match) {
  var year = parseInt(match[1]);
  var month = parseInt(match[2]) - 1; // Zero-indexed months.
  var date = parseInt(match[3]);
  var hour = parseInt(match[4]);
  var minute = parseInt(match[5]);
  var second = parseInt(match[6]);
  var date = new Date(year, month, date, hour, minute, second);
}

注意时区的考虑。在那个SQLite格式片段中,似乎没有任何时区数据,所以要小心。
更新:
@james-j澄清了他特别寻找分钟和秒数。
以下是提取分钟和秒数的代码片段:
var msPerMin = 1000 * 60;
var min = Math.floor(timeInMs / msPerMin);
var timeInMs = timeInMs - (min * msPerMin);
var sec = Math.floor(timeInMs / 1000 );

非常感谢您提供如此深入的答案。 - Jimothey

1

其他答案都是正确的,但浏览器并不总是解析日期字符串(参见下文),因此您应该手动解析字符串。 OP中的格式与ES5中的格式不一致,并且某些浏览器将无法正确解析它们。将OP格式转换为在所有浏览器中均可使用的日期的简单函数是:

function stringToDate(s) { 
    s = s.split(/[-\/\. :]/);
    return new Date(s[0], --s[1], s[2], s[3], s[4], s[5], 0)
}

有一些格式被所有浏览器解析,但它们不符合标准,如果使用,则依赖于所有实现继续支持未指定的格式。

编辑

为了容忍畸形输入(额外的空格,不同的分隔符,额外的字符),可以使用match而不是split

function stringToDate(s) { 
    s = s.match(/\d+/g);
    return new Date(s[0], --s[1], s[2], s[3], s[4], s[5], 0)
}

然而,由于这些值来自数据库并且已经指定了格式,日期字符串应该符合格式。错误的数据不应该首先存储。

为了完整起见,这里有一个函数可以实现您想要的功能:

/*  Return difference between two dates as minutes and seconds mm:ss.
 *  Input format is yyyy-mm-dd hh:mm:ss
 *  If strings don't converted to dates (incorrect format or invalid values),
 *  return undefined.
 */
function diffInMins(s0, s1) {
  var diff;

  // Convert strings to date ojbects
  var d0 = stringToDate(s0);
  var d1 = stringToDate(s1);

  // Helper function to padd with leading zero
  function z(n) {
    return (n<10? '0' : '') + n;
  }

  // Check conversions
  if (d0 && d1 && typeof d0 == 'object' && typeof d1 == 'object') {

    // Return difference formatted as mm:ss
    if (d0 && d1) {
      diff = d1 - d0;
      return z(diff/60000 | 0) + ':' + z(Math.round(diff%60000/1000));
    }
  }
}

这个例子有点偏差。如果有前导空格,拆分数组中会有额外的元素,从而影响结果日期值。月份值也存在一个偏移量为一的问题(零索引月份)。 - manzoid
@manzoid - 当然,你需要预处理用户输入以检查格式,去除额外的空格等等,这只需要一行代码,并且无论你打算如何解析字符串,都必须完成。月份索引已经考虑在内,注意 --s[1] - RobG
没错,只是一些小问题,因为展示健壮的解决方案很重要。你对各种评论提出的修改很好。而且你是对的,在那里使用了--运算符。再次出于健壮性的考虑,如果表示为“-1”,可能更容易阅读。类似的可读性注释——最好避免位或操作来转换为数字,而是选择更自描述的技术。无论如何,OP应该点赞这些周到的回答之一,但我将从你的回答开始点赞。干杯—— - manzoid

0

JavaScript实际上使得日期相减变得非常简单。以下是它的工作原理:

// Create Date objects from the strings
startDate = new Date('2012-10-07 11:01:13');
endDate = new Date('2012-10-07 12:42:13');

// Subtract Date objects to return milliseconds between them
millisecondsBetween = (endDate - startDate);

// Convert to whatever you like
alert(millisecondsBetween/1000/60 + ' minutes');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接