Вопрос:

Java Convert GMT / UTC по местному времени не работает должным образом

java simpledateformat utc date-formatting timestamp-with-timezone

103994 просмотра

6 ответа

1692 Репутация автора

Чтобы показать воспроизводимый сценарий, я делаю следующее

  1. Получить текущее системное время (местное время)

  2. Конвертировать местное время в UTC // Работает отлично До здесь

  3. Обратное время UTC, назад к местному времени. Использовали 3 разных подхода (перечислены ниже), но все 3 подхода сохраняют время только в UTC.

    {

    long ts = System.currentTimeMillis();
    Date localTime = new Date(ts);
    String format = "yyyy/MM/dd HH:mm:ss";
    SimpleDateFormat sdf = new SimpleDateFormat (format);
    
    // Convert Local Time to UTC (Works Fine) 
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date gmtTime = new Date(sdf.format(localTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 1
    sdf.setTimeZone(TimeZone.getDefault());        
    localTime = new Date(sdf.format(gmtTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 2 using DateFormat
    DateFormat df = new SimpleDateFormat (format);
    df.setTimeZone(TimeZone.getDefault());
    localTime = df.parse((df.format(gmtTime)));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Approach 3
    Calendar c = new GregorianCalendar(TimeZone.getDefault());
    c.setTimeInMillis(gmtTime.getTime());
    System.out.println("Local Time " + c.toString());
    

    }

Автор: Vinod Jayachandran Источник Размещён: 15.10.2013 07:19

Ответы (6)


1 плюс

610 Репутация автора

Я настоятельно рекомендую использовать Joda Time http://joda-time.sourceforge.net/faq.html

Автор: Tomasz Waszczyk Размещён: 15.10.2013 07:21

4 плюса

140632 Репутация автора

Joda времени


ОБНОВЛЕНИЕ: проект Joda-Time сейчас находится в режиме обслуживания , и команда советует перейти на классы java.time . Смотрите Учебник по Oracle .

Посмотрите мой другой ответ, используя лучшие в отрасли классы java.time .


Обычно мы считаем плохой формой на StackOverflow.com отвечать на конкретный вопрос, предлагая альтернативную технологию. Но в случае классов даты, времени и календаря, связанных с Java 7 и более ранними версиями, эти классы настолько заведомо плохи как в дизайне, так и в исполнении, что я вынужден предложить вместо этого использовать стороннюю библиотеку: Joda-Time .

Joda-Time работает, создавая неизменные объекты . Поэтому вместо того, чтобы изменять часовой пояс объекта DateTime, мы просто создаем новый DateTime с другим назначенным часовым поясом.

Ваша главная задача использования местного и UTC-времени очень проста в Joda-Time, занимая всего 3 строки кода.

    org.joda.time.DateTime now = new org.joda.time.DateTime();
    System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
    System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );

Выход при запуске на западном побережье Северной Америки может быть:

Local time in ISO 8601 format: 2013-10-15T02:45:30.801-07:00

UTC (Zulu) time zone: 2013-10-15T09:45:30.801Z

Вот класс с несколькими примерами и дальнейшими комментариями. Использование Joda-Time 2.5.

/**
 * Created by Basil Bourque on 2013-10-15.
 * © Basil Bourque 2013
 * This source code may be used freely forever by anyone taking full responsibility for doing so.
 */
public class TimeExample {
    public static void main(String[] args) {
        // Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 8 and earlier.
        // http://www.joda.org/joda-time/

        // Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
        // JSR 310 was inspired by Joda-Time but is not directly based on it.
        // http://jcp.org/en/jsr/detail?id=310

        // By default, Joda-Time produces strings in the standard ISO 8601 format.
        // https://en.wikipedia.org/wiki/ISO_8601
        // You may output to strings in other formats.

        // Capture one moment in time, to be used in all the examples to follow.
        org.joda.time.DateTime now = new org.joda.time.DateTime();

        System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
        System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );

        // You may specify a time zone in either of two ways:
        // • Using identifiers bundled with Joda-Time
        // • Using identifiers bundled with Java via its TimeZone class

        // ----|  Joda-Time Zones  |---------------------------------

        // Time zone identifiers defined by Joda-Time…
        System.out.println( "Time zones defined in Joda-Time : " + java.util.Arrays.toString( org.joda.time.DateTimeZone.getAvailableIDs().toArray() ) );

        // Specify a time zone using DateTimeZone objects from Joda-Time.
        // http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
        org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
        System.out.println( "Paris France (Joda-Time zone): " + now.toDateTime( parisDateTimeZone ) );

        // ----|  Java Zones  |---------------------------------

        // Time zone identifiers defined by Java…
        System.out.println( "Time zones defined within Java : " + java.util.Arrays.toString( java.util.TimeZone.getAvailableIDs() ) );

        // Specify a time zone using TimeZone objects built into Java.
        // http://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html
        java.util.TimeZone parisTimeZone = java.util.TimeZone.getTimeZone( "Europe/Paris" );
        System.out.println( "Paris France (Java zone): " + now.toDateTime(org.joda.time.DateTimeZone.forTimeZone( parisTimeZone ) ) );

    }
}
Автор: Basil Bourque Размещён: 15.10.2013 10:00

56 плюса

2127 Репутация автора

Решение

Я также рекомендую использовать Йоду, как упоминалось ранее.

Решение вашей проблемы с использованием только стандартных объектов Java Date может быть выполнено следующим образом:

    // **** YOUR CODE **** BEGIN ****
    long ts = System.currentTimeMillis();
    Date localTime = new Date(ts);
    String format = "yyyy/MM/dd HH:mm:ss";
    SimpleDateFormat sdf = new SimpleDateFormat(format);

    // Convert Local Time to UTC (Works Fine)
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date gmtTime = new Date(sdf.format(localTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:"
            + gmtTime.toString() + "," + gmtTime.getTime());

    // **** YOUR CODE **** END ****

    // Convert UTC to Local Time
    Date fromGmt = new Date(gmtTime.getTime() + TimeZone.getDefault().getOffset(localTime.getTime()));
    System.out.println("UTC time:" + gmtTime.toString() + "," + gmtTime.getTime() + " --> Local:"
            + fromGmt.toString() + "-" + fromGmt.getTime());

Выход:

Local:Tue Oct 15 12:19:40 CEST 2013,1381832380522 --> UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000
UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000 --> Local:Tue Oct 15 12:19:40 CEST 2013-1381832380000
Автор: trylimits Размещён: 15.10.2013 10:19

2 плюса

9257 Репутация автора

У вас есть дата с известным часовым поясом (Здесь Europe/Madrid) и целевой часовой пояс ( UTC)

Вам просто нужно два SimpleDateFormats:

        long ts = System.currentTimeMillis ();
        Дата localTime = новая дата (тс);

        SimpleDateFormat sdfLocal = new SimpleDateFormat ("гггг / мм / дд чч: мм: сс");
        sdfLocal.setTimeZone (TimeZone.getTimeZone ( "Europe / Madrid"));

        SimpleDateFormat sdfUTC = новый SimpleDateFormat ("гггг / мм / дд чч: мм: сс");
        sdfUTC.setTimeZone (TimeZone.getTimeZone ( "UTC"));

        // Преобразование местного времени в UTC
        Date utcTime = sdfLocal.parse (sdfUTC.format (localTime));
        System.out.println ("Local:" + localTime.toString () + "," + localTime.getTime () + "-> время UTC:" + utcTime.toString () + "-" + utcTime.getTime ( ));

        // Обратное преобразование времени UTC в местное время
        localTime = sdfUTC.parse (sdfLocal.format (utcTime));
        System.out.println ("UTC:" + utcTime.toString () + "," + utcTime.getTime () + "-> Местное время:" + localTime.toString () + "-" + localTime.getTime ( ));

Таким образом, после того, как вы увидите, что он работает, вы можете добавить этот метод к своим утилитам:

    public Date convertDate (Date dateFrom, String fromTimeZone, String toTimeZone) выдает ParseException {
        Образец строки = "гггг / мм / дд чч: мм: сс";
        SimpleDateFormat sdfFrom = new SimpleDateFormat (шаблон);
        sdfFrom.setTimeZone (TimeZone.getTimeZone (fromTimeZone));

        SimpleDateFormat sdfTo = новый SimpleDateFormat (шаблон);
        sdfTo.setTimeZone (TimeZone.getTimeZone (toTimeZone));

        Date dateTo = sdfFrom.parse (sdfTo.format (dateFrom));
        вернуть dateTo;
    }
Автор: albfan Размещён: 01.08.2016 09:04

2 плюса

41238 Репутация автора

Я присоединяюсь к хору рекомендуя , что вы пропустите уже давно устаревшие классы Date, Calendar, SimpleDateFormatи друг. В частности, я бы предостерег от использования устаревших методов и конструкторов Dateкласса, таких как Date(String)используемый вами конструктор. Они устарели, потому что они не работают надежно в разных часовых поясах, поэтому не используйте их. И да, большинство конструкторов и методов этого класса устарели.

В то время как вы задавали вопрос, Joda-Time был (насколько я знаю) явно лучшей альтернативой, время снова пошло дальше. Сегодня Joda-Time - в значительной степени законченный проект, и его разработчики рекомендуют использовать java.timeвместо этого современный Java-интерфейс даты и времени. Я покажу вам, как.

    ZonedDateTime localTime = ZonedDateTime.now(ZoneId.systemDefault());

    // Convert Local Time to UTC 
    OffsetDateTime gmtTime
            = localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
    System.out.println("Local:" + localTime.toString() 
            + " --> UTC time:" + gmtTime.toString());

    // Reverse Convert UTC Time to Local time
    localTime = gmtTime.atZoneSameInstant(ZoneId.systemDefault());
    System.out.println("Local Time " + localTime.toString());

Для начала, обратите внимание, что код не только вдвое меньше вашего, но и понятнее.

На моем компьютере код печатает:

Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]

Я пропустил миллисекунды из эпохи. Вы всегда можете получить их, System.currentTimeMillis();как в вашем вопросе, и они не зависят от часового пояса, поэтому я не нашел их здесь интересными.

Я нерешительно сохранил ваше имя переменной localTime. Я думаю, что это хорошее имя. В современном API есть класс с именем LocalTime, поэтому использование этого имени, только без заглавной буквы, LocalTimeможет привести к путанице в объекте, у которого нет типа (a LocalTimeне содержит информацию о часовом поясе, которую мы должны здесь сохранить, чтобы иметь возможность правильное преобразование, оно также содержит только время суток, а не дату).

Ваше преобразование из местного времени в UTC было неправильным и невозможным

Устаревший Dateкласс не содержит никакой информации о часовом поясе (вы можете сказать, что внутренне он всегда использует UTC), поэтому нет такой вещи, как преобразование Dateодного часового пояса в другой. Когда я просто запустил ваш код на моем компьютере, первая напечатанная строка была:

Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000

07:25:45 CESTправильно, конечно. Правильное время UTC было бы 05:25:45 UTC, но он CESTснова говорит , что неверно.

Теперь вам больше никогда не понадобится этот Dateкласс, :-), но если вы когда-нибудь захотите, то обязательно прочитайте « Все о java.util.Date» в кодовом блоге Джона Скита.

Вопрос: Могу ли я использовать современный API с моей версией Java?

Если вы используете хотя бы Java 6 , вы можете.

  • В Java 8 и более поздних версиях новый API поставляется встроенным.
  • В Java 6 и 7 получают ThreeTen Backport , бэкпорт новых классов (это ThreeTen для JSR-310, где впервые был определен современный API).
  • На Android используйте Android-версию ThreeTen Backport. Он называется ThreeTenABP, и я думаю, что в этом вопросе есть замечательное объяснение : как использовать ThreeTenABP в Android Project .
Автор: Ole V.V. Размещён: 02.09.2017 05:33

3 плюса

140632 Репутация автора

ТЛ; др

Instant.now()                           // Capture the current moment in UTC.
.atZone( ZoneId.systemDefault() )       // Adjust into the JVM's current default time zone. Same moment, different wall-clock time. Produces a `ZonedDateTime` object.
.toInstant()                            // Extract a `Instant` (always in UTC) object from the `ZonedDateTime` object.
.atZone( ZoneId.of( "Europe/Paris" ) )  // Adjust the `Instant` into a specific time zone. Renders a `ZonedDateTime` object. Same moment, different wall-clock time.
.toInstant()                            // And back to UTC again.

java.time

Современный подход использует классы java.time, которые вытеснили проблемные старые унаследованные классы даты и времени ( Date, Calendarи т. Д.).

Использование вами слова «local» противоречит использованию в классе java.time . В java.time «местный» означает любой населенный пункт или все населенные пункты, но не один конкретный населенный пункт. В java.time классы с именами , начинающимися с «Local ...» все не хватает какой - либо концепции временной зоны или офсетным из-UTC. Таким образом, они не представляют конкретный момент, они не являются точкой на временной шкале, в то время как ваш Вопрос связан с моментами, точками на временной шкале, просматриваемыми в различные часы.

Получить текущее системное время (местное время)

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

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

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

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

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time.

В качестве ярлыка, вы можете пропустить использование, Instantчтобы получить ZonedDateTime.

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;

Конвертировать местное время в UTC // Работает отлично До здесь

Вы можете отрегулировать от зонированной даты и времени до UTC, извлекая Instantиз ZonedDateTime.

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Instant instant = zdt.toInstant() ;

Обратное время UTC, назад к местному времени.

Как показано выше, примените a, ZoneIdчтобы скорректировать тот же момент в другое время на настенных часах, используемое людьми определенного региона (часового пояса).

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

ZoneId zDefault = ZoneId.systemDefault() ;  // The JVM's current default time zone.
ZonedDateTime zdtDefault = instant.atZone( zDefault ) ;

ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ;  // The JVM's current default time zone.
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;  // The JVM's current default time zone.
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;

Возвращаясь к UTC с зонированной даты и времени, звоните ZonedDateTime::toInstant. Концептуально думайте об этом как: ZonedDateTime = Instant + ZoneId .

Instant instant = zdtAuckland.toInstant() ;

Все эти объекты, Instantи все три ZonedDateTimeобъекта представляют один и тот же момент одновременности, одну и ту же точку истории.

Использовали 3 разных подхода (перечислены ниже), но все 3 подхода сохраняют время только в UTC.

Забудьте о попытке исправить код , используя эти ужасные Date, Calendarи GregorianCalendarклассы. Это жалкий беспорядок плохого дизайна и недостатков. Тебе больше никогда не нужно их трогать. Если вам нужно взаимодействовать со старым кодом, который еще не обновлен до java.time , вы можете конвертировать туда и обратно с помощью новых методов преобразования, добавленных к старым классам.


О java.time

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

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

Чтобы узнать больше, смотрите Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии . Нет необходимости в строках, нет необходимости в java.sql.*классах.

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

  • Java SE 8 , Java SE 9 , Java SE 10 и более поздние версии
    • Встроенный.
    • Часть стандартного 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, и более .

Автор: Basil Bourque Размещён: 17.06.2018 07:38
Вопросы из категории :
32x32