Конвертировать дату / время для данного часового пояса - Java - PullRequest
59 голосов
/ 06 октября 2011

Я хочу преобразовать эту метку времени GMT в GMT + 13:

2011-10-06 03:35:05

Я попробовал около 100 различных комбинаций DateFormat, TimeZone, Date, GregorianCalendar и т. Д., Чтобы попытаться выполнить эту ОЧЕНЬ базовую задачу.

Этот код делает то, что я хочу для ТЕКУЩЕГО ВРЕМЕНИ:

Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));

DateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");    
formatter.setTimeZone(TimeZone.getTimeZone("GMT+13"));  

String newZealandTime = formatter.format(calendar.getTime());

Но я хочу установить время, а не использовать текущее время.

Я обнаружил, что каждый раз, когда я пытаюсь установить время следующим образом:

calendar.setTime(new Date(1317816735000L));

используется TimeZone локальной машины.Это почему?Я знаю, что когда «new Date ()» возвращает время UTC + 0, так почему, когда вы устанавливаете время в миллисекундах, оно больше не предполагает, что время указано в UTC?

Возможно:

  1. Установка времени для объекта (Calendar / Date / TimeStamp)
  2. (возможно) Установка TimeZone начальной отметки времени (calendar.setTimeZone (...))
  3. Отформатируйте отметку времени с помощью нового часового пояса (formatter.setTimeZone (...)))
  4. Возвращает строку с новым часовым поясом.(formatter.format (calendar.getTime ()))

Заранее благодарен за любую помощь: D

Ответы [ 15 ]

38 голосов
/ 13 марта 2014

Для меня самый простой способ сделать это:

Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

//Here you say to java the initial timezone. This is the secret
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
//Will print in UTC
System.out.println(sdf.format(calendar.getTime()));    

//Here you set to your timezone
sdf.setTimeZone(TimeZone.getDefault());
//Will print on your default Timezone
System.out.println(sdf.format(calendar.getTime()));
29 голосов
/ 10 октября 2011

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

В любом случае, я отвлекся!Если у вас есть смещение UTC (лучше работать в UTC, чем смещения по Гринвичу), вы можете рассчитать время в миллисекундах и добавить его к своей отметке времени.Обратите внимание, что отметка времени SQL может отличаться от отметки времени Java, поскольку способ вычисления времени, прошедшего с начала эпохи, не всегда одинаков - в зависимости от технологий баз данных и операционных систем.

Я бы посоветовал вам использовать System.currentTimeMillis () как ваши метки времени, поскольку они могут обрабатываться более последовательно в Java, не беспокоясь о преобразовании меток времени SQL в объекты Date Java и т. д.*

Надеюсь, это полезно!

22 голосов
/ 06 октября 2011

Как всегда, я рекомендую прочитать эту статью о дате и времени на Java, чтобы вы ее поняли.

Основная идея состоит в том, что «под капотом» все делается в миллисекундах UTC с начала эпохи. Это означает, что проще всего работать без использования часовых поясов, за исключением форматирования строки для пользователя.

Поэтому я бы пропустил большинство предложенных вами шагов.

  1. Установка времени для объекта (дата, календарь и т. Д.).
  2. Установка часового пояса для объекта форматирования.
  3. Возвращает строку из средства форматирования.

В качестве альтернативы вы можете использовать Время Joda . Я слышал, что это гораздо более интуитивно понятный API даты и времени.

14 голосов
/ 02 сентября 2016

ТЛ; др

Instant.ofEpochMilli( 1_317_816_735_000L )
    .atZone( ZoneId.of( "Pacific/Auckland" ) )
    .format( DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( new Locale( "en" , "NZ" ) ) )

... тоже ... * * 1004

LocalDateTime.parse( "2011-10-06 03:35:05".replace( " " , "T" ) )
    .atZone( ZoneId.of( "Pacific/Auckland" ) )

java.time

В Вопросе и большинстве Ответов используются устаревшие классы даты и времени из самых ранних версий Java. Эти старые классы оказались неприятными и запутанными. Избежать их. Вместо этого используйте классы java.time.

ISO 8601

Ваша входная строка почти в стандартном формате ISO 8601. Просто замените ПРОБЕЛ в середине на T.

String input = "2011-10-06 03:35:05".replace( " " , "T" );

LocalDateTime

Теперь проанализируйте как LocalDateTime, поскольку на входе отсутствует какая-либо информация о смещении от UTC или часовом поясе. A LocalDateTime не имеет понятия смещения или часового пояса, поэтому не представляет фактический момент на временной шкале.

LocalDateTime ldt = LocalDateTime.parse( input );

ZoneOffset

Похоже, вы говорите, что из бизнес-контекста вы знаете, что эта строка предназначена для представления момента, который на 13 часов опережает UTC. Итак, мы создаем ZoneOffset.

ZoneOffset offset = ZoneOffset.ofHours( 13 ); // 13 hours ahead of UTC, in the far east of the globe.

OffsetDateTime

Примените его, чтобы получить объект OffsetDateTime. Это становится актуальным моментом на временной шкале.

OffsetDateTime odt = ldt.atOffset( offset);

ZoneId

Но тогда вы упомянули Новую Зеландию. Таким образом, вы имели в виду определенный часовой пояс. Часовой пояс - это смещение от UTC плюс набор правил для обработки аномалий, таких как переход на летнее время (DST). Таким образом, мы можем указать ZoneId ZonedDateTime, а не просто смещение.

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

ZoneId z = ZoneId.of( "Pacific/Auckland" );

ZonedDateTime

Применить ZoneId.

ZonedDateTime zdt = ldt.atZone( z );

Вы можете легко перейти в другую зону в тот же момент на временной шкале.

ZoneId zParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = zdt.withZoneSameInstant( zParis );  // Same moment in time, but seen through lens of Paris wall-clock time.

Отсчет с эпохи

Я настоятельно рекомендую не обрабатывать значения даты и времени в виде отсчетов от эпохи, например, миллисекунд с начала 1970 UTC. Но если нужно, создайте Instant из такого числа.

Instant instant = Instant.ofEpochMilli( 1_317_816_735_000L );

Затем назначьте часовой пояс, как показано выше, при желании, чтобы отойти от UTC.

ZoneId z = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt = instant.atZone( z );

Ваше значение 1_317_816_735_000L равно:

  • 2011-10-05T12:12:15Z (ср., 05 октября 2011 г. 12:12:15 по Гринвичу)
  • 2011-10-06T01:12:15+13:00[Pacific/Auckland] (четверг, 6 октября 2011 г., 01:12:15 в Окленде, Новая Зеландия).

Генерация строк

Чтобы сгенерировать строку в стандартном формате ISO 8601 , просто наберите toString. Обратите внимание, что ZonedDateTime разумно расширяет стандартный формат, добавляя название часового пояса в квадратных скобках.

String output = zdt.toString();

Для других форматов выполните поиск переполнения стека для класса DateTimeFormatter. Уже покрыто много раз.

Укажите FormatStyle и Locale.

Locale l = new Locale( "en" , "NZ" );
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( l );
String output = zdt.format( f );

Обратите внимание, что часовой пояс не имеет ничего общего с языком 1118 *. У вас может быть Europe/Paris дата-время, отображаемое на японском языке и культурные нормы, или Asia/Kolkata дата-время, отображаемое на португальском языке и бразильские культурные нормы.

О java.time

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

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

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

Большая часть функциональности java.time перенесена на Java 6 & 7 в ThreeTen-Backport и дополнительно адаптирована к Android в ThreeTenABP (см. Как пользоваться… ).

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

5 голосов
/ 09 марта 2015

Решение на самом деле довольно простое (чистый, простой Java):

System.out.println(" NZ Local Time: 2011-10-06 03:35:05");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localNZ = LocalDateTime.parse("2011-10-06 03:35:05",formatter);
ZonedDateTime zonedNZ = ZonedDateTime.of(localNZ,ZoneId.of("+13:00"));
LocalDateTime localUTC = zonedNZ.withZoneSameInstant(ZoneId.of("UTC")).toLocalDateTime();
System.out.println("UTC Local Time: "+localUTC.format(formatter));

ВЫХОД IS:

 NZ Local Time: 2011-10-06 03:35:05
UTC Local Time: 2011-10-05 14:35:05
4 голосов
/ 06 октября 2011

Посмотрел, и я не думаю, что в Java есть часовой пояс с GMT + 13. Поэтому я думаю, что вы должны использовать:

Calendar calendar = Calendar.getInstance();
//OR Calendar.getInstance(TimeZone.getTimeZone("GMT"));

calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY)+13);

Date d = calendar.getTime();

(Если затем изменить «GMT» на этот часовой пояс и удалить вторую строку кода)

OR

SimpleDateFormat df = new SimpleDateFormat();
df.setTimeZone(TimeZone.getTimeZone("GMT+13"));
System.out.println(df.format(c.getTime()));

Если вы хотите установить определенное время / дату, вы также можете использовать:

    calendar.set(Calendar.DATE, 15);
calendar.set(Calendar.MONTH, 3);
calendar.set(Calendar.YEAR, 2011);
calendar.set(Calendar.HOUR_OF_DAY, 13); 
calendar.set(Calendar.MINUTE, 45);
calendar.set(Calendar.SECOND, 00);
2 голосов
/ 12 декабря 2013

Joda-Time

Классы java.util.Date/Calendar являются беспорядочными, и их следует избегать.

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

Вот ваш ответ с использованием библиотеки Joda-Time 2.3.Очень просто.

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

Если бы вы поместили T в середине вашей строки вместо пробела, вы могли бы пропустить первые две строки кода, имея дело с средством форматирования для анализа строки.Конструктор DateTime может принимать строку в формате ISO 8601 .

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;

// Parse string as a date-time in UTC (no time zone offset).
DateTimeFormatter formatter = org.joda.time.format.DateTimeFormat.forPattern( "yyyy-MM-dd' 'HH:mm:ss" );
DateTime dateTimeInUTC = formatter.withZoneUTC().parseDateTime( "2011-10-06 03:35:05" );

// Adjust for 13 hour offset from UTC/GMT.
DateTimeZone offsetThirteen = DateTimeZone.forOffsetHours( 13 );
DateTime thirteenDateTime = dateTimeInUTC.toDateTime( offsetThirteen );

// Hard-coded offsets should be avoided. Better to use a desired time zone for handling Daylight Saving Time (DST) and other anomalies.
// Time Zone list… http://joda-time.sourceforge.net/timezones.html
DateTimeZone timeZoneTongatapu = DateTimeZone.forID( "Pacific/Tongatapu" );
DateTime tongatapuDateTime = dateTimeInUTC.toDateTime( timeZoneTongatapu );

Дамп этих значений…

System.out.println( "dateTimeInUTC: " + dateTimeInUTC );
System.out.println( "thirteenDateTime: " + thirteenDateTime );
System.out.println( "tongatapuDateTime: " + tongatapuDateTime );

При запуске…

dateTimeInUTC: 2011-10-06T03:35:05.000Z
thirteenDateTime: 2011-10-06T16:35:05.000+13:00
tongatapuDateTime: 2011-10-06T16:35:05.000+13:00
1 голос
/ 18 декабря 2013

Мы можем справиться с этим, используя значение смещения

 public static long convertDateTimeZone(long lngDate, String fromTimeZone,
        String toTimeZone){
    TimeZone toTZ = TimeZone.getTimeZone(toTimeZone);
    Calendar toCal = Calendar.getInstance(toTZ);        

    TimeZone fromTZ = TimeZone.getTimeZone(fromTimeZone);
    Calendar fromCal = Calendar.getInstance(fromTZ);
    fromCal.setTimeInMillis(lngDate);
    toCal.setTimeInMillis(fromCal.getTimeInMillis()
            + toTZ.getOffset(fromCal.getTimeInMillis())
            - TimeZone.getDefault().getOffset(fromCal.getTimeInMillis()));      
    return toCal.getTimeInMillis();
}

Фрагмент кода теста:

 System.out.println(new Date().getTime())
 System.out.println(convertDateTimeZone(new Date().getTime(), TimeZone
                .getDefault().getID(), "EST"));

Выход: 1387353270742 1387335270742

1 голос
/ 06 октября 2011

Я попробую этот код

try{
            SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss Z");
            Date datetime = new Date();

            System.out.println("date "+sdf.format(datetime));

            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

            System.out.println("GMT "+ sdf.format(datetime));

            sdf.setTimeZone(TimeZone.getTimeZone("GMT+13"));

            System.out.println("GMT+13 "+ sdf.format(datetime));

            sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

            System.out.println("utc "+sdf.format(datetime));

            Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));

            DateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");    
            formatter.setTimeZone(TimeZone.getTimeZone("GMT+13"));  

            String newZealandTime = formatter.format(calendar.getTime());

            System.out.println("using calendar "+newZealandTime);

        }catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

и получение этого результата

date 06-10-2011 10:40:05 +0530
GMT 06-10-2011 05:10:05 +0000 // here getting 5:10:05
GMT+13 06-10-2011 06:10:05 +1300 // here getting 6:10:05
utc 06-10-2011 05:10:05 +0000
using calendar 06 Oct 2011 18:10:05 GMT+13:00
0 голосов
/ 15 июля 2019

Я хотел бы дать современный ответ.

Вы не должны действительно хотеть преобразовывать дату и время из строки с одним смещением по Гринвичу в строку с другим смещением по Гринвичу и с другимформат.Скорее в вашей программе храните мгновение (момент времени) как надлежащий объект даты и времени.Только когда вам нужно дать строковый вывод, отформатируйте ваш объект в нужную строку.

java.time

Синтаксический ввод

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE)
            .appendLiteral(' ')
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .toFormatter();

    String dateTimeString = "2011-10-06 03:35:05";
    Instant instant = LocalDateTime.parse(dateTimeString, formatter)
            .atOffset(ZoneOffset.UTC)
            .toInstant();

ДляБольшинство целей Instant - хороший выбор для хранения момента времени.Если вам нужно было явно указать, что дата и время пришли из GMT, используйте вместо этого OffsetDateTime.

Преобразование, форматирование и вывод на печать

    ZoneId desiredZone = ZoneId.of("Pacific/Auckland");
    Locale desiredeLocale = Locale.forLanguageTag("en-NZ");
    DateTimeFormatter desiredFormatter = DateTimeFormatter.ofPattern(
            "dd MMM uuuu HH:mm:ss OOOO", desiredeLocale);

    ZonedDateTime desiredDateTime = instant.atZone(desiredZone);
    String result = desiredDateTime.format(desiredFormatter);
    System.out.println(result);

Это напечатано:

06 октября 2011 16:35:05 GMT + 13: 00

Я указал часовой пояс Pacific / Auckland, а не смещение, которое вы упомянули, +13: 00.Я понял, что вы хотели новозеландское время, и Pacific / Auckland лучше расскажет об этом читателю.Часовой пояс также учитывает летнее время (DST), поэтому вам не нужно учитывать это в своем собственном коде (для большинства целей).

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

OOOO в строке шаблонов форматирования - это один из способов печати смещения, что может быть лучшечем печатать сокращение часового пояса, вы получите от z, так как сокращения часового пояса часто бывают неоднозначными.Если вы хотите NZDT (для новозеландского летнего времени), просто поставьте вместо него z.

Ваши вопросы

Я отвечу на ваши пронумерованные вопросы относительнок современным классам в java.time.

Можно:

  1. Установить время на объекте

НетСовременные классы неизменны.Вам необходимо с самого начала создать объект с желаемой датой и временем (это имеет ряд преимуществ, включая безопасность потоков).

(возможно) Установка временной зоны начальной отметки времени

Метод atZone, который я использую в коде, возвращает ZonedDateTime с указанным часовым поясом.Другие классы даты и времени имеют похожий метод, иногда называемый atZoneSameInstant или другими именами.

Отформатируйте отметку времени с новой временной зоной

С преобразованием java.time в новый часовой пояс и форматированием выполняются два отдельных шага, как показано.

Вернуть строку с новым часовым поясом.

Да, преобразовать в нужный часовой пояс, как показано, и отформатировать, как показано.

Я нашелчто в любое время я пытаюсь установить время следующим образом:

calendar.setTime(new Date(1317816735000L));

используется TimeZone локальной машины.Почему это так?

Это не то, что вы думаете, и это хорошо, если показать лишь пару (многих) проблем дизайна со старыми классами.

  • ADate не имеет часового пояса.Только когда вы печатаете его, его метод toString захватывает ваш местный часовой пояс и использует его для рендеринга строки.Это верно и для new Date().Это поведение сбило с толку многих программистов за последние 25 лет.
  • A Calender имеет часовой пояс.Он не меняется, когда вы делаете calendar.setTime(new Date(1317816735000L));.

Ссылка

Учебное пособие по Oracle: Дата и время , объясняющее, как использовать java.time.

...