Converting local timestamp to UTC timestamp in Jav

2019-01-18 04:13发布

问题:

I have a milliseconds-since-local-epoch timestamp that I'd like to convert into a milliseconds-since-UTC-epoch timestamp. From a quick glance through the docs it looks like something like this would work:

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

Is there a better way to do this?

回答1:

Use a Calendar to get what the offset was at the local Epoch, then add that to the local-epoch timestamp.

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


回答2:

Sadly, this seems to be the best way to do this:

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);
}

(I know that this is several months past post date, but it's a problem that is very useful to solve when working with text messages on Android. dave's answer is wrong.)



回答3:

Using Joda Time it would be like this :

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

dt.getMillis();

EDITED: Sorry, this is the correct version :

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

dt.getMillis();


回答4:

Actually, Chris Lercher hit the nail on the head, but he only made it in a short comment, so I wanted to expand on it.

Imagine two stopwatches; one is somewhere where UTC is the local time on Jan 1, 1970, and the other stopwatch is local to your area (let's say that it's in New York, 5 hours after UTC). At UTC midnight, on Jan 1, 1970, the UTC stopwatch is started. 5 hours later, your local stopwatch is started. Those two stopwatch times differ by some amount, determined only by what the difference between UTC was from your local time at local midnight on Jan 1, 1970. Any daylight-saving shenanigans, since then, have no bearing on the difference between those stopwatches. So, any DST corrections for your present time or for the times you're converting, are irrelevant. All you need is how much later your local stopwatch started on Jan 1, 1970.

As Chris pointed out, this is just: getOffset(0L), so:

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

... should work fine. However....

To help really grasp this, note this: that "0L" in getOffset() is the milliseconds since the UTC epoch (which is the only real epoch). So, your offset variable is going to have the number of seconds of offset at midnight UTC (ie, when it was, say, 19:00 on 12/31/1969 in New York). If your local time switched to/from daylight-saving in those last hours before local midnight, then getOffset(0L) wouldn't be correct. You need to know what your daylight-saving status was at local midnight, not UTC's midnight.

I'd be surprised if this were the case, anywhere (ie, any timezone which changed to/from DST between their local midnight and UTC midnight of Jan 1, 1970). However, just for fun, a cheap hack to help guard against this would be to check if the offset changed in those hours:

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

Here, localMidnightOffset will be what the timezone offset was at a time -offset milliseconds after UTC midnight in 1970. If no DST change happened, then localMidnightOffset will equal offset, and you're done. If some DST change did occur, then you might have to hunt around... probably keep doing a

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

until it stops changing... and hope you don't get caught in an endless loop. I'm curious to see if anybody has a guaranteed-converging solution.

Kinda makes you wish the world were flat, huh?



回答5:

No, that definitely won't work - it doesn't take DST into account. You can't just use getOffset(oldTime) either, as the DST may have changed between the two...

You could use getOffset(oldTime) to get an initial guess at the timestamp, then check getOffset(utcTime) to see whether they're the same or not. It gets fun, basically.

Joda Time should support this using DateTimeZone.getOffsetFromLocal but that's slightly broken (IMO) around DST transitions.

All of this really depends on what you mean by "milliseconds since local epoch". If you really mean elapsed milliseconds since local 1970, you could just find out the offset at that date, and apply that regardless. Typically (IME) a "local" millis value doesn't mean quite that though - it means "the number of millis to get to a particular date and time (e.g. April 9th 2010, 18:06pm) in UTC, but in respect of a different time zone". In other words, it can represent ambiguous or impossible date/time combinations based on DST transitions.



回答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);

This worked for me to convert to UTC.



回答7:

May be this can help you i have try this way. Please comment me if there is any best and optimize way to convert local time to UTC timestamp.

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

Call : 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;
        }

Here is the result : (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