很抱歉迟到了几个月才入党……
这些答案的问题在于,他们都没有明确表明这是一个BUG。或者,更准确地说,这是谷歌认定的“正确”行为(例如,参见 Issue 1271,没有采取任何行动就关闭了),而这显然是完全错误的做法。
考虑一下。我想将某人的生日放在电子表格中,并且我有一个很大的列表。然后我想读出该值并将其放入一个表格中:
function getAge(ss, index) {
var ages = ss.getRangeByName("birthDates").getValues();
return ages[index];
}
...
app.createLabel(getAge(ss, index));
该表格现在包含错误的出生日期为 4 月至 10 月之间出生的每个人。谷歌已经决定他们实际上是在一天前出生的。或者尝试读出年龄,并将它们放入另一个电子表格中,无需处理,两个电子表格具有相同的时区设置。同样的故事,当人们发邮件告诉我我弄错了他们的生日时,这让我看起来非常愚蠢。
应该这样做:如果有人手动指定一个日期,例如“06/06/1997”,然后用 读取该日期getValue
,他们希望得到“06/06/1997”,而不是前一天。他们真的不希望您尝试第二次猜测他们:“好吧,这个日期恰好是在 DST 期间,所以他们真正希望读回的getValue
是更正到非 DST 的内容,所以我们为什么不减去一个一个小时,并在前一天给他们?”
这很疯狂。如果 Excel 这样做,那将是一个笑柄。请修复它。
编辑
Serge,这很有趣,但是您的电子表格显示了一种解决方法,它提取 TZ,然后根据该 TZ 显示。但这只是避免了开始时这是错误的问题。考虑一下。电子表格几乎总是包含固定日期的周年纪念日——某人的生日、战斗的日期、约会的时间/日期等等。这些独立于 TZ. 如果您在比利时的生日是 1960 年 7 月 6 日,那么如果您碰巧住在加利福尼亚,那也是 7 月 6 日。如果您出生于上午 00:30,您的护照仍显示为 7 月 6 日,即使在 CET 和 PDT 中该时间实际上是 7 月 5 日。对于周年纪念日或约会,没有人在乎时区。在电子表格中,只有一小部分用户关心 TZ。我无法立即想到一个真正重要的用例。
Lotus 1-2-3 做对了。Excel 做对了。日期是一个整数序列号,没有时区信息。将日期从一个电子表格复制到另一个电子表格,它总是正确的。使用 VB/whatever 从电子表格中获取日期,将其放入另一个电子表格中,它总是正确的(我认为)。尽管我不想这么说,Excel 定义了电子表格,而且他们做得很好。除了VB。
在 GAS 中,如果我使用 复制日期getValues
,这是错误的。谷歌对此过于聪明了。好的,有一个复杂的解决方法涉及查找创建电子表格的 TZ,假设您输入的日期实际上是相对于 UTC 的纪元日期,并根据电子表格的当前 TZ 设置显示它(至少,我认为这就是你正在做的)。但那又怎样?而且,它总是有效吗?如果您将电子表格邮寄给住在 PDT 的人怎么办?我相信你可以让它工作,但这没有抓住重点——这只是一种变通方法,我认为它可能很脆弱(请注意,有3 个TZ 设置:Google 帐户设置、工作表设置和脚本设置,你必须考虑所有这些 - 见 ahab')。根本问题是 Google 电子表格存储相对于特定 TZ 的日期(底层格式 ms 是相对于 Unix 纪元吗??)。这是错误的,现在无法修复,并且只是与日期处理有关的一大堆蠕虫的一部分。
作为一个短期修复,我可能会在电子表格中的所有日期上添加 12 小时。作为一个长期解决方案,我可能应该将所有日期作为文本字符串处理,并自己在 JavaScript 中完成。我已经不得不用“整数”毫秒数替换所有经过的时间,以处理与 GAS 日期中丢失的毫秒数有关的问题。继续,说服我我错了。
由 Serge 编辑:
您好,为了便于阅读,我冒昧地在您的编辑中回答(对此感到抱歉)。
我理解你的观点,但我不同意。
您引用的 UI 示例确实需要一种解决方法,因为这是纯 javascript,而不是电子表格日期......也就是说,Excel 电子表格和 Google 电子表格的工作方式相同(第二天是 day+1,时间 = 小数,参考日期 0 = 1899 年 12 月 31 日))只要您留在电子表格世界中,当您退出电子表格并转到 Javascript 时,问题就来了(参考日期 = 01/01/1970,以毫秒为单位)...
如果他们选择不关心 TZ 并在任何时区保持日期“静态”,那将会有很多其他问题:例如,如果你想在比利时的我和华盛顿的奥巴马总统之间在 Skype 上安排约会怎么办? ? 如果我们决定在星期一早上 8 点进行谈话,是在比利时还是在华盛顿早上 8 点?
使用谷歌解决方案,他只需要早起一点,但我们都会在那里,因为日期和时间会显示相同的“时刻”,按照你建议的方式,我们不会有任何交谈的机会(那将是多么可悲^^)。
还有很多情况显然是更好的选择:与日历服务通信也是一个很好的例子。
静态 excel 解决方案效果很好,因为这样的文档没有以相同的方式共享,并且不会将日期转换为 Javascript 日期对象。Excel SS 与计算机用户相关,Google SS 使用时区设置保持与用户计算机的这种关系,因为它(本质上)与用户计算机无关。我几乎没有希望说服你 (;-) 但我希望这至少能让事情变得更清楚......(无论如何我不是谷歌工程师,也无法以任何方式影响他们^^)。
再编辑...
这是一个小测试表,其 TZ 设置设置为 GMT-8,它获取另一个测试表的值 (设置为 GMT+2 比利时冬季时间),并使用这个简单的脚本以正确的方式显示它:
function timeCorrection() {
var thisSheet = SpreadsheetApp.getActiveSheet();
var otherSS = SpreadsheetApp.openById('0AnqSFd3iikE3dENrc2h1M0ktQTlSNWpNUi1TS0lrNlE');
var otherSheet = otherSS.getSheetByName('Sheet1');
var otherValue = otherSheet.getRange(1,1).getValue();
var tz = otherSS.getSpreadsheetTimeZone();
var thisSheetValue = new Date(Utilities.formatDate(otherValue,tz,'MM/dd/yyyy HH:mm:ss'));
thisSheet.getRange(1,1).setValue(thisSheetValue);
}
就这么简单 ;-) ,“外国工作表”的设置被设置为与其脚本设置相同的值,这是在温哥华创建的新创建工作表的默认值。