【问题标题】:Weird time offset dealing with date/time and timezone处理日期/时间和时区的奇怪时间偏移
【发布时间】:2019-07-31 17:56:39
【问题描述】:

我一直在尝试编写一些函数来根据谷歌电子表格上多个单元格的信息创建谷歌日历和谷歌日历事件。

here 发布的带有日期部分的第一个问题已得到解决。

现在我遇到了时间部分的问题。

以下代码:

  var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Passeios");
  var timeStart = ss.getRange(6,4).getValue();
  var timeEnd = ss.getRange(6,5).getValue();
  var ssTZ = SpreadsheetApp.getActive().getSpreadsheetTimeZone();

  Logger.log("timeStart: " + timeStart );
  Logger.log("timeEnd: " + timeEnd);


  var dateStart = ss.getRange(6,8).getValue();

  var dateStartObj = new Date(Utilities.formatDate(dateStart, 'ssTZ' , 'MMMM dd, yyyy HH:mm:ss Z'));

var timeStartObj= new Date(Utilities.formatDate(timeStart, 'ssTZ' , 'MMMM dd, yyyy HH:mm:ss Z'));

var justTimeStart = Utilities.formatDate(timeStart, ssTZ, 'HH:mm');


Logger.log(" Time Start Object: " + timeStartObj);
Logger.log("Time Start Object Hours: " + timeStartObj.getHours());
Logger.log("Time Start Object Minutes: " + timeStartObj.getMinutes());

Logger.log("Start Time HH:mm: " + justTimeStart)

var hourStart = Utilities.formatDate(timeStart, ssTZ, 'HH');
var minutesStart = Utilities.formatDate(timeStart, ssTZ, 'mm');
var hourEnd = Utilities.formatDate(timeEnd, ssTZ, 'HH');
var minutesEnd = Utilities.formatDate(timeEnd, ssTZ, 'mm');


Logger.log(" TimeZone :" + ssTZ);
Logger.log(hourStart);
Logger.log(minutesStart);
Logger.log(hourEnd);
Logger.log(minutesEnd);  

产生以下日志

时间开始:1899 年 12 月 30 日星期六 07:06:28 GMT-0300 (BRT)

时间结束:1899 年 12 月 30 日星期六 07:36:28 GMT-0300 (BRT)

时间开始对象:1899 年 12 月 30 日星期六 07:06:28 GMT-0300 (BRT)

时间开始对象小时:7

时间开始对象分钟:6

开始时间 HH:mm: 07:00

时区:美国/圣保罗

07

00

07

30

电子表格单元格的格式为 HH:mm 并显示 07:00 开始时间 07:30 结束时间

正如您所见,在记录单元格值或使用单元格值构造 Date() 对象时,我不确定从哪里来的 6 分 28 秒的偏移量。

将单元格格式化为仅小时或分钟或 HH:mm 不会带有该偏移量。

编辑。

我注意到 Date() 构造函数在单引号之间有 ssTZ 变量,因此它可能被丢弃,因为它不应该被识别为有效的时区。

不确定它使用的是什么,但实际电子表格时区和错误引用的时区之间的差异似乎是 28 秒,我也不明白它们来自哪里。

6 分钟的偏移量仍然存在,您可以查看以下代码并打印日志。

  var timeStart = ss.getRange(6,4).getValue();
  var ssTZ = SpreadsheetApp.getActive().getSpreadsheetTimeZone();

var timeStartObj1= new Date(Utilities.formatDate(timeStart, 'ssTZ' , 'MMMM dd, yyyy HH:mm:ss Z'));
var timeStartObj2= new Date(Utilities.formatDate(timeStart, ssTZ , 'MMMM dd, yyyy HH:mm:ss Z'));
var justTimeStart = Utilities.formatDate(timeStart, ssTZ, 'HH:mm');

  Logger.log("timeStart: " + timeStart );
  Logger.log(" Time Start Object1: " + timeStartObj1);
  Logger.log(" Time Start Object2: " + timeStartObj2);
  Logger.log("justTimeStart: " + justTimeStart)

时间开始:1899 年 12 月 30 日星期六 07:06:28 GMT-0300 (BRT)

时间开始对象 1:1899 年 12 月 30 日星期六 07:06:28 GMT-0300 (BRT)

时间开始对象 2:1899 年 12 月 30 日星期六 07:06:00 GMT-0300 (BRT)

justTimeStart: 07:00

编辑 2

它与时区有关,并且可能由于日期被解释为 120 年前的 1899 年而进行了一些调整。

在指定格式的字符串中使用没有 Z 的以下构造函数时:

var timeStartObj3= new Date(Utilities.formatDate(timeStart, ssTZ , 'MMMM dd, yyyy HH:mm:ss'));
Logger.log(" Time Start Object3: " + timeStartObj3);

日志结果是:

时间开始对象 3:1899 年 12 月 30 日星期六 07:00:00 GMT-0300 (BRT)

编辑 3.

越来越古怪……

如果我在 Logger.log 调用中使用文本连接,我会得到一个不同的字符串,如果我只使用 var 名称调用日志:

  var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Passeios");
  var timeStart = ss.getRange(6,4).getValue();
  Logger.log("timeStart: " + timeStart);
  Logger.log(timeStart);

时间开始:1899 年 12 月 30 日星期六 07:06:28 GMT-0300 (BRT)

12 月 30 日星期六 07:00:00 GMT-03:06 1899

我确实理解实际上相同的时间以不同的方式表示(我假设即使未显示 28 秒也是如此)。

我的猜测是,无论是否使用连接,文本解析方法的行为都会有所不同(这至少令人困惑)。

我仍然不知道那些 06 分和 28 秒来自哪里,或者在使用来自单元格值的时间和日期时如何确保一致性,而时间和日期来自只有日期或只有时间的单元格值,并且必须混合它们。

这真是令人困惑......

【问题讨论】:

  • 为了确认where those 06 minutes and 28 seconds come from,你能提供一个样本电子表格来复制你的情况吗?在您的问题中,timeStarttimeEndgetValue() 检索到的值。所以首先,我想确认单元格的值。

标签: datetime google-apps-script google-sheets timezone timezone-offset


【解决方案1】:

Google 表格将日期或时间单元格值存储为对应于从 1899 年 12 月 30 日 0:00:00 开始的全天数(或分数)的数字,如 here 所述。

在处理仅包含时间值的单元格时,the getValues() 函数将生成一个 Date 对象,其日期部分设置为 12/30/1899,但这将具有相应的时区偏移量,该偏移量会及时创建该奇数偏移量正如@Matt Johnson 在之前的回答中所解释的那样

最大的问题是这个偏移量对于不同的时区将不一致,因为该日期会为不同的位置/时区和不同的年份产生不同的偏移量,因此您需要解决一大堆可能的情况。

当尝试创建 Date() 对象时,这会产生不同的结果,因为有时偏移量似乎作为时区偏移量传递,而其他时候它是实际时间值的一部分,具体取决于构造函数和文本被解析为的格式,造成了很多混乱,从这段代码中可以看出:

var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Passeios");
var ssTZ = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
var timeStart = ss.getRange(6,4).getValue();

var date1 = new Date(timeStart);
var date2 = new Date(Utilities.formatDate(timeStart, ssTZ , 'MMMM dd, yyyy HH:mm:ss Z'));
var date3 = new Date(Utilities.formatDate(timeStart, ssTZ , 'MMMM dd, yyyy HH:mm:ss'));

var date4 = new Date(Utilities.formatDate(timeStart, ssTZ , "yyyy-MM-dd'T'HH:mm:ss'Z'"));
     
  
Logger.log("date1: " + date1);
Logger.log("date2: " + date2);
Logger.log("date3: " + date3);
Logger.log("date4: " + date4);

制作此日志:

日期 1:1899 年 12 月 30 日星期六 07:06:28 GMT-0300 (BRT)

日期 2:1899 年 12 月 30 日星期六 07:06:00 GMT-0300 (BRT)

日期 3:1899 年 12 月 30 日星期六 07:00:00 GMT-0300 (BRT)

日期 4:1899 年 12 月 30 日星期六 04:00:00 GMT-0300 (BRT)

有时时区偏移量会被完全忽略,其他时候,只有秒部分会被忽略,而且它可能会被解释为完全不同的时区。 (该字符串格式取自formatDate()class documentation example

正如@TheMaster 建议的那样,这可能会在电子表格中解决,在工作表上创建辅助单元格/列以添加更一致的日期部分,但这在动态处理此信息的情况下可能不实用(在我的情况下是组合来自多个关联表单的多个答案的多个 QUERY 结果)。

@TheMaster 还建议的另一种方法可能是使用 getDisplayValues() 并解析文本,但如果工作表中的显示格式发生更改,这可能会产生各种麻烦。

似乎没有确定的答案

我认为解决这个问题的最简单的方法是在代码中使用数值变量,并使用 Utilities.formatDate() 解析小时和分钟的数值,并将 format 属性设置为 'HH' 和 @987654330 @分别。

var hours = Utilities.formatDate(timeStart, ssTZ, 'HH');
var minutes = Utilities.formatDate(timeStart, ssTZ, 'mm');

Logger.log("Hours: "+ hours);
Logger.log("Minutes: "+ minutes);

给出以下结果:

小时:07

分钟:00

这似乎是一致的,它忽略了时区相关的偏移量,并会产生一个简单的小时数和一个简单的分钟数,对应于工作表上显示的值,然后可以一致地用于创建 Date( ) 对象。

请注意,电子表格上的单元格仍需要格式化为日期/时间才能正常工作,但与从 getDisplayValues() 结果解析文本不同,任何日期/时间格式仍可正常工作。

我对秒或毫秒不感兴趣,但我怀疑这些可以用同样的方式解决。

希望这可以帮助其他人。

【讨论】:

  • 您还可以通过编程方式添加固定日期。类似timeStart+new Date().setHours(0,0,0,0)-new Date(1899,11,30)
【解决方案2】:

6 分 28 秒来自圣保罗和 GMT 之间的 Local Mean Time (LMT) 偏移量。 You can see it in the TZDB sources:

# Zone  NAME               STDOFF    RULES   FORMAT   [UNTIL]
Zone    America/Sao_Paulo  -3:06:28  -       LMT      1914
                           -3:00     Brazil  -03/-02  1963 Oct 23  0:00
                           -3:00     1:00    -02      1964
                           -3:00     Brazil  -03/-02

LMT 条目位于第一行。最后一列 (1914) 是“直到”日期 - 意味着在 TZDB 中,LMT 一直使用到 1914 年。之后,应用区域条目中的下一条规则 (-3:00)。

LMT 是根据参考位置的经度和纬度计算得出的。它与当时可能在该地区使用的计时无关。在许多日期较旧的情况下,没有可用的历史信息可以知道这么长时间之前的准确时间。

换句话说,您 1899 年的示例日期来自巴西已知计时实践之前的某个时期,因此改为应用当地平均时间。

使用更现代的日期,按照今天的标准,您应该会得到对您更有意义的结果。

【讨论】:

  • 问题是不是我自己在使用那个旧时间它来自谷歌表格我有很多单元格值要处理,这些只是时间值,格式为 HH:mm,如我理解的表格中所示它被谷歌表格存储为一个数字,对应于一天的小数值 (HH + mm/60)/24 所以 07:00 在内部存储为 0.2916666667 问题是当尝试使用函数来处理这个时与该时间相关联的时间设置为 1899,所以我想我必须找到一种解决方法来解决这个问题,以便能够从中创建一致的 Date 对象
  • 嗯,从概念上讲,您需要应用 some 日期以确定正确的时区偏移量。它可以是一个固定的日期,也可以是“今天”。请记住,虽然巴西不再使用 DST,但它曾经使用过,谁知道将来是否会再次使用。
  • @Pablo 正如马特所说,添加一个日期:A1:[Your Time] 07:00 B1:[Fixed Date]2019-01-01 C1:=A1+B1 应该可以解决您的问题。或者,如果您对解析字符串感到满意,请改用 getDisplayValues()
  • @TheMaster 这涉及几个动态查询和字段,因此在这种情况下方法不实用。这个想法是将来自 1 个单元格的日期部分和来自另一个单元格的时间部分结合起来。因为似乎使用 formatData 作为 HH 和 mm 只是给了我一个没有来自 LMT 的偏移量的普通数字,所以我将使用该方法使用日期字段构建一个 Date 对象,然后在小时、分钟内调用它的 set 方法等。它会起作用,但它绝对不是一个优雅的解决方案。我希望工作表中的 getValues() 方法能够以不同的方式处理仅限时间的单元格。
猜你喜欢
  • 2021-12-19
  • 1970-01-01
  • 2011-05-18
  • 2020-04-12
  • 2015-08-26
  • 2018-03-15
  • 1970-01-01
  • 2011-05-23
  • 2019-03-08
相关资源
最近更新 更多