Java中将本地时间戳转换为UTC时间戳

15

我有一个自本地时代以来的毫秒级时间戳,我想将其转换为自UTC时代以来的毫秒级时间戳。从快速查看文档来看,似乎以下内容应该可以实现:

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

有更好的方法吗?

7个回答

9

不幸的是,这似乎是最好的方法:

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的答案是错误的。


7
使用 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();
}

3
这完全没有考虑夏令时。 - Ross Light
@Quartz - 不需要,因为我们正在处理相对于纪元的增量。您是否有一个失败的示例(区域和timeSinceLocalEpoch)? - leedm777

3
使用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();

3

实际上,Chris Lercher说得很到位,但他只是在短评中提到了这一点,所以我想进一步阐述。

想象两个秒表;一个在UTC为当地时间的1970年1月1日,另一个秒表是本地的(假设在纽约,在UTC之后5个小时)。在1970年1月1日的UTC午夜,启动UTC秒表。5小时后,启动您所在地区的本地秒表。这两个秒表的时间差异由仅仅取决于UTC与1970年1月1日当地午夜时刻的本地时间之间的差异。自那时以来的任何夏令时调整都不会影响这些秒表之间的差异。因此,对于您的当前时间或您正在转换的时间的任何夏令时校正都是无关紧要的。您只需要知道您的本地秒表在1970年1月1日之后晚了多少时间

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

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

应该工作得很好。然而……

为了更好地理解这一点,请注意:getOffset()中的“0L”是自UTC时代(它是唯一的真实的时代)以来的毫秒数。因此,您的偏移量变量将在UTC午夜时具有偏移量的秒数(例如,在纽约时间是19:00的12/31/1969时)。如果您的当地时间在本地午夜之前的最后几个小时内切换到/从夏令时,则getOffset(0L)将不正确。您需要知道本地午夜时的夏令时状态,而不是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午夜之后-offset毫秒的时候的时区偏移量。如果没有发生夏令时更改,则localMidnightOffset将等于offset,那么就完成了。如果出现了某些夏令时更改,那么您可能需要四处寻找...可能要继续执行。

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

直到它停止变化...希望你不要陷入无尽的循环中。我很好奇是否有人有一个保证收敛的解决方案。
有点让你希望世界是平的,对吧?

这是我在SO上看到的最佳答案。 - paz

2
不,那绝对行不通——它没有考虑夏令时。你也不能只使用getOffset(oldTime),因为夏令时可能在两者之间发生了变化……
你可以使用getOffset(oldTime)来获取时间戳的初始猜测值,然后检查getOffset(utcTime)以查看它们是否相同。基本上这很有趣。 Joda Time应该支持使用DateTimeZone.getOffsetFromLocal,但在夏令时转换期间存在一些问题(依我之见)。
所有这些都取决于您所说的“本地纪元毫秒数”的含义。如果您真的是指自1970年以来经过的本地毫秒数,那么您可以找出该日期的偏移量,并无论如何应用它。通常(IME),“本地”毫秒值并不完全意味着这个 - 它意味着“到达UTC中的特定日期和时间(例如2010年4月9日18:06),但是相对于不同的时区”。换句话说,它可能表示基于DST转换的模糊或不可能的日期/时间组合。

但我认为你可以使用 getOffset(0L)。重要的是那时候的时间差,在纪元开始时 - 我希望,夏令时转换没有发生在1月1日。 - Chris Lercher

0
也许这个可以帮到你,我尝试过这种方法。如果有更好和优化的方式将本地时间转换为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;
        }

这是结果:(参考:检查转换)

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

0
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时间。


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