【问题标题】:Java epoch time on server converted to client time服务器上的 Java 纪元时间转换为客户端时间
【发布时间】:2021-03-23 01:40:12
【问题描述】:

服务器正在使用 System.currentTimeMillis() 存储客户端操作的日期/时间。假设此服务器位于 EST 时区。

法国的一位客户想查看该操作是在什么时间进行的。通过 System.currentTimeMillis()(在美国服务器上)存储的长值返回给法国的客户端。

我在理解如何在客户端进行转换以准确描述他们的操作时间时遇到问题。我的理解是,在服务器上 System.currentTimeMillis() 是一个无区域的 UTC 时间。当我用“UTC”时区实例化一个日历对象时,事情似乎很奇怪,当服务器使用 currentTimeMillis() 保存时,它存储一个时区的纪元时间也是如此

第一次尝试:

    String timezone = clientTimezone;//Lets say france

    Calendar cal = Calendar.getInstance();
    cal.setTimeInMillis(time); //This time var is the server stored value (currentTimeMillis)
    
    if(!timezone.equals("")) {
        long timezoneAlteredTime = time + TimeZone.getTimeZone(timezone).getRawOffset();
        cal = Calendar.getInstance(TimeZone.getTimeZone(timezone));
        cal.setTimeInMillis(timezoneAlteredTime);
    }

其他尝试:

    String timezone = clientTimezone;//Lets say france
    TimeZone utc = TimeZone.getTimeZone("UTC");
    Calendar cal = Calendar.getInstance(utc);
    cal.setTimeInMillis(time); //This time var is the server stored value 
    
    if(!timezone.equals("")) {
        long timezoneAlteredTime = time + TimeZone.getTimeZone(timezone).getRawOffset();
        cal = Calendar.getInstance(TimeZone.getTimeZone(timezone));
        cal.setTimeInMillis(timezoneAlteredTime);
    }

我完全误解了这个问题吗?您将如何进行此转换

【问题讨论】:

  • 您不应该进行任何转换。自纪元以来的毫秒数(从System.currentTimeMillis() 开始)在世界各地都是相同的,并且与时区无关。因为 eopch 是一个时间点,所有时区的同一时间点(而不是一天中的同一时钟时间)。
  • 我建议你不要使用Calendar 和TimeZone。这些课程设计不良且早已过时。而是使用来自java.time, the modern Java date and time API 的ZonedDateTime 和ZoneId。
  • 您可能会发现这很有帮助:What should I do when someone answers my question?

标签: java date calendar timezone epoch


【解决方案1】:

java.time

当您知道如何操作时,这非常简单。我建议您将工作留给现代 Java 日期和时间 API java.time。

    String clientTimezone = "Europe/Paris";
    long time = 1_607_708_578_154L;
    
    ZonedDateTime timeInClientTimeZone = Instant.ofEpochMilli(time)
            .atZone(ZoneId.of(clientTimezone));
    
    System.out.println(timeInClientTimeZone);

此示例代码 sn-p 的输出为:

2020-12-11T18:42:58.154+01:00[欧洲/巴黎]

你的代码出了什么问题?

我的理解是在服务器上 System.currentTimeMillis() 是无时区的 UTC 时间。

到目前为止你的理解是正确的。

尽管纪元通常以 UTC 定义,但这里的线索是无区域。因此,您不需要 UTC 中的任何日期时间对象。您也不需要对毫秒计数进行任何调整,因此这样做会将正确的时间变成错误的时间。

链接

Oracle tutorial: Date Time 解释如何使用 java.time。

【讨论】:

    【解决方案2】:

    Answer by Ole V.V. 是正确的:调用 Instant::atZone 将时刻从 UTC 调整到特定时区。时间线上的同一点,不同的挂钟时间。

    这里还有一些想法。

    假设此服务器位于 EST 时区。

    仅供参考,通常最好将 UTC 设置为服务器上的时区。

    System.currentTimeMillis() 是无时区 UTC 时间

    不是无区域的。该方法返回自 1970 年第一个时刻的纪元参考以来的毫秒数,如 UTC 所示。 (零时分秒的offset-from-UTC)

    如果没有从 UTC 偏移或时区的上下文,您就无法表示时刻、时间线上的特定点。

    当我实例化一个 Calendar 对象时,事情似乎很奇怪

    Calendar 类的一切都很奇怪。

    这就是为什么我们几年前就停止使用那个糟糕的课程了。仅使用 java.time 类。

    服务器正在使用 System.currentTimeMillis() 存储客户端操作的日期/时间。

    我们为此开设了一个课程:Instant。所以不需要仅仅使用整数,现在您可以使用类型安全且方便的类。

    Instant 类代表 UTC 中的时刻。它的对象使用纳秒级的分辨率,尽管现在大多数计算机时钟都可以捕捉到微秒级的时刻。

    Instant instant = Instant.now() ;
    

    您可以在毫秒数和Instant 之间进行转换。

    Instant instant = Instant.ofEpochMilli( yourCountOfMillis ) ;
    

    根据定义,Instant 采用 UTC。要从另一个偏移量查看同一时刻,请使用OffsetDateTime。要查看特定时区的同一时刻,请使用ZonedDateTime。

    了解时区是过去、现在和未来对特定地区人们使用的偏移量的变化的历史。出于各种原因,政客们经常更改其管辖范围的偏移量。

    搜索 Stack Overflow 以了解更多信息。这些问题已经讨论过很多次了。

    【讨论】:

    • Calendar 类的一切都很奇怪。说得好。
    猜你喜欢
    • 1970-01-01
    • 2012-01-26
    • 2012-08-30
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 2012-10-24
    • 2018-05-03
    • 1970-01-01
    相关资源
    最近更新 更多