CST / CDT时区变更问题

时间:2017-11-22 12:33:52

标签: java oracle date timezone timestamp

我们使用服务器默认时区CST以'22 -NOV-17 05.33.51.937000000 PM'格式存储时间。在很多地方我们有一半的时间比较。因此,CST到CDT和CDT到CST都面临着问题,因为在数据库检索时我们无法识别时区。所以它打破了我们在CST与CDT和CDT到CST时间变化的时间比较。

我们无法改变存储逻辑,例如存储时区和以UTC时区存储,因为它会在很多地方破坏我们现有的逻辑。

有没有办法识别日期时区,如CST或CDT,以'22 -NOV-17 05.33.51.937000000 PM'格式存储在数据库中。

2 个答案:

答案 0 :(得分:3)

  

我们将时间存储在' 22-NOV-17 05.33.51.937000000 PM'格式

对您的评论没有意义

  

我们将数据存储为时间戳

在Oracle数据库中,TIMESTAMP没有格式 - 它以11个字节的形式存储在数据库中,表示年份(2个字节),月,日,小时,分钟,秒(每个1个字节)和小数秒(4个字节)。只有当您使用的任何接口(SQL / Plus,SQL Developer,Toad,Java,PHP等)与数据库通信时,才决定向用户显示该接口将其格式化为字符串(但是数据库只会将其保留为不带任何格式的字节)。

假设您使用的是SQL / Plus或SQL Developer,则可以使用以下命令找到默认格式:

SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_TIMESTAMP_FORMAT';

使用以下命令更改默认格式:

ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SSXFF9';

TIMESTAMP WITH TIME ZONE

ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SSXFF9 TZR';
  

那么有没有办法识别日期时区,如CST或CDT,存储在数据库中! 22-NOV-17 05.33.51.937000000 PM'格式。

不,没有任何其他元数据可以识别时间戳的来源并指出它来自哪个位置(即是否有另一列链接到输入数据的用户可以映射到物理位置和所以一个时区)那么就无法确定它来自哪个时区。

您需要:

  • 将您的数据库列更改为TIMESTAMP WITH TIME ZONE并存储时区;或
  • 在存储时将所有值转换为相同的时区。

答案 1 :(得分:1)

我假设CST和CDT你指的是北美中央标准时间和中部夏令时,例如在Rainy River,Chicago和Mexico(城市)等地观察到的。稍后将更多关于这种模糊性。

对于99.977%的所有时间,很容易知道它们是标准时间还是夏令时。从DST到标准时间过渡的两个小时内,只有两次是模棱两可的,正如评论中所述,没有办法从时间戳中知道解决这种歧义的正确方法。

<强> java.time

这个答案将带您尽可能远离Java 7.您仍然可以使用java.time,即现代Java日期和时间API,也称为JSR-310。它已被移植到ThreeTen Backport中的Java 6和7,因此需要将其添加到项目中(直到有一天升级到Java 8或更高版本)。

我接受你的日期时间字符串格式。我们可以做什么:

    DateTimeFormatter storedFormatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("d-MMM-uu hh.mm.ss.SSSSSSSSS a")
            .toFormatter(Locale.US);
    ZoneId zone = ZoneId.of("America/Mexico_City");

    String storedTime = "22-NOV-17 05.33.51.937000000 PM";
    LocalDateTime dateTime = LocalDateTime.parse(storedTime, storedFormatter);
    // First shot -- will usually be correct
    ZonedDateTime firstShot = ZonedDateTime.of(dateTime, zone);
    System.out.println(firstShot);

打印:

2017-11-22T17:33:51.937-06:00[America/Mexico_City]

你可以看到它选择了-06:00的偏移量,这意味着时间是标准时间(CDT是-05:00)。

由于您的月份缩写全部为大写,我需要告诉格式化程序不敏感地解析大小写。如果America / Mexico_City时区不适合您,请选择更好的时区,例如America / Rainy_River或America / Chicago。

秋季不明确的时间

我曾经不得不解析一个包含日期时间的日志文件而没有指示标准时间和夏令时(DST)。由于我们假设时间总是向前推进,我们在转换到标准时间时失败了,并且丢失了一小时的日志文件。在这种情况下,我们可能已经使用夏季时间的信息解决了这个问题,直到向后跳跃一小时,从标准时间开始。您可能想要考虑一下类似的东西是否可行。

其他选项包括每次只需要花费DST时间 - 这就是上面的代码所做的 - 或者采用平均值并与之引入的错误一起生效。

我们至少可以发现模棱两可的时间:

    ZoneOffset standardOffset = ZoneOffset.ofHours(-6);
    ZoneOffset dstOffset = ZoneOffset.ofHours(-5);
    // check if in fall overlap
    ZonedDateTime standardDateTime 
            = ZonedDateTime.ofLocal(dateTime, zone, standardOffset);
    ZonedDateTime dstDateTime 
            = ZonedDateTime.ofLocal(dateTime, zone, dstOffset);
    if (! standardDateTime.equals(dstDateTime)) {
        System.out.println("Ambiguous, this could be in CST or CDT: " + dateTime);
    }

现在,如果字符串是29-OCT-17 01.30.00.000000000 AM,我会收到消息

Ambiguous, this could be in CST or CDT: 2017-10-29T01:30

ZonedDateTime.ofLocal()将使用提供的偏移量来解决歧义,如果它是日期时间和区域的有效偏移量。

春天不存在的时间

同样,我们可以检测您的日期时间是否落在向DST过渡时钟向前移动的间隙中:

    // Check if in spring gap
    if (! firstShot.toLocalDateTime().equals(dateTime)) {
        System.out.println("Not a valid date-time, in spring gap: " + dateTime);
    }

这可以提供类似

的消息
Not a valid date-time, in spring gap: 2018-04-01T02:01

我建议你可以放心地拒绝这些价值观。他们不可能是正确的。

避免使用三个字母的时区缩写

CST可以参考中央标准时间(北美和中美洲),澳大利亚中部标准时间,古巴标准时间和中国标准时间。 CDT可能意味着中部夏令时或古巴夏令时。三个和四个字母的缩写不是标准化的,并且通常是模糊的。优先选择 region / city 格式的时区ID,例如America / Winnipeg。