Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/465.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何将JS日期(以编程方式)转换为Google工作表序列号,反之亦然?_Javascript_Date_Datetime_Google Sheets_Google Sheets Api - Fatal编程技术网

Javascript 如何将JS日期(以编程方式)转换为Google工作表序列号,反之亦然?

Javascript 如何将JS日期(以编程方式)转换为Google工作表序列号,反之亦然?,javascript,date,datetime,google-sheets,google-sheets-api,Javascript,Date,Datetime,Google Sheets,Google Sheets Api,所以,我有个约会 > new Date() Mon Aug 05 2019 06:55:46 GMT-0700 (Pacific Daylight Time 在Google Sheets API中,默认的dateTime呈现选项是DateTimeRenderOption.SERIAL_NUMBER,根据 指示日期、时间、日期时间和持续时间字段以双倍序列号格式输出,如Lotus1-2-3所推广的。小数点左边数值的整数部分计算自1899年12月30日以来的天数。小数点右边的小数部分将时间计算

所以,我有个约会

> new Date()
Mon Aug 05 2019 06:55:46 GMT-0700 (Pacific Daylight Time
在Google Sheets API中,默认的dateTime呈现选项是DateTimeRenderOption.SERIAL_NUMBER,根据

指示日期、时间、日期时间和持续时间字段以双倍序列号格式输出,如Lotus1-2-3所推广的。小数点左边数值的整数部分计算自1899年12月30日以来的天数。小数点右边的小数部分将时间计算为一天的小数部分。例如,1900年1月1日中午的时间是2.5,2是因为它是1899年12月30日之后的2天,0.5是因为中午是半天。1900年2月1日下午3点将是33.625。这正确地将1900年视为非闰年


我想知道如何使用API/library将JS日期转换为序列号,并将序列号转换为JS日期对象?

这将获得序列号。您可能想也可能不想修剪它

function createSerialNum() {
    var oneDay = 24 * 60 * 60 * 1000;
    var firstDate = new Date(1899, 11, 30);
    var secondDate = new Date();
    console.log(secondDate);
    var secondDateMidnight = new Date(secondDate.getFullYear(), secondDate.getMonth(), secondDate.getDate());
    var diff = secondDate.getTime() - secondDateMidnight.getTime();
    var left = Math.round(Math.abs((firstDate.getTime() - secondDate.getTime()) / (oneDay))) - 1;
    var right = diff / oneDay;
    var result = left + right;
    console.log(result);
    return result;
}
这会把它变成一个约会

function createDateFromSerial(serialNum){
    serialNum = String(serialNum).split(".");
    var ogDate;
    var oneDay = 24 * 60 * 60 * 1000;
    var firstDate = new Date(1899, 11, 30);  
    var days = serialNum[0];
    var ms = serialNum[1] * oneDay;
    ms = String(ms).substring(0, 8);

    firstDate.setDate(days);

    ogDate = new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate(), 0, 0, 0, ms);
    console.log(ogDate);
    return ogDate;
}

这方面的算法与在摄氏度和华氏度之间转换的算法非常相似,您可能还不太熟悉

JavaScript日期使用的刻度与您描述的Google Sheets的刻度有三个主要区别: 1 JS日期基于毫秒,而Google Sheets日期基于天, 2 JS日期采用1970年1月1日UTC上午12:00:00等于零,而Google Sheets日期采用1900年12月30日,以及 3 Google Sheets中的日期和时间构造函数期望其输入的时区与UTC相匹配,而JavaScript的日期构造函数使用脚本运行的任何位置的时区

为了在这两种格式之间转换,以及调整时区处理,我们需要使用乘法/除法将刻度从天扩展到毫秒,并且我们还需要使用加法/减法将刻度移动,以解释“零”的不同解释

这详细说明了如何执行此操作: 注意,除了这里显示的一个示例值之外,这个脚本还没有使用GoogleSheets进行测试

//定义日期的常量(以毫秒为单位) 每分钟常量毫秒=60000;//假设此时没有闰年调整 每日施工成本=86400000;//假设今天没有闰年调整 每70年2天的施工成本=2209161599801;//gSheets'和unix's zero之间的差异 //定义用于构建JS UTC日期对象的函数 /*第一个函数生成一个初始日期对象,计算差值 在用户的时区和UTC之间,并使用它来创建最终日期对象*/ 函数newUTCDateFromComponentsYYYY,MM,DD=1,hh=0,MM=0,ss=0{ //接受2到6个参数作为日期的组成部分,返回UTC日期对象 const date=新日期yyyy,MM,DD,hh,MM,ss; 常量偏移毫秒=date.getTimezoneOffset*毫秒/分钟; 返回新的Datedate.getTime-偏移毫秒; } 函数newutcDateFromTimestamp{ //获取时间戳,返回UTC日期对象 const date=新的日期时间戳; 返回日期; } //定义在JavaScript时间戳和gSheets日期之间转换的函数 函数gsheetsDateToJsTimestampdays{ const jsTimestamp=days*MS_/u DAY-MS_/u 70年2天; 返回时间戳; } 函数jsUTCTimestampToGsheetsDatemillisecs{ 设天数=毫秒+毫秒/每70年/每2天/毫秒/每1天; days=Math.ceildays*100000/100000;//将结果四舍五入到5位 返程天数; } // //例子 // //JS到gSheets const jsUTCDateSource=newutcdatefrommcomponents 2019,0,1,16,20;//2019年1月1日下午4:20 const jsUTCDateStringSource=jsUTCDateSource.toutstring; const jsUTCMillisecSource=jsUTCDateSource.getTime; const gsheetsDateResult=jsUTCTimestampToGsheetsDatejsUTCMillisecSource; console.log`${jsUTCDateStringSource} 从1970年1月1日开始的JS毫秒:${jsUTCMillisecSource} 从1899年12月30日开始的几天里,谷歌就开始了:${gsheetsDateResult}`; console.log`---`; //gsheetstojs 常量gsheetsDateSource=43466.68056;//2019年1月1日下午4:20 const jsMillisecsResult=gsheetsDateToJsTimestampgsheetsDateSource; const jsUTCDateResult=newutcdatefromtimstampjsmillisesresult; const jsUTCMillisecsResult=jsUTCDateResult.getTime; const jsUTCDateStringResult=jsUTCDateResult.toutstring; console.log`from Google Sheets:${gsheetsDateSource} 到JS:${jsUTCMillisecsResult}//off几秒钟,见下文 可读字符串:${jsUTCDateStringResult}`; console.log`注意:由于四舍五入,结果相差了几分之一秒 但计算结果与人类可读的字符串相同 日期到序列号:Date.getTime-新日期1899,11,30.getTime/24*60*60*1000

迄今为止的序列号:新日期序列号*24*60*60*1000+新日期1899、11、30 et转换首先是关于序列号格式的一些注释

串行格式

如文档中所述,序列号由两部分组成。小数点前的整数表示1899年12月30日起的天数,分数表示一天中的时间,不带时区。也就是说,0.5始终表示中午12:00的时间,而不管用户机器或工作表设置的时区、夏令时或其他任何情况

时区:

序列号没有时区。这意味着无论工作表的时区如何,单元格值0将始终呈现为12/30/1899 0:00:00。 工作表的时区仅影响“现在”或“今天”等函数,更改时区将更改其数值输出,而不会更改时间函数的数值输出

例如:

将单元格设置为formula=NOW,然后将单元格格式化为数字进行渲染。 将工作表的时区更改为与以前不同的时区+6小时。 对图纸进行更改,以便重新计算“现在”值。 请注意,数值将改变约+0.25,以表示6小时的差异。 还请注意,没有其他日期时间单元格会因时区更改而更改其显示。 通过获取数据时,默认值RenderOption为 ValueRenderOption.FORMATTED_值,这意味着忽略dateTimeRenderOption.SERIAL_编号的默认dateTimeRenderOption

格式化

日期时间格式的单元格将根据电子表格的区域设置采用区域设置格式,而不是用户机器的区域设置,例如9/12/2020 8:15:40或12.9.2020 8:15:40(如果电子表格设置为德语)。如果您对日期所做的只是显示它,并且您不关心可能存在区域设置不匹配,那么这是可以的。请注意,在此字符串上使用Date.parse是不正确的,因为如果电子表格区域设置不是英语,则该值将不一致

如果将valueRenderOption更改为valueRenderOption.UNFORMATTED_值,我们将得到日期的序列号值,我将在下面给出公式

书写

要使用JS日期写入工作表,我们不必将其转换为lotus串行格式。我们可以使用以下公式写入单元格:

`=日期${DATE.getFullYear},${DATE.getMonth+1},${DATE.getDate}+时间${DATE.getHours},${DATE.getMinutes},${DATE.getSeconds}`

这将有一个类似于:=DATE2020,9,12+TIME9,15,41的公式输出 该单元格将渲染为9/12/2020 9:15:41,不考虑时区

在该示例中,单元格是用日期对象的本地时间写入的。我们可以通过以下方式将其写入日期对象的UTC时间:

`=日期${DATE.getUTCFullYear},${DATE.getUTCMonth+1},${DATE.getUTCDate}+时间${DATE.getUTCHours},${DATE.getUTCMinutes},${DATE.getUTCSeconds}`

您使用哪一个取决于您的用例。使用本地时间意味着,如果本地时间为3:15,单元格将在图纸中呈现预期为3:15的效果。 使用UTC时间意味着,如果本地时间是3:15,而您的时区是-600GMT,则单元格将显示为9:15,这可能会增加混淆。 但是,如果您发现两个日期之间存在差异,则使用当地时间可能是一个问题。以夏令时为例。例如,如果您所在的美国/芝加哥时区的夏令时截止于2020年11月1日凌晨2点,则凌晨2点和凌晨1点之间的本地时差为2小时

新日期2020,10,1,2-新日期2020,10,1,1//7200000 2小时注释JS月为0索引

但在工作表中,并没有夏令时,所以无论电子表格设置的时区如何,减去这两个日期都是1小时

因此,我们有几个选择:

将纸张日期时间值视为UTC时间。 优点:时代是明确的。应用程序可以在用户时区中呈现时间戳,并另存为UTC。 缺点:直接编辑工作表可能会令人困惑,因为您需要手动将本地时间转换为UTC。无法使用NOW/TODAY功能。 考虑纸张值没有时区。 优点:时代用现在和今天的方法。直接编辑工作表时,时间始终作为用户的本地时间输入。 缺点:时区信息丢失。计算时间差时,夏令时更改可能会导致计算关闭一小时。 考虑纸张值与设置纸张的时区相对应。 优点:时代是明确的。《时代周刊》采用“现在”和“今天”的方法。 缺点:工作表时区始终是标准时间,因此直接编辑工作表与1的问题相同。在工作表的标准时间和用户的本地时间之间转换很困难,需要外部库。 无论我们选择哪种方法,我们都将从相同的公式开始

最后,代码

请注意,这与Jeremy Ring的答案非常相似,只是这将处理DST更改

/**
 * Converts a Lotus serial format to a JS UTC date.
 * @param serial {number}
 * @return {Date}
 */
function serialToDateUTC(serial) {
  const intPart = Math.floor(serial);
  const fracPart = serial - intPart;
  const time = Math.round(fracPart * DAY_TO_MS);
  const date = new Date(Date.UTC(1899, 11, 30 + intPart) + time);
  return date;
}

/**
 * Converts a JS Date to Lotus serial format.
 * @param date {Date}
 * @return {number}
 */
function utcDateToSerial(date) {
  const dateSansTime = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
  const intPart = Math.round((dateSansTime.getTime() - Date.UTC(1899, 11, 30)) / DAY_TO_MS);
  const fracPart = (date.getTime() - dateSansTime.getTime()) / DAY_TO_MS;
  return intPart + fracPart;
}
要显示UTC日期,请将date.toutString或Intl.DateTimeFormat与 时区设置。例如:

console.logserialToDateUTC44086.344212963.ToutString;//2020年9月12日8:15:40

选择1 如果我们希望在工作表中存储为UTC,并在本地时间向用户显示,我们将使用这些UTC转换读取/写入工作表,然后通过Date.toLocaleString或Intl.DateTimeFormat在本地显示 请注意,当直接在Google文档中编辑时间时,此选项可能会令人困惑;需要在UTC时区中输入时间

选择2 如果我们忽略时区,这意味着我们要考虑工作表中的日期/时间值始终是用户的本地值,而不考虑其时区。 对于这一点,如果我们有一个当地时间为4:24的日期,我们希望使用一个公式将4:24存储在表单中,这就是简单的=TIME4,24,0。 为此,我们可以使用前面发布的相同方法,但强制/假装本地时间是UTC时间。例如

const timestamp=新日期;新建DateDate.UTCtimestamp.getFullHours、timestamp.getMonth、timestamp.getDate、timestamp.getHours、timestamp.getMinutes、timestamp.getSeconds

<>这会使时间差异不稳定——考虑一个时间表应用程序,在那里你通过Date.now或新日期占用一个开始时间和结束时间,并且有一个带有公式结束时间减去开始时间的持续时间单元。如果我们跨越时间的变化,那么将其强制到标准时间可能会损失或增加一个小时的时间差

选择3 处理工作表时区本地的所有时间。 这需要我们获得两条信息。单元格数据和工作表的时区。JavaScript本机无法在另一个时区中创建日期,也无法从任意时区获取时区偏移量。为此,我们需要一个外部库,例如。
使用Google files.Get api获取时区,然后从国家和时区查找中获取dstOffset而不是utc偏移。使用该偏移量可从本地时区的Date.getTimezoneOffset调整工作表的时区。是的,有点粗糙。

我一直在谷歌工作表API中使用这个。对我来说很好

// Hour format: HH:MM
// Date format: YYYY-MM-DD
function serialDate(hour, date) {

  let leftDate = date;
  leftDate = new Date(leftDate);
  leftDate = leftDate.getTime();

  let serialNumberReference = (new Date('1899-12-30')).getTime();

  leftDate = (leftDate - serialNumberReference) / (3600 * 24 * 1000);

  let rightDate = date;
  rightDate = (new Date(rightDate + ' ' + hour)).getTime() - (new Date(rightDate + ' 00:00').getTime());
  rightDate = rightDate / (3600 * 24 * 1000);

  return leftDate + rightDate;
}

请让我们知道您的功能结果如何,以及我们的回答是否正确helpful@Cat,我一定会在本周试用并发布更新。谢谢大家的帮助。这是一天的休息,而不是3月4日。返回的数字代表3月3日。在left的定义中,不需要减去1: