发送到Google Apps脚本的Google Spreadsheet对象包装中是否存在错误?

时间:2014-12-27 22:26:17

标签: javascript google-apps-script google-sheets spreadsheet google-docs

附件是Test Sheet,显示结果和预期结果。测试表具有运行工作表所需的所有功能。

主函数应该返回从开始日期生成的日期范围。每个日期应比其前一个日期提前三个月。

测试使用两个版本的函数,一个在日期对象上使用UTC日期方法,另一个在日期对象上使用普通日期方法。

"工作簿"中名为 quarter 的工作表有结果。工作表处理测试依赖于这些命名范围 StartDateQ,StartDateWeird

任何寻求解决此问题的人只需查看名为quarter的工作表,包含这些功能的脚本页面以及掌握Google Apps脚本和Google电子表格API的工作知识。

问题

返回日期数组不符合预期。在某些情况下,日期看起来正确,但有些日期有不需要的时间数据,只有选择一个单元格才能看到。在某些情况下,日期不正确,一天休息。

为什么这是一个问题?

在实际的电子表格中,依赖于日期匹配的公式会失败,因为某些可视日期具有关联的实际时间数据。由于时间数据不匹配,即使在视觉上,日期看起来相同,匹配失败。匹配失败打破了工作表。

算法

  1. 如果至少要处理一个开始日期,请使用countAge()获取生成日期的季度数。
  2. 使用电子表格发送的开始日期初始化日期对象。
  3. 创建一个空数组以保存日期以返回电子表格。
  4. 对于季度记录,一次循环一次。
  5. 将最新日期插入阵列。
  6. 将日期移至下一季度,直到没有更多季度可以生成日期。
  7. 将结果返回到电子表格
  8. 正在使用的功能(内部API)

    是(obj,type) //比较对象是否是正确的类型

    countAge(dateString,dateString2) //计算日期之间的年份,即年龄

    测试版本

    quartersUTC(startDate,endDate) //自定义函数从两个日期获取季度日期 - UTC版本

    quartersPlain(startDate,endDate) //自定义函数从两个日期获取季度日期 - 普通版

    评注

    由于测试功能都不依赖于"日期数学" ,而是依赖日期设定者,例如,

    不是这个:

    d = new Date(); d.setMonth(d.getMonth()) + 3;

    但是: if (latestDate.getMonth() == 0) { latestDate.setMonth(3,1) }

    为什么编程逻辑无法产生预期结果似乎并不明显。

    功能

    这是普通版本。 UTC版本使用.setUTCmonth()

    function quartersPlain(startDate,endDate)  {
    
    // Check if there is a date to process, if not, exit.
    
      if (!is(startDate,"Date")) {
          return "Missing date";
      }
    
    
    // Let's get the number of quarters  
    
    
      /* If there isn't an end date, we want the tally from the start date to now, aka the true age
         otherwise, we want want the tally between two dates.
      */
      var tally = 0; 
      if (!is(endDate,"Date")) {
        tally = (countAge(startDate) * 4) + 3
      } 
      else {
    
        if (new Date(endDate) > new Date(startDate)) {
          tally = (countAge(startDate,endDate) * 4)
        }
        else {
          tally = (countAge(endDate,startDate) * 4)
          startDate = endDate
        }
      }
      // testing ...
      // return tally
    
    
    
    /* 
    1. Initialize date object with start date sent from spreadsheet.
    2. Make an empty array to hold dates to return to the spreadsheet.
    3. For the tally of quarters, loop through one count at a time.
    4. Insert the latest date to the array.
    5. Move the date along to the next quarter.
    */
    
      // it's a logical fail needing to assign the date twice, kludgy 
      var latestDate = new Date(startDate);
    
      /* testing ...
      latestDate.setFullYear(latestDate.getUTCFullYear());
      latestDate.setUTCMonth(latestDate.getUTCMonth());
      latestDate.setDate(latestDate.getUTCDate());  
      */
    
      var dates = [ ]; 
    
      for (var loop = 0; loop < tally; loop++) {
    
        dates.push(new Date(latestDate));
    
    
        if (latestDate.getMonth() == 0) {
            latestDate.setMonth(3,1)
        }
        else if (latestDate.getMonth() == 1) {
            latestDate.setMonth(4,1)
        }
        else if (latestDate.getMonth() == 2) {
            latestDate.setMonth(5,1)
        }    
        else if (latestDate.getMonth() == 3) {
            latestDate.setMonth(6,1)
        }    
        else if (latestDate.getMonth() == 4) {
            latestDate.setMonth(7,1)
        }    
        else if (latestDate.getMonth() == 5) {
            latestDate.setMonth(8,1)
        }    
        else if (latestDate.getMonth() == 6) {
            latestDate.setMonth(9,1)
        }    
        else if (latestDate.getMonth() == 7) {
            latestDate.setMonth(10,1)
        }    
        else if (latestDate.getMonth() == 8) {
            latestDate.setMonth(11,1)
        }    
        else if (latestDate.getMonth() == 9) {
            latestDate.setMonth(0,1)
            latestDate.setFullYear(latestDate.getFullYear() + 1)
        }    
        else if (latestDate.getMonth() == 10) {
            latestDate.setMonth(1,1)
            latestDate.setFullYear(latestDate.getFullYear() + 1)
        } 
        else if (latestDate.getMonth() == 11) {
            latestDate.setMonth(2,1)
            latestDate.setFullYear(latestDate.getFullYear() + 1)
        }           
    
      }
    
       return dates;
    
    }
    

    `

1 个答案:

答案 0 :(得分:1)

简短的回答是这是由于夏令时。 : - )

在夏令时的开始,在使用夏令时的时区,比没有夏令时的日期早一个小时。

所以例如在我的timeZone(澳大利亚东部标准时间)[澳大利亚= GMT + 10 / GMT + 11夏令时]当我看到你的&#34;开始季度&#34;日期为1-APR-1947(自1/1/900起为17258天),它在脚本调试器中报告为:

Tue Apr 01 1947 18:00:00 GMT+1000 (AEST)

(建议BTW您当地的TimeZone是GMT-8或美国西海岸,是吗?)

但如果我在夏令时期间将其指向您的某个日期(例如内部的1/1/1960 - 21916),则报告为:

Fri Jan 01 1960 19:00:00 GMT+1100 (AEDT)

这意味着当您使用.setMonth()或.setUTCMonth()计算函数中的新日期时,这些日期是&#34;聪明的&#34;并考虑夏令时..所以有时在你的结果集中你会得到额外一天的.04167(。04167 x 86400秒= 3600秒或1小时)..通过调整日期的格式可以很容易地看到列为&#34;日期时间&#34;

在真实的电子表格中导致问题的简单解决方案是使用= INT()来直接报告DATE组件.. 毕竟,这就是你真正的似乎对此感兴趣。

除了.setMonth()调用之外,您还可以在脚本函数中使用.setHours(0)。

我无法看到任何&#34;约会1天&#34;在您的样本数据中,但我可以通过使用UTC时间产生类似的结果,然后当他们在本地时区显示时我可以看到例如12/31/1949 23:00:00 ...因为在英国冬季澳大利亚有夏天在英国夏季/澳大利亚冬季,以及在(春季和秋季)之间没有夏令时,四分之一开始日期没有时间成分......在白天节省,反之亦然......产生一些有趣的结果:

10/1/1950 0:00:00 12/31/1950 23:00:00 4/1/1951 0:00:00 7/1/1951 1:00:00 10/1/1951 0:00:00

在您的数据中,您还会注意到,在夏令时略有延长的情况下,2007年1月/ 4月的模式没有时间,7月/ 10月的模式发生了变化。 其他有趣的(明显的)异常是因为他们过去常常采用夏令时。

顺便说一句,如果您想让原始代码更紧凑,您可以执行以下操作:

for (var loop = 0; loop < tally; loop++) {
    dates.push(new Date(latestDate));

    var nextMonth = (latestDate.getMonth() + 3) % 12;   // mod 12 
    latestDate.setMonth(nextMonth,1);
    if (nextMonth < 3) {
        latestDate.setFullYear(latestDate.getFullYear() + 1)
    }
 }

哦,最后一件事,我建议您使用Google表格/脚本中的.setMonth()或.setUTCMonth()函数将问题标题更改为&#34;意外时间结果34;因为这更好地描述了IMO的问题。

干杯。