我的工作表的一列中有一堆 ISO-8601 格式的字符串。 如何让谷歌表格将它们视为日期,以便我可以对它们进行数学运算(例如,两个单元格之间的分钟差)? 我只是尝试了
=Date("2015-05-27T01:15:00.000Z")
,但没有乐趣。 必须有一种简单的方法来做到这一点。 有什么建议吗?
要获取实际的日期值,您可以使用正常的数字格式对其进行格式化...
=DATEVALUE(MID(A1,1,10)) + TIMEVALUE(MID(A1,12,8))
例如。
A | B | |
---|---|---|
1 | 2016-02-22T05:03:21Z | 2016 年 2 月 22 日 5:03:21 上午 |
DATEVALUE()
函数将格式化的日期字符串转换为值,TIMEVALUE()
对时间执行相同的操作。在大多数电子表格中,日期和时间都用数字表示,其中整数部分是自 1900 年 1 月 1 日以来的天数,小数部分是当天的时间分数。例如,2009 年 6 月 11 日 17:30 约为 39975.72917。
上面的公式将日期部分和时间部分分别解析,然后将它们相加。
我发现使用起来更简单
=SUM(SPLIT(A2,"TZ"))
格式化
yyyy-MM-dd HH:mm:ss.000
即可再次看到日期值为 ISO-8601。
试试这个
=CONCATENATE(TEXT(INDEX(SPLIT(SUBSTITUTE(A1,"Z",""),"T"),1),"yyyy-mm-dd")," ",TEXT(INDEX(SPLIT(SUBSTITUTE(A1,"Z",""),"T"),2),"hh:mm:ss"))
其中 A1 可以是带有 ISO-8601 格式字符串的单元格或字符串本身。
打开 Apps 脚本并将此自定义函数粘贴到编辑器中。
function isoDate(isoDateString) {
return new Date(isoDateString)
}
将其与如下公式一起使用:
=isoDate("2023-01-06T22:37:12Z")
结果将在表格中显示为日期。
我发现使用正则表达式替换是一种更简单、干净且可读的方法:
=regexreplace(A1, "T|Z", " ") + B1
A | B | C | D | |
---|---|---|---|---|
1 | 2023-04-17T16:29:20.085Z | -3:00:00 | 1080805:29:20 | 2023年4月17日13:29:20 |
A1
是要格式化的值;B1
是您所在地区的时区差异;C1
有公式且无格式;D1
具有相同的公式,格式为 Date Time
。正则表达式用空格替换字母 T 和 Z。
+ B1
部分将添加时差以获得您所在时区的最终值。
如果您愿意,您可以在公式中对其进行硬编码,而不是使用单元格。
=regexreplace(A1, "T|Z", " ") + "-3:00"
或者,如果您不想在公式中得到这种差异,您可以删除
+ B1
部分
=regexreplace(A1, "T|Z", " ")
这是一个简单的版本,它将 T 替换为空格,然后采用前 19 个字符来排除毫秒和尾随 Z,最后使用 -- 将其强制转换为正确的日期/时间值。
确保将该列转换为日期/时间而不是数字或类似内容,以便正确显示:)
=--LEFT(SUBSTITUTE(A1,"T"," "),19)
我使用以下 Apps 脚本函数将 ISO8601 时间戳转换为相应电子表格时区中的串行日期时间:
/** JS Date to Excel DateTime (AKA SERIAL_NUMBER date).
*
* @param date A JavaScript date object, possibly representing a datetime like 2022-11-11T15:24:00.000Z..
* @param timeZoneOffsetMillies The time zone offset of the target serial date time (usually the one of the sheet).
*
* @return A Excel serial date like 44876.641666666605.
*/
const dateToSerialDateTime = function(date, timeZoneOffsetMillies) {
/** Milliseconds per day (24 * 60 * 60 * 1000). */
const MILLISECONDS_PER_DAY = 86400000;
const timeZoneOffsetDays = timeZoneOffsetMillies / MILLISECONDS_PER_DAY;
return ((date.getTime() / MILLISECONDS_PER_DAY) + 25569) + timeZoneOffsetDays; // 1970-01-01 - 1900-01-01 = 25569
};
/** Get the the timezone offset in milliseconds.
*
* @param timeZone The time zone in text format, ie. "Europe/Paris"
* @return {number} Time zone offset in milliseconds.
*/
const getTimeZoneOffset = function(timeZone) {
const strOffset = Utilities.formatDate(new Date(), timeZone, "Z");
const offsetSeconds = ((+(strOffset.substring(0, 3))) * 3600) + ((+strOffset.substring(3)) * 60);
return offsetSeconds * 1000;
}
/**
* Returns the current sheets timezone.
*
* @return The current sheets timezone in IANA time zone database name format (ie Europe/Berlin).
* @customfunction
*/
function TIMEZONE() {
return SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone();
}
/**
* Returns the current sheets timezone offset in milliseconds.
*
* @return The current sheets timezone offset in milliseconds.
* @customfunction
*/
function TIMEZONE_OFFSET() {
const tz = TIMEZONE();
return tz != null ? getTimeZoneOffset(tz) : null;
}
/**
* Convert ISO8601 timestamp strings (ie. 2022-11-22T14:47:01+0100) to a Sheets serial datetime.
*
* @param {string|Array<Array<string>>} input Input ISO8601 date string to parse.
*
* @return {number} The native sheets "serial datetime" as double (format the field as Number->Date Time manually).
* @customfunction
*/
function PARSE_ISO8601(input) {
const tzOffsetMillies = TIMEZONE_OFFSET();
const parseIso8601 = ts => ts ? dateToSerialDateTime(new Date(ts), tzOffsetMillies) : null;
return Array.isArray(input) ? input.map(row => row.map(field => parseIso8601(field))) : parseIso8601(input);
}