Преобразование местной метки времени в метку времени UTC в Java - PullRequest
15 голосов
/ 09 апреля 2010

У меня есть метка времени в миллисекундах с локальной эпохи, которую я хотел бы преобразовать в метку времени с миллионной секунды с момента UTC. При быстром взгляде на документы это выглядит примерно так:

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

Есть ли лучший способ сделать это?

Ответы [ 7 ]

9 голосов
/ 26 августа 2010

К сожалению, это, кажется, лучший способ сделать это:

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. Дейв ответил неправильно.)

7 голосов
/ 10 апреля 2010

Используйте 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 голосов
/ 03 мая 2013

На самом деле, Крис Лерчер ударил ногтем по голове, но он сделал это только в кратком комментарии, поэтому я хотел расширить его.

Вообразите двух секундомеров; один находится где-то, где UTC - это местное время 1 января 1970 года, а другой секундомер - локальный в вашем районе (скажем, он в Нью-Йорке, через 5 часов после UTC). В полночь UTC 1 января 1970 года запускается секундомер UTC. Через 5 часов ваш местный секундомер запущен. Эти два времени секундомера отличаются на некоторую величину, определяемую только тем, что разница между UTC была от вашего местного времени в по местному полуночи 1 января 1970 . Любые махинации, спасающие дневное время, с тех пор не имеют никакого отношения к разнице между этими секундомерами. Таким образом, любые поправки на летнее время для вашего настоящего времени или времени, когда вы конвертируете , не имеют значения. Все, что вам нужно, это то, сколько позже ваш местный секундомер запустился 1 января 1970 года .

Как указал Крис, это просто: getOffset (0L), поэтому:

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

... должно работать нормально. Однако ....

Чтобы действительно понять это, обратите внимание: «0L» в getOffset () - это миллисекунды с эпохи UTC (которая является единственной действительной эпохой). Итак, ваша переменная смещения будет иметь количество секунд смещения в полночь UTC (то есть, когда это было, скажем, 19:00 31.12.1969 г. в Нью-Йорке). Если ваше местное время было переключено на / с перехода на летнее время в эти последние часы до местного полуночи, то getOffset (0L) не будет корректным. Вы должны знать, каков был ваш статус перехода на летнее время в местный полночь, а не в полночь UTC.

Я был бы удивлен, если бы это было так, где угодно (то есть, любой часовой пояс, который изменился на / из летнего времени между их местной полуночью и полуночью UTC 1 января 1970 года). Однако, просто для удовольствия, дешевый хак, который поможет избежать этого, будет проверять, изменилось ли смещение в эти часы:

// 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 будет тем, что было смещением часового пояса через миллисекунды с временным смещением после полуночи UTC в 1970 году. Если не произошло изменения летнего времени, localMidnightOffset сравняет смещение, и все готово. Если какое-то изменение DST действительно произошло, то вам, возможно, придется охотиться ... вероятно, продолжайте делать

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

пока он не перестанет меняться ... и надеюсь, что вы не попадете в бесконечный цикл. Мне любопытно посмотреть, есть ли у кого-нибудь решение с гарантированной конвергенцией.

Что-то заставляет тебя желать, чтобы мир был плоским, а?

3 голосов
/ 11 января 2011

Используя 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();
2 голосов
/ 09 апреля 2010

Нет, это точно не сработает - это не учитывает DST. Вы не можете просто использовать getOffset(oldTime), так как DST может измениться между двумя ...

Вы можете использовать getOffset(oldTime), чтобы получить начальное предположение о временной отметке, а затем проверить getOffset(utcTime), чтобы увидеть, совпадают ли они или нет. В общем, весело.

Joda Time должен поддерживать это, используя DateTimeZone.getOffsetFromLocal, но это слегка сломано (IMO) вокруг переходов DST.

Все это действительно зависит от того, что вы подразумеваете под "миллисекундами с локальной эпохи". Если вы действительно имеете в виду истекшие миллисекунды с местного 1970 года, вы можете просто узнать смещение на эту дату и применить его независимо от этого. Обычно (IME) «локальное» значение в миллисекундах не означает, что это означает, что оно означает «количество миллисекунд до определенной даты и времени (например, 9 апреля 2010 г., 18:06 вечера) в UTC, но в отношении другой часовой пояс ". Другими словами, он может представлять неоднозначные или невозможные комбинации даты и времени на основе переходов DST.

0 голосов
/ 21 июля 2016

Может быть, это может помочь вам, я должен попробовать этот путь. Пожалуйста, прокомментируйте меня, если есть лучший и оптимальный способ конвертировать местное время в 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 голосов
/ 22 апреля 2016
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.

...