【问题标题】:Converting local timestamp to UTC timestamp in Java在Java中将本地时间戳转换为UTC时间戳
【发布时间】:2011-02-06 06:02:24
【问题描述】:

我有一个毫秒-since-local-epoch 时间戳,我想将它转换为毫秒-since-UTC-epoch 时间戳。快速浏览一下文档,看起来像这样可以工作:

int offset = TimeZone.getDefault().getRawOffset();
long newTime = oldTime - offset;

有没有更好的方法来做到这一点?

【问题讨论】:

    标签: java datetime timezone timestamp


    【解决方案1】:

    不,这绝对行不通 - 它不考虑夏令时。你也不能只使用getOffset(oldTime),因为两者之间的夏令时可能已经改变......

    您可以使用getOffset(oldTime) 对时间戳进行初步猜测,然后检查getOffset(utcTime) 以查看它们是否相同。基本上它很有趣。

    Joda Time 应该使用 DateTimeZone.getOffsetFromLocal 支持此功能,但这是围绕 DST 转换的 slightly broken (IMO)。

    所有这一切实际上取决于您所说的“自本地纪元以来的毫秒数”的含义。如果您真的指的是自本地 1970 年以来经过的毫秒数,您可以找出该日期的偏移量,然后无论如何都应用它。通常(IME)“本地”毫秒值并不完全意味着 - 它意味着“到达特定日期和时间(例如 2010 年 4 月 9 日,晚上 18:06)在 UTC 中的毫秒数,但就不同的时区”。换句话说,它可以表示基于 DST 转换的不明确或不可能的日期/时间组合。

    【讨论】:

    • 但我认为你可以使用getOffset(0L) 重要的是当时的时差,在纪元开始时 - 我希望 DST 转换不会发生在 1 月 1 日。
    【解决方案2】:

    使用Calendar 获取本地纪元的偏移量,然后将其添加到本地纪元时间戳。

    public static long getLocalToUtcDelta() {
        Calendar local = Calendar.getInstance();
        local.clear();
        local.set(1970, Calendar.JANUARY, 1, 0, 0, 0);
        return local.getTimeInMillis();
    }
    
    public static long converLocalTimeToUtcTime(long timeSinceLocalEpoch) {
        return timeSinceLocalEpoch + getLocalToUtcDelta();
    }
    

    【讨论】:

    • 这根本不考虑夏令时。
    • @Quartz - 不必这样做,因为我们正在处理来自 Epoch 的增量。您是否有失败的示例(区域和 timeSinceLocalEpoch)?
    【解决方案3】:

    遗憾的是,这似乎是最好的方法:

    public static Date convertLocalTimestamp(long millis)
    {
        TimeZone tz = TimeZone.getDefault();
        Calendar c = Calendar.getInstance(tz);
        long localMillis = millis;
        int offset, time;
    
        c.set(1970, Calendar.JANUARY, 1, 0, 0, 0);
    
        // Add milliseconds
        while (localMillis > Integer.MAX_VALUE)
        {
            c.add(Calendar.MILLISECOND, Integer.MAX_VALUE);
            localMillis -= Integer.MAX_VALUE;
        }
        c.add(Calendar.MILLISECOND, (int)localMillis);
    
        // Stupidly, the Calendar will give us the wrong result if we use getTime() directly.
        // Instead, we calculate the offset and do the math ourselves.
        time = c.get(Calendar.MILLISECOND);
        time += c.get(Calendar.SECOND) * 1000;
        time += c.get(Calendar.MINUTE) * 60 * 1000;
        time += c.get(Calendar.HOUR_OF_DAY) * 60 * 60 * 1000;
        offset = tz.getOffset(c.get(Calendar.ERA), c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH), c.get(Calendar.DAY_OF_WEEK), time);
    
        return new Date(millis - offset);
    }
    

    (我知道这比发布日期晚了几个月,但在 Android 上处理短信时,这是一个非常有用的问题。dave 的回答是错误的。)

    【讨论】:

      【解决方案4】:

      使用 Joda Time 会是这样的:

      DateTime dt = new DateTime(year, month, day, hour, minute, 0, 0, DateTimeZone.forID("local");
      
      dt.getMillis();
      

      已编辑:对不起,这是正确的版本:

      DateTime dt = new DateTime(timestamp, DateTimeZone.forID("local");
      
      dt.getMillis();
      

      【讨论】:

        【解决方案5】:

        实际上,Chris Lercher 一针见血,但他只发表了简短的评论,所以我想对此进行扩展。

        想象两个秒表;一个是 UTC 是 1970 年 1 月 1 日当地时间的地方,另一个秒表是您所在地区的本地时间(假设它在纽约,UTC 5 小时后)。在 1970 年 1 月 1 日 UTC 午夜,UTC 秒表启动。 5 小时后,您的本地秒表启动。这两个秒表时间存在一定差异,仅由 1970 年 1 月 1 日 当地时间午夜的 UTC 与您的当地时间之间的差异来确定。从那时起,任何夏令时的恶作剧都与这些秒表之间的差异无关。因此,对于您当前时间或您正在转换时间的任何 DST 更正都是无关紧要的。 您所需要的只是本地秒表在 1970 年 1 月 1 日开始的时间

        正如 Chris 指出的,这只是:getOffset(0L),所以:

        int offset = TimeZone.getDefault().getOffset(0L);
        long newTime = oldTime - offset;
        

        ... 应该可以正常工作。 然而....

        为了帮助真正掌握这一点,请注意:getOffset() 中的“0L”是自 UTC 纪元(这是唯一的 real 纪元)以来的毫秒数。因此,您的偏移变量将具有 UTC 午夜偏移的秒数(例如,1969 年 12 月 31 日纽约的 19:00)。如果您的当地时间在local 午夜之前的最后几个小时内切换为夏令时,则 getOffset(0L) 将不正确。您需要知道 local 午夜的夏令时状态,而不是 UTC 的午夜。

        如果在任何地方(即,在 1970 年 1 月 1 日当地时间午夜和 UTC 午夜之间更改为/从 DST 更改的任何时区)都是这种情况,我会感到惊讶。然而,只是为了好玩,一个廉价的黑客来帮助防止这种情况是检查偏移量是否在这些小时内发生了变化:

        // Offset at UTC midnight
        int offset = TimeZone.getDefault().getOffset(0L);
        long newTime = oldTime - offset;
        // Offset at Local midnight
        int localMidnightOffset = TimeZone.getDefault().getOffset(-offset);
        

        在这里,localMidnightOffset 将是 1970 年 UTC 午夜之后 time-offset 毫秒的时区偏移量。如果没有发生 DST 更改,则 localMidnightOffset 将等于偏移量,您就完成了。如果某些 DST 更改确实发生了,那么您可能不得不四处寻找......可能继续做一个

        localMidnightOffset = TimeZone.getDefault().getOffset(-localMidnightOffset)
        

        直到它停止变化……并希望你不要陷入死循环。我很想知道是否有人有保证收敛的解决方案。

        有点让你希望世界是平的,对吧?

        【讨论】:

        • 我在 SO 上看到的最佳答案一段时间
        【解决方案6】:
        static final long localTimeZoneoffset = TimeZone.getDefault().getOffset(0L);
        static final long dstOffset = TimeZone.getDefault().getDSTSavings();
        
        long offsetOftime = TimeZone.getDefault().getOffset(time.getTime());
        long timeinmilli = 0L; 
        if(offsetOftime != localTimeZoneoffset)
           timeinmilli = time.getTime()+localTimeZoneoffset+dstOffset;
        else
           timeinmilli = time.getTime()+localTimeZoneoffset;
        return new Timestamp(timeinmilli);
        

        这对我转换为 UTC 很有用。

        【讨论】:

          【解决方案7】:

          也许这可以帮助你我已经尝试过这种方式。如果有任何最佳和优化的方式将本地时间转换为 UTC 时间戳,请评论我。

          String mDate= "Jul 21,2016 1:23 PM";
          String mDateFormat =""MMM d,yyyy h:mm a";
          

          致电: getConvertedTimeToUTC(mDate,mDateFormat);

               public String getConvertedTimeToUTC(String ourDate, String mDateFormat) {
                      try {
                          SimpleDateFormat fmt = new SimpleDateFormat(mDateFormat);
                          fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
                          Date value = fmt.parse(ourDate);
                          if (value != null)
                              return String.valueOf(value.getTime() / 1000);
                          else
                              return null;
                      } catch (Exception e) {
                          ourDate = "00-00-0000 00:00";
                      }
                      return ourDate;
                  }
          

          结果如下:(Ref : check conversion)

          Result 1469107380
          GMT: Thu, 21 Jul 2016 13:23:00 GMT
          Your time zone: Thursday 21 July 2016 06:53:00 PM IST GMT+5:30
          

          【讨论】:

            猜你喜欢
            • 2019-08-15
            • 2019-03-09
            • 2019-01-05
            • 2013-03-23
            • 2013-09-12
            • 2012-09-11
            • 2015-04-20
            • 2018-09-17
            • 2018-04-17
            相关资源
            最近更新 更多