Подтвердить что ты не робот

SimpleDateFormat parse теряет часовой пояс

код:

 SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
    System.out.println(new Date());
    try {
        String d = sdf.format(new Date());
        System.out.println(d);
        System.out.println(sdf.parse(d));
    } catch (Exception e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }

Выход:

Thu Aug 08 17:26:32 GMT+08:00 2013
2013.08.08 09:26:32 GMT
Thu Aug 08 17:26:32 GMT+08:00 2013

Обратите внимание, что format() корректно форматирует Date на GMT, но parse() потерял данные GMT. Я знаю, что могу использовать substring() и обойти это, но в чем причина этого феномена?

Вот дублированный вопрос, который не имеет ответов.

Изменить: Позвольте мне задать вопрос по-другому, каков способ получить объект Date, чтобы он всегда находился в GMT?

4b9b3361

Ответ 1

Все, что мне было нужно, это:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

SimpleDateFormat sdfLocal = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");

try {
    String d = sdf.format(new Date());
    System.out.println(d);
    System.out.println(sdfLocal.parse(d));
} catch (Exception e) {
    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
}

Результат: немного сомнительный, но я хочу, чтобы только дата была последовательной

2013.08.08 11:01:08
Thu Aug 08 11:01:08 GMT+08:00 2013

Ответ 2

Решение OP для его проблемы, по его словам, имеет сомнительный результат. Этот код по-прежнему показывает путаницу в отношении представлений о времени. Чтобы устранить эту путаницу и сделать код, который не приведет к неправильным временам, рассмотрите это расширение того, что он сделал:

public static void _testDateFormatting() {
    SimpleDateFormat sdfGMT1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
    sdfGMT1.setTimeZone(TimeZone.getTimeZone("GMT"));
    SimpleDateFormat sdfGMT2 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z");
    sdfGMT2.setTimeZone(TimeZone.getTimeZone("GMT"));

    SimpleDateFormat sdfLocal1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
    SimpleDateFormat sdfLocal2 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z");

    try {
        Date d = new Date();
        String s1 = d.toString();
        String s2 = sdfLocal1.format(d);
        // Store s3 or s4 in database.
        String s3 = sdfGMT1.format(d);
        String s4 = sdfGMT2.format(d);
        // Retrieve s3 or s4 from database, using LOCAL sdf.
        String s5 = sdfLocal1.parse(s3).toString();
        //EXCEPTION String s6 = sdfLocal2.parse(s3).toString();
        String s7 = sdfLocal1.parse(s4).toString();
        String s8 = sdfLocal2.parse(s4).toString();
        // Retrieve s3 from database, using GMT sdf.
        // Note that this is the SAME sdf that created s3.
        Date d2 = sdfGMT1.parse(s3);
        String s9 = d2.toString();
        String s10 = sdfGMT1.format(d2);
        String s11 = sdfLocal2.format(d2);
    } catch (Exception e) {
        e.printStackTrace();
    }       
}

проверка значений в отладчике:

s1  "Mon Sep 07 06:11:53 EDT 2015" (id=831698113128)    
s2  "2015.09.07 06:11:53" (id=831698114048) 
s3  "2015.09.07 10:11:53" (id=831698114968) 
s4  "2015.09.07 10:11:53 GMT+00:00" (id=831698116112)   
s5  "Mon Sep 07 10:11:53 EDT 2015" (id=831698116944)    
s6  -- omitted, gave parse exception    
s7  "Mon Sep 07 10:11:53 EDT 2015" (id=831698118680)    
s8  "Mon Sep 07 06:11:53 EDT 2015" (id=831698119584)    
s9  "Mon Sep 07 06:11:53 EDT 2015" (id=831698120392)    
s10 "2015.09.07 10:11:53" (id=831698121312) 
s11 "2015.09.07 06:11:53 EDT" (id=831698122256) 

sdf2 и sdfLocal2 включают часовой пояс, поэтому мы можем видеть, что действительно происходит. s1 и s2 находятся в 06:11:53 в зоне EDT. s3 и s4 находятся в 10:11:53 в зоне GMT ​​- эквивалентно первоначальному времени EDT. Представьте, что мы сохраняем s3 или s4 в базе данных, где мы используем GMT для согласованности, поэтому мы можем иметь время из любой точки мира, не сохраняя разные часовые пояса.

s5 анализирует время GMT, но рассматривает его как локальное время. Поэтому он говорит "10:11:53" - время по Гринвичу - но считает, что 10:11:53 по местному времени. Нехорошо.

s7 анализирует время GMT, но игнорирует GMT в строке, поэтому по-прежнему рассматривает его как локальное время.

s8 работает, потому что теперь мы включаем GMT в строку, а парсер локальной зоны использует его для преобразования из одного часового пояса в другой.

Теперь предположим, что вы не хотите сохранять зону, вы хотите анализировать s3, но отображать ее как локальное время. Ответ заключается в анализе с использованием того же часового пояса, в котором он был сохранен, поэтому используйте тот же sdf, который был создан в sdfGMT1. s9, s10 и s11 - все представления исходного времени. Все они "правильные". То есть d2 == d1. Тогда речь идет только о том, как вы хотите отображать его. Если вы хотите отобразить то, что хранится в БД - время по Гринвичу - тогда вам нужно отформатировать его, используя GMT sdf. Ths s10.

Итак, вот окончательное решение, если вы не хотите явно хранить с "GMT" в строке и хотите отображать в формате GMT:

public static void _testDateFormatting() {
    SimpleDateFormat sdfGMT1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
    sdfGMT1.setTimeZone(TimeZone.getTimeZone("GMT"));

    try {
        Date d = new Date();
        String s3 = sdfGMT1.format(d);
        // Store s3 in DB.
        // ...
        // Retrieve s3 from database, using GMT sdf.
        Date d2 = sdfGMT1.parse(s3);
        String s10 = sdfGMT1.format(d2);
    } catch (Exception e) {
        e.printStackTrace();
    }       
}

Ответ 3

ТЛ; др

Как можно получить объект Date, чтобы он всегда находился в GMT?

Instant.now() 

Подробнее

Вы используете проблемные старые классы даты и времени, которые теперь вытесняются классами java.time.

Instant = UTC

Класс Instant представляет момент на временной шкале в UTC с разрешением наносекунд (до девяти (9) цифр десятичной дроби).

Instant instant = Instant.now() ; // Current moment in UTC.

ISO 8601

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

Классы java.time по умолчанию используют стандартные форматы при разборе и генерации строк.

String output = instant.toString() ;  

2017-01-23T12:34:56.123456789Z

Часовой пояс

Если вы хотите увидеть тот же момент, который представлен на настенных часах определенного региона, примените ZoneId, чтобы получить ZonedDateTime.

Укажите правильное имя часового пояса в формате continent/region, например America/Montreal, Africa/Casablanca или Pacific/Auckland. Никогда не используйте 3-4-буквенное сокращение, такое как EST или IST, поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!).

ZoneId z = ZoneId.of( "Asia/Singapore" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same simultaneous moment, same point on the timeline.

См. этот код в прямом эфире на IdeOne.com.

Обратите внимание на восьмичасовую разницу, поскольку часовой пояс Asia/Singapore в настоящее время имеет смещение от UTC +08: 00. В тот же момент, разное время настенных часов.

instant.toString(): 2017-01-23T12:34:56.123456789Z

zdt.toString(): 2017-01-23T20:34:56.123456789+08:00[Asia/Singapore]

Преобразование

Избегайте устаревшего класса java.util.Date. Но если вам нужно, вы можете обратить. Посмотрите на новые методы, добавленные к старым классам.

java.util.Date date = Date.from( instant ) ;

... идти в другую сторону...

Instant instant = myJavaUtilDate.toInstant() ;

Дата только

Только для даты, используйте LocalDate.

LocalDate ld = zdt.toLocalDate() ;

О java.time

Среда java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

Проект Joda-Time, который теперь находится в режиме обслуживания, рекомендует выполнить переход на классы java.time.

Чтобы узнать больше, см. Учебное пособие по Oracle. И поищите в Кару множество примеров и объяснений. Спецификация: JSR 310.

Где взять классы java.time?

  • Java SE 8, Java SE 9 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые мелкие функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 & 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и еще more.

Ответ 4

Вы найдете этот сайт очень полезным https://www.programcreek.com/java-api-examples/index.php?class=java.text.SimpleDateFormat&method=applyLocalizedPattern Также я разместил здесь метод, который вы можете реорганизовать для удовлетворения ваших потребностей. java date исключение синтаксического анализа во время согласования UTC с местным часовым поясом