代码之家  ›  专栏  ›  技术社区  ›  nuxie

Snowflake-.toISOString()在JS存储过程中错误地舍入时间戳

  •  1
  • nuxie  · 技术社区  · 1 年前

    我在Snowflake中使用时遇到了一个奇怪的问题 .toISOString() 在JavaScript存储过程中。行为非常不一致,我怀疑这可能是一个错误。

    在某些情况下,当作为存储过程参数传递的时间戳被转换为 .toISOString() ,看起来它被不合理地四舍五入了0.001。以下提供了示例。

    当我打电话时 .toISOString() 在JS本身中,这似乎根本没有发生。

    有人遇到过类似的问题吗?我们将在解决方案中将时间戳缩短为秒,以确保其正常工作,但这不是一个非常可靠的长期解决方案。

    CREATE OR REPLACE PROCEDURE "timestamp_test"("ts" TIMESTAMP_NTZ(9))
    RETURNS VARCHAR(16777216)
    LANGUAGE JAVASCRIPT
    EXECUTE AS CALLER
    AS $$
        return ts.toISOString();
        // return new Date(ts).toISOString();
    $$;
    
    CALL "timestamp_test" ('2024-05-16 10:41:07.57'::TIMESTAMP_NTZ(9)); 
    -- returns: 2024-05-16T10:41:07.569Z
    
    CALL "timestamp_test" ('2024-05-16 10:41:07.578'::TIMESTAMP_NTZ(9));
    -- returns: 2024-05-16T10:41:07.577Z
    
    CALL "timestamp_test" ('2024-05-16 10:41:07.577'::TIMESTAMP_NTZ(9));
    -- returns: 2024-05-16T10:41:07.577Z
    
    CALL "timestamp_test" ('2024-05-16 10:41:07.56'::TIMESTAMP_NTZ(9)); 
    -- returns: 2024-05-16T10:41:07.560Z
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   Mina    1 年前

    发生这种情况是因为 Snowflake s TIMESTAMP_NTZ 具有高达纳秒的精度,但是 JavaScript 的Date对象的精度仅为毫秒,并且在转换时间戳时,这种差异可能导致意外的舍入行为

    一种解决方案可以是在存储过程中手动构造ISO字符串,方法是将时间戳截断为秒,并确保显式包含毫秒

    CREATE OR REPLACE PROCEDURE "timestamp_test"("ts" TIMESTAMP_NTZ(9))
    RETURNS VARCHAR(16777216)
    LANGUAGE JAVASCRIPT
    EXECUTE AS CALLER
    AS $$
        function toIsoStringWithPrecision(date) {
            const pad = (num, size) => ('000' + num).slice(size * -1);
            const isoString = date.getUTCFullYear() +
                '-' + pad(date.getUTCMonth() + 1, 2) +
                '-' + pad(date.getUTCDate(), 2) +
                'T' + pad(date.getUTCHours(), 2) +
                ':' + pad(date.getUTCMinutes(), 2) +
                ':' + pad(date.getUTCSeconds(), 2) +
                '.' + pad(date.getUTCMilliseconds(), 3) + 'Z';
            return isoString;
        }
    
        const date = new Date(ts.getTime());
        return toIsoStringWithPrecision(date);
    $$;
    

    pad 此处确保日期的每个部分都有正确的长度(如有必要,可添加零)
    getUTCMonth 以开头 0 (例如,Jan=0,Fab=1,…等等),这就是我们添加的原因 +1 .