如何从 Google 电子表格中读取正确的时间/持续时间值

IT技术 javascript time google-apps-script google-sheets
2021-02-11 09:56:41

我试图从时间格式的单元格 (hh:mm:ss) 中获取小时值,值可以更大 24:00:00 例如 20000:00:00 应该给 20000:

桌子:

两个 Google 电子表格

如果您阅读了 E1 的值:

var total = sheet.getRange("E1").getValue();
Logger.log(total);

结果是:

4 月 12 日星期六 07:09:21 GMT+00:09 1902

现在我尝试将其转换为 Date 对象并获取它的 Unix 时间戳:

  var date = new Date(total);
  var milsec = date.getTime();
  Logger.log(Utilities.formatString("%11.6f",milsec));
  var hours = milsec / 1000 / 60 / 60; 
  Logger.log(hours)

1374127872020.000000

381702.1866722222

问题是如何获得 20000 的正确值?

5个回答

扩展 Serge 所做的事情,我编写了一些应该更容易阅读的函数,并考虑到电子表格和脚本之间的时区差异。

function getValueAsSeconds(range) {
  var value = range.getValue();

  // Get the date value in the spreadsheet's timezone.
  var spreadsheetTimezone = range.getSheet().getParent().getSpreadsheetTimeZone();
  var dateString = Utilities.formatDate(value, spreadsheetTimezone, 
      'EEE, d MMM yyyy HH:mm:ss');
  var date = new Date(dateString);

  // Initialize the date of the epoch.
  var epoch = new Date('Dec 30, 1899 00:00:00');

  // Calculate the number of milliseconds between the epoch and the value.
  var diff = date.getTime() - epoch.getTime();

  // Convert the milliseconds to seconds and return.
  return Math.round(diff / 1000);
}

function getValueAsMinutes(range) {
  return getValueAsSeconds(range) / 60;
}

function getValueAsHours(range) {
  return getValueAsMinutes(range) / 60;
}

您可以像这样使用这些函数:

var range = SpreadsheetApp.getActiveSheet().getRange('A1');
Logger.log(getValueAsHours(range));

不用说,从一个范围内获取小时数需要做很多工作。请为问题 402 加星标,这是一项功能请求,可以从单元格中获取文字字符串值。

一个细节:我刚刚注意到,对于像 03:30 这样的“特殊”时区,它不会返回一个整数......在最终返回的值(分钟和小时)上添加一个 Math.round() 可能有用这个特例。毕竟它不会打扰其他情况;-)
2021-03-27 09:56:41
2021-03-28 09:56:41
谢谢 Serge,我调整了逻辑,使其正确处理不是整小时的时区偏移。
2021-03-31 09:56:41
感谢这个不错的小功能,比我的代码方便得多;-)顺便说一句,我在尝试解释时确实混淆了 JS 和 SS 参考,我现在更正了我的解释,希望任何人都能理解......
2021-04-06 09:56:41
谢谢埃里克,帮助完整脚本
2021-04-07 09:56:41

有两个新函数getDisplayValue()getDisplayValues()它们返回日期时间或任何与电子表格上显示的完全相同的内容。此处查看文档

问答是关于床单的。
2021-03-30 09:56:41
谢谢,是的,我需要专门将时间和日期从工作表中提取到文档中。显示值,不会被时区或其他东西弄乱。我现在通过将所有相关文件和脚本中的时区设置为“GMT(无夏令时)”来解决这个问题。
2021-04-10 09:56:41
将此用于 Google Docs 文本文档,我得到“未定义”
2021-04-12 09:56:41

您看到的值 (Sat Apr 12 07:09:21 GMT+00:09 1902) 是 Javascript 标准时间中的等效日期,比参考日期晚 20000 小时。

您应该简单地从结果中删除电子表格参考值以获得您想要的结果。

这段代码可以解决问题:

function getHours(){
  var sh = SpreadsheetApp.getActiveSpreadsheet();
  var cellValue = sh.getRange('E1').getValue();
  var eqDate = new Date(cellValue);// this is the date object corresponding to your cell value in JS standard 
  Logger.log('Cell Date in JS format '+eqDate)
  Logger.log('ref date in JS '+new Date(0,0,0,0,0,0));
  var testOnZero = eqDate.getTime();Logger.log('Use this with a cell value = 0 to check the value to use in the next line of code '+testOnZero);
  var hours = (eqDate.getTime()+ 2.2091616E12 )/3600000 ; // getTime retrieves the value in milliseconds, 2.2091616E12 is the difference between javascript ref and spreadsheet ref. 
  Logger.log('Value in hours with offset correction : '+hours); // show result in hours (obtained by dividing by 3600000)
}

在此处输入图片说明

注意:这段代码只有几个小时,如果你有几分钟和/或几秒钟的时间,那么它也应该被开发来处理……如果你需要它,请告诉我们。


编辑:解释一下......

电子表格使用 12/30/1899 的参考日期,而 Javascript 使用 01/01/1970,这意味着两个参考之间存在 25568 天的差异。所有这些都假设我们在两个系统中使用相同的时区。当我们将电子表格中的日期值转换为 javascript 日期对象时,GAS 引擎会自动添加差异以保持日期之间的一致性。

在这种情况下,我们不想知道某事的真实日期,而是想知道绝对小时值,即“持续时间”,因此我们需要删除 25568 天的偏移量。这是使用getTime()返回从 JS 参考日期计算的毫秒数方法完成的,我们唯一需要知道的是电子表格参考日期的毫秒值,并从实际日期对象中减去该值。然后进行一些数学运算来获得小时而不是毫秒,我们就完成了。

我知道这看起来有点复杂,我不确定我的解释尝试是否真的能澄清问题,但它总是值得尝试,不是吗?

无论如何,只要(如评论中所述)根据电子表格的时区设置调整偏移值,结果就是我们所需要的。当然可以让脚本自动处理,但它会使脚本更复杂,不确定它是否真的有必要。

一个小问题,我得到 19999.0 可能是值 2.2091616E12 取决于电子表格的时区吗?
2021-03-27 09:56:41
我添加了一行,您可以使用小时值 = 0 来获取适合您的 TZ 的常量值,这确实取决于您的电子表格时区
2021-03-31 09:56:41
埃里克的回答要“优雅”得多,我建议您将他的回答标记为最佳答案。
2021-04-01 09:56:41
这个值来自哪里:2.2091616E12?
2021-04-10 09:56:41

对于简单的电子表格,您可以在没有夏令时的情况下将电子表格时区更改为 GMT,并使用以下简短的转换功能:

function durationToSeconds(value) {
  var timezoneName = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
  if (timezoneName != "Etc/GMT") {
    throw new Error("Timezone must be GMT to handle time durations, found " + timezoneName);
  }
  return (Number(value) + 2209161600000) / 1000;
}

Eric Koleda 的回答在很多方面都更为笼统。我写这篇文章时试图了解它如何处理电子表格时区、浏览器时区以及 1900 年阿拉斯加和斯德哥尔摩时区变化的极端情况。

“没有夏令时的格林威治标准时间”——天才。非常感谢你,伙计。
2021-03-18 09:56:41

在某处创建一个持续时间值为“00:00:00”的单元格。此单元格将用作参考。可能是隐藏的单元格,也可能是具有配置值的不同工作表中的单元格。例如如下:

在此处输入图片说明

然后编写一个带有两个参数的函数 - 1)要处理的值,以及 2)“00:00:00”的参考值。例如:

function gethours(val, ref) {

  let dv = new Date(val)
  let dr = new Date(ref)

  return (dv.getTime() - dr.getTime())/(1000*60*60)

}

由于 Sheets 对 Duration 类型所做的任何事情对于两者都是完全相同的,我们现在可以将它们转换为 Dates 并减去,这给出了正确的值。在上面的代码示例中,我使用了 .getTime() 它给出了自 1970 年 1 月 1 日以来的毫秒数,...。

如果我们试图计算值究竟发生了什么,并进行更正,代码就会变得太复杂。

一个警告:如果小时数非常大,比如 200,000:00:00,就会出现大量的小数值,因为天/年不完全是 24 小时/365 天(?在这里推测)。具体来说,200000:00:00 结果是 200,000.16。