时区偏移不是固定的

问题来源是修复这个bug,Shanghai时区的日期问题

[BugFix] fix timestamp underflow for parquet format by dirtysalt · Pull Request #18521 · StarRocks/starrocks

如果我们预先计算好8个小时的话,那么对于1900年之前的日期,seconds会少计算了343s. 我当时不是特别理解这个问题,所以就直接+343s进行了修正。这个情况对于shanghai时区没有问题,但是对于其他时区就不行了。

我的同事在review里面推荐我看两个帖子:

大致意思就是说,Shanghai在1901-01-01 12:00:00 这个时间点上往回调整了343s. 所以在这个时间点之前的second其实就是减少343s的,但是我们在处理日期转换的时候需要考虑回来,不能直接+8处理完事。

Daylight Saving Time Changes 1900 in Shanghai, Shanghai Municipality, China

所以处理offset的时候,其实还需要传入second绝对值,才能知道具体如何调整。

C++程序可以参考下面的,需要google cctz实现 google/cctz: CCTZ is a C++ library for translating between absolute and civil times using the rules of a time zone.

void TimestampValue::from_unixtime(int64_t second, const cctz::time_zone& ctz) {
    static const cctz::time_point<cctz::sys_seconds> epoch =
            std::chrono::time_point_cast<cctz::sys_seconds>(std::chrono::system_clock::from_time_t(0));
    cctz::time_point<cctz::sys_seconds> t = epoch + cctz::seconds(second);
    const auto tp = cctz::convert(t, ctz);
    from_timestamp(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), 0);
}

Java程序可以参考下面额:

package com.starrocks.lab;


import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList;
import java.util.List;


/*
1900-01-01 00:00:00 ---> -2209017943000000
1910-01-01 00:00:00 ---> -1893484800000000
1920-01-01 00:00:00 ---> -1577952000000000
1930-01-01 00:00:00 ---> -1262332800000000
1940-01-01 00:00:00 ---> -946800000000000
1950-01-01 00:00:00 ---> -631180800000000
1960-01-01 00:00:00 ---> -315648000000000
1970-01-01 00:00:00 ---> -28800000000
1980-01-01 00:00:00 ---> 315504000000000
1990-01-01 00:00:00 ---> 631123200000000
 */
public class TimezoneOffsetTest {
    public static void main(String[] args) {
        List<Long> list = new ArrayList<>();
        list.add(-2209017943L);
        list.add(-1893484800L);
        list.add(-1577952000L);
        list.add(-1262332800L);
        list.add(-946800000L);
        list.add(-631180800L);
        list.add(-315648000L);
        list.add(-28800L);
        list.add(315504000L);
        list.add(631123200L);


        DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
        builder.append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        DateTimeFormatter formatter = builder.toFormatter();


        ZoneId zone = ZoneId.of("Asia/Shanghai");
        for (Long sec : list) {
            LocalDateTime dt = LocalDateTime.ofInstant(Instant.ofEpochSecond(sec, 0), zone);
            System.out.printf("sec = %d, date = %s\n", sec, dt.format(formatter));
        }


    }
}