Преобразование локальной временной метки в метку времени UTC в Java
У меня есть временная метка миллисекунд-с-local-эпохи, которую я бы хотел преобразовать в метку миллисекунд-с-UTC-эпохи. Из быстрого взгляда на документы, похоже, что-то вроде этого будет работать:
int offset = TimeZone.getDefault().getRawOffset();
long newTime = oldTime - offset;
Есть ли лучший способ сделать это?
Ответы
Ответ 1
Используйте Calendar
, чтобы получить то, что смещение было в локальной Epoch, а затем добавьте это к метке времени локальной эпохи.
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
К сожалению, это лучший способ сделать это:
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 неверен.)
Ответ 3
Используя Joda Time, он будет выглядеть следующим образом:
DateTime dt = new DateTime(year, month, day, hour, minute, 0, 0, DateTimeZone.forID("local");
dt.getMillis();
EDITED: Извините, это правильная версия:
DateTime dt = new DateTime(timestamp, DateTimeZone.forID("local");
dt.getMillis();
Ответ 4
Собственно, Крис Лершер ударил ноготь по голове, но он сделал это только в коротком комментарии, поэтому я хотел расширить его.
Представьте себе два секундомера; где-то где UTC является местным временем 1 января 1970 года, а другой секундомер является локальным для вашей области (скажем, это в Нью-Йорке, через 5 часов после UTC). В полдень в UTC, 1 января 1970 года, начинается секундомер UTC. Через 5 часов начнется ваш локальный секундомер. Эти два времени секундомера отличаются на некоторую величину, определяется только тем, какая разница между UTC была с вашего локального времени в местную полночь 1 января 1970 года.. Любые летние спасатели, с тех пор, не имеют никакого отношения к разнице между этими секундомерами. Таким образом, любые корректировки DST для вашего настоящего времени или для времени, когда вы конвертируете, не имеют значения. Все, что вам нужно, это то, насколько позднее ваш локальный секундомер начался 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) будет неправильным. Вам нужно знать, что ваш дневной свет был в местной полночь, а не в полдень.
Я был бы удивлен, если бы это было так, где угодно (т.е. любой часовой пояс, который изменился на/с DST между их местной полуночью и полутоновой 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 году. Если не было изменений в DST, то localMidnightOffset будет равно смещению, и все будет готово. Если произойдет какое-то изменение DST, вам, возможно, придется поохотиться... возможно, продолжайте делать
localMidnightOffset = TimeZone.getDefault().getOffset(-localMidnightOffset)
пока он не перестанет меняться... и надеюсь, что вы не попадете в бесконечный цикл. Мне любопытно узнать, есть ли у кого-то гарантированное сближение решения.
Своего рода вы хотите, чтобы мир был плоским, а?
Ответ 5
Нет, это определенно не сработает - это не учитывает DST. Вы не можете просто использовать getOffset(oldTime)
, так как DST, возможно, изменился между двумя...
Вы можете использовать getOffset(oldTime)
, чтобы получить начальное предположение на отметке времени, затем проверьте getOffset(utcTime)
, чтобы убедиться, что они одинаковые или нет. Это получает удовольствие, в основном.
Joda Time должен поддерживать это, используя DateTimeZone.getOffsetFromLocal
, но немного сломался (IMO) вокруг переходов DST.
Все это действительно зависит от того, что вы подразумеваете под "миллисекундами с местной эпохи". Если вы действительно имеете в виду истекшие миллисекунды с начала 1970 года, вы можете просто узнать смещение в эту дату и применить это независимо. Обычно (IME) значение "local" millis вовсе не означает, что это означает "количество миллисов, чтобы добраться до определенной даты и времени (например, 9 апреля 2010 года, 18:06) в UTC, но в отношении другой часовой пояс". Другими словами, он может представлять двусмысленные или невозможные комбинации даты и времени на основе переходов DST.
Ответ 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