2016-12-10 9 views
1

Я пытаюсь проанализировать дату в формате ISO библиотекой JodaTime для android и получить неправильное смещение часового пояса.Неправильное смещение временной зоны на Android после разбора JodaTime

String dateString = "1990-05-03T20:00:00.000Z"; 
DateTimeFormatter inputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(DateTimeZone.UTC); 
DateTimeFormatter outputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(DateTimeZone.getDefault()); 
DateTime dt = inputFormatter.parseDateTime(dateString); 
String result = outputFormatter.print(dt); 

Мой Часовой пояс по умолчанию является "Europe/Moscow" Но результат "1990-05-04T00: 00: 00,000 + 0400", когда оно должно быть «1990-05-03T23: 00: 00,000 + 0300 ». Версия для Android - KitKat.

Что я сделал не так?

+0

Я думаю, ничего плохого. 1990-05-03 - в летнее время (май), поэтому Joda-Time использует летнее смещение +04: 00 для Европы/Москвы. Может быть, вы просто смущены, потому что на самом деле Москва в зимнее время. –

+0

@MenoHochschild С 2014 года в России есть только зимнее время, и мы не меняем время каждый год. Как я могу отключить летнее/зимнее время в Joda-Time? – preceptron

ответ

3

Если вы хотите только использования фактической Московской зоны смещения для отображения исторической метки, то вы можете использовать этот код:

String dateString = "1990-05-03T20:00:00.000Z"; 
DateTimeFormatter inputFormatter = 
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(DateTimeZone.UTC); 
DateTime dt = inputFormatter.parseDateTime(dateString); 
System.out.println(dt); // 1990-05-03T20:00:00.000Z 

// only use actual offset for Moscow 
DateTimeZone zoneOffset = 
    DateTimeZone.forOffsetMillis(
     DateTimeZone.forID("Europe/Moscow").getOffset(DateTime.now()) 
    ); 
DateTimeFormatter outputFormatter = 
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(zoneOffset); 
String result = outputFormatter.print(dt); 
System.out.println(result); // 1990-05-03T23:00:00.000+0300 

Тем не менее тот же момент, как раз с различным смещением и местного времени часть из форму, которую вы ищете.

О ваш комментарий, как синтаксический анализ UTC времени с сервера:

String input = "2016-12-10T13:40:38.595"; 
DateTime utc = 
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").parseLocalDateTime(input) 
    .toDateTime(DateTimeZone.UTC); 
System.out.println(utc); // 2016-12-10T13:40:38.595Z 

Как отправить текущее время устройства, как UTC к серверу, если данные устройства являются неправильными?

Прежде всего, я считаю, что это лучший дизайн, если нет необходимости отправлять текущую временную метку клиента на сервер, потому что сервер должен использовать свои собственные часы для регистрации метки времени при получении клиентских сообщений. Это эффективно препятствует поддельным клиентам отправлять какое-либо бессмысленное время.

В противном случае, если и tz-данные, и часы устройства (через System.currentTimeMillis()) ошибочны, но временная метка локального устройства по-прежнему правильная, как указано в ваших комментариях (допустимые значения полей для года-месяца-дня-часа-минуты) то можно применить следующий обходной путь:

// incorrect platform data from device clock or outdated tz-data 
long platformMillis = System.currentTimeMillis(); 
int offsetMillis = TimeZone.getDefault().getOffset(platformMillis); 
DateTimeZone tzPlatform = DateTimeZone.forOffsetMillis(offsetMillis); // +04 on your device 

// we assume correct local time if the errors of platform data compensate each other 
LocalDateTime ldt = new LocalDateTime(new Date(platformMillis), tzPlatform); 

// now we combine local timestamp with the tz-data of Joda-Time (hopefully up-to-date) 
long utcMillis = ldt.toDateTime(DateTimeZone.getDefault()).getMillis(); 

Side примечание: Моя библиотека Time4A позволяет более короткое решение.

Moment now = SystemClock.inPlatformView().now().inStdTimezone(); 
long utcMillis = TemporalType.MILLIS_SINCE_UNIX.from(now); 
+0

Это работа. Но что, если я буду использовать такой код для всех часовых поясов? 'DateTimeZone zoneOffset = DateTimeZone.forOffsetMillis (DateTimeZone.getDefault(). GetOffset (DateTime.now())); DateTimeFormatter outputFormatter = DateTimeFormat.forPattern ("yyyy-MM-dd'T'HH: mm: ss.SSSZ"). WithZone (zoneOffset); ' – preceptron

+0

@preceptron Почему бы не работать в каком-либо другом часовом поясе? Вы можете отображать любое мгновение с произвольным смещением, сохраняя момент, но изменяя местную метку времени. Однако историческая форма с историческим смещением представляется мне более естественной, а не вашей просьбой использовать только фактическое смещение, которое не связано с меткой времени, которую вы хотите отобразить. –

+0

@preceptron Возможно, это делает его более ясным: представьте, что вы находитесь не в Москве, а в Бразилии, поэтому вы можете отобразить SAME мгновенно со смещением зоны Sao-Paulo, в результате чего местная временная метка для Бразилии. Локальная метка времени и изменение смещения, но момент все тот же. И это действительно зависит от вас, который вы хотите использовать для отображения.Если вы просто поручите Joda-Time использовать московскую зону (без явного смещения), тогда Joda-Time будет использовать смещение, заданное в контекстуальный исторический момент (который должен быть напечатан). –