理解Etc/GMT时区

32

为什么苹果在自动续订订阅的App Store收据返回时使用Etc/GMT时区?

Etc/GMT是什么时区?Java SDK是否理解这个时区?还是我需要使用其他第三方库,如Joda-Time


你有理由认为这不仅仅是说UTC的奇怪方式吗? - hmakholm left over Monica
我猜是这样,但他们为什么要使用它呢?只是为了让它看起来复杂! - royalghost
他们可能因为某种原因被迫使用一个时区数据库来组织语言环境,该数据库通常采用大陆/城市结构,并使用“Etc”作为“不遵循该结构的事物”的转义。 - hmakholm left over Monica
FYI,Joda-Time 项目现已进入维护模式,建议迁移到java.time类。请参阅Oracle的教程 - Basil Bourque
5个回答

33

Etc/GMT并不严格等同于UTC或GMT。只有当偏移量为0时,它们才表示相同的时间点。 在所有其他情况下,它们都是完全不同的。

苹果公司在此处解释了该称谓。

链接中的直接引用举了一个例子:

我们在时区名称和输出缩写中使用 POSIX 风格的符号, 即使这与许多人的期望相反。 POSIX 在格林威治以西使用正号,但许多人期望 正号在格林威治以东。例如,TZ = 'Etc / GMT + 4' 使用 缩写“GMT + 4”,对应于比UTC晚4小时 (即在格林威治以西),尽管许多人期望它表示比UTC早4小时(即在格林威治以东)。


8
FYI - 这只是苹果公司复制的时区数据库。那些注释来自于数据库本身,并不是苹果公司添加的。 - Matt Johnson-Pint

19

偏移量与时区

理解:

  • 偏移自协调世界时(UTC)时间是一个数字,指比UTC基准时间提前或落后的小时-分钟-秒数。
  • 时区则包含更多内容。时区记录了某个地区居民过去、现在和未来使用的偏移量变化历史。

正数与负数编号

各行业中的不同协议在编号时存在差异。有些人认为UTC之前的偏移量是正数,而其他人则使用负数。同样地,一些人认为UTC之后的偏移量是负数,而其他人则使用正数。

在我看到的大多数现代协议中(如ISO 8601),偏移UTC的地区(向东)的数字是正数,而偏移UTC的地区(向西)则是负数。因此,美洲地区的偏移量使用负数,例如America/Los_Angeles目前偏移量为-07:00-08:00,因为会受到夏令时的影响而在一年中变化。

我建议您将时间按照UTC右边为正值,左边为负值的方式看待为主流,相反的方式只是一个令人讨厌的小变化。
时区名称通常采用“大陆/地区”的格式,例如:America/Edmonton,Europe/Paris,Africa/Tunis,Asia/Kolkata和Pacific/Auckland。请参阅维基百科上的列表(可能不是最新的)。也有一些例外情况,如Etc/GMT…命名约定与其他命名相反:
- Etc/GMT+1 = -01:00偏移=比UTC早一小时 - Etc/GMT+12 = -12:00偏移=比UTC早12小时
还有:
- Etc/GMT-1 = +01:00偏移=比UTC晚一小时 - Etc/GMT-12 = +12:00偏移=比UTC晚12小时
很混乱吧?欢迎来到日期时间处理的奇怪世界。从这里开始,只会变得更奇怪。
关键点:
- 理解那些发布数据的人的含义和意图。不要假设输入字符串的含义。
  • 所有日期时间处理都应使用java.time类,不要使用糟糕的旧类如java.util.DateCalendarSimpleDateFormat等。
  • 幸运的是,java.time类可以帮助您解决这个混乱。查看Ole V.V.的正确答案,其中使用了ZoneId类。

    你的问题

    Apple使用Etc/GMT时区的原因

    他们指的是偏移量为零,即UTC本身。字符串Etc/GMT是一个带有零小时-分钟-秒的偏移量从UTC的一个规范标签。

    日期时间字符串末尾常见的字母Z(发音为“Zulu”)意味着相同的事情,即偏移量为零。

    Etc/GMT时区到底是什么?

    字符串Etc/GMT是一个时间区域的名称,该时区只有一个来自UTC的偏移量,即零小时-分钟-秒。

    大多数其他时区(例如 Europe/BerlinAfrica/Casablanca)的偏移值在历史上有所不同。例如,在摩洛哥的 Africa/Casablanca 时区中,政治家去年决定,他们将全年保持夏令时而不是每年两次切换到标准时间和夏令时,我用笑声说“永久”,因为那其实意味着“直到政治家再次改变主意”。世界各地的政治家频繁重新定义他们的时区。

    Java SDK理解这个时区吗?

    是的。请参见 Ole V.V. 的 答案ZoneId.of("Etc/GMT")

    还是我必须使用像Joda-Time这样的第三方库?

    顺便提一下,Joda-Time 项目现在处于维护模式,建议迁移至 java.time 类。请参见 Oracle 的教程

    您应该使用java.time类来处理所有日期和时间。


    8

    Etc/GMT 是标准的表达方式,表示 UTCGMTGMT0 或者 GMT+00:00

    Java JDK 可以理解所有这些格式。您可以通过以下操作轻松查看其效果:

    import java.util.TimeZone;
    
    public class Playground {
    
        public static void main(String... args) {
    
            for (String s : TimeZone.getAvailableIDs()) {
                System.out.println(s);
            }
        }
    }
    

    这将打印出你的Java JDK可以解析的所有不同TimeZone格式:

    ...
    Etc/GMT
    Etc/GMT+0
    Etc/GMT-0
    Etc/GMT0
    Etc/Greenwich
    Etc/UCT
    Etc/UTC
    Etc/Universal
    ...


    1
    没错!它会说它可以解析它,但是当我尝试时,它会抛出ParseException异常。不过,我现在找到了解决方法。 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); sdf.setTimeZone(TimeZone.getTimeZone("Etc/GMT")); try { System.out.println( sdf.parse("2011-09-02 10:26:35 Etc/GMT") ); } catch (ParseException e) { e.printStackTrace();
    }
    - royalghost
    1
    请查看我的答案,了解时区标识符的正确解释方式和理由。 - David Peden
    这个答案是 Etc/Correct。 - RoundTower
    2
    此答案已过时。TimeZone类现已被ZoneIdZoneOffset所取代,成为遗留代码。请参阅JSR 310。 - Basil Bourque

    1
    当在不同时区传递时间点时,建议使用UTC或GMT(对于大多数目的而言,两者大致相等,我们不加区分)。看起来苹果确实这样做了。
    JDK可以很好地理解“Etc/GMT”。
        ZoneId etcGmt = ZoneId.of("Etc/GMT");
    

    JDK使用tz数据库(以前称为Olson数据库;链接在底部)。数据库中的时区名称列表在另一个链接中。其中列出了Etc/GMT。您会注意到,它被给定为GMT的规范名称(还有一些别名,一些当前的和一些弃用)。
    顺便说一下,我的代码行当然使用java.time的ZoneId,这是现代Java日期和时间API的JDK类。这是您想要使用的类(还有一个旧的和设计不良的时区类,您不想使用)。
    我认为您并没有打算问,但对于任何感兴趣的人:JDK也理解Etc/GMT+0、Etc/GMT+1、Etc/GMT0、Etc/GMT-0、Etc/GMT-1等(没有双关语),因为它们也在数据库中。它还正确处理David Peden所接受的答案中提到的引用中提到的反向符号。
    链接

    0

    我建议您使用一个 Java 库 来处理 App Store 收据,而不要再考虑日期格式。

    添加这个 artifact (by.dev.madhead.utils.appstore_receipts_validator:model:2.0.0)。

    使用任何 HTTP 客户端调用 App Store 并获取响应(这里我使用 Ktor):

    suspend fun verify(receipt: String, password: String): VerifyReceiptResponse {
        val rawResponse = client.post<String> {
            url("https://buy.itunes.apple.com/verifyReceipt")
            contentType(ContentType.Application.Json)
            accept(ContentType.Application.Json)
            body = VerifyReceiptRequest(
                    receipt,
                    password,
                    true
            )
        }
    }
    

    使用Jackson解析响应:

    val response = objectMapper.readValue(rawResponse)
    

    现在您可以使用普通的Java API来处理响应。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接