Создайте объект datetime для представления экземпляра независимо от часовых поясов для работы со строками - PullRequest
0 голосов
/ 07 апреля 2019

У меня есть в виде String следующее:
month в форме 01-12
day в форме 01-31
year в форме, например, 2019
time формы, которую я не знаю, может ли она содержать миллисекунды или просто что-то вроде HH:MM
Все это должно представлять "временную метку", которая является точной, то есть не относительно временной зоны, а, например, если бы я сделал в качестве упрощенного примера:
day/month/year time это следует считать правильным, независимо от проблем с часовым поясом (надеюсь, я объясню это ясно).
Мой вопрос: каков наилучший способ создать какой-нибудь LocalTime или подобный объект из них, чтобы он не изменился из-за настроек локали и т. Д., И я могу правильно выполнить любые манипуляции со строками или получить эпоху?

1 Ответ

1 голос
/ 08 апреля 2019

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

Давайте возьмем пример из комментариев.Пассажир бронирует рейс со временем прибытия в Стамбул 25/04/2019 13:10.По договоренности время прибытия указывается по местному времени аэропорта прибытия.Поскольку Стамбул находится по смещению UTC +03: 00, время прибытия равно 2019-04-25 10:10 UTC.

Общепризнанной хорошей практикой является сохранение дат и времени в UTC * 1007.* в вашей базе данных.Если вы используете базу данных SQL, вы должны обычно использовать ее тип данных timestamp with time zone (часть «с часовым поясом» является ложью, поскольку вы не можете хранить временную метку с часовым поясом по вашему выбору; это всегда UTC,но я только что сказал, что это хорошая практика, так что это здорово).

Скажите, что вы получили это как ввод от пользователя: 25/04/2019 13:10.Преобразовать его в LocalDateTime легко (если вы знаете, как; пример кода ниже).Сохранение в UTC невозможно , если мы не знаем ни часовой пояс (например, Европа / Стамбул или Азия / Стамбул), ни смещение UTC (например, +03: 00).Представьте, что пассажир Джон Доу-младший никогда раньше не летал и не знает, как договориться о времени прибытия в местное время аэропорта прибытия.Таким образом, ему 13:10 может быть 13:10 в его часовом поясе (Америка / Чикаго, равное 18:10 UTC), 13:10 в часовом поясе отправления (Америка / Нью-Йорк, равное 17:10 UTC),13:10 UTC, 13:10 в часовом поясе прибытия (равном 10:10 UTC) или что-то еще.

Теперь давайте скажем, что мы делаем знаем, что ввод в Европе/ Часовой пояс Стамбула.Тогда преобразование в UTC является простым.Мы храним время как 2019-04-25 10:10 UTC.Теперь это не изменится из-за установки часового пояса компьютера или JVM.Это легко сравнить с другими временами UTC в вашей базе данных, и вы можете спокойно игнорировать часовые пояса при этом.Когда вам нужно представить время пользователю (например, распечатать его в билете), вы конвертируете время в Стамбул (13:10) (или в какой часовой пояс хочет пользователь).

Не делайте ' время эпохи , если вы не используете API, который требует их. Стандартная эпоха Java определяется как 1 января 1970 года в 00:00 UTC.Обратите внимание, что он определен в UTC, поэтому это четко определенный момент времени.Время эпохи - это количество секунд или миллисекунд с момента подписи со знаком.В нашем примере время прибытия составляет 1 556 187 000 секунд с начала эпохи, так что это время вашей эпохи.Как видите, он не предназначен для удобства чтения.Вы не захотите расшифровать файл журнала или запустить сеанс отладки, где время представлено таким образом.Вы также не хотите делать какие-либо запросы к базе данных, где время представлено таким же образом.

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

Типы даты / времени Java

Java предлагает следующеетипы даты и времени для нас:

  • A LocalDateTime - это дата и время дня без смещения UTC или часового пояса, например, 2019-04-25T13:10.Так что не определяет момент времени.
  • С другой стороны, Instant - это момент времени без смещения UTC или часового пояса.Так что не определяет дату и время дня.Он печатается в формате UTC (например, 2019-04-25T10:10Z) и внутренне представлен в виде количества секунд и наносекунд с начала эпохи.
  • * OffsetDateTime - это дата и время дня со смещением UTCнапример 2018-04-25T13:10+03:00.Таким образом, определяет момент времени, а определяет дату и время дня.
  • A ZonedDateTime - это дата и время дня со временем.зона , например 2018-04-25T13:10+03:00[Europe/Istanbul].Так что это тоже определяет момент времени и определяет дату и время дня.

Как это может выглядеть в коде

    DateTimeFormatter userInputFormatter = DateTimeFormatter.ofPattern("dd/MM/uuuu HH:mm");
    String userInput = "25/04/2019 13:10";
    ZoneId arrivalTimeZone = ZoneId.of("Europe/Istanbul");

    // Parse into a LocalDateTime
    LocalDateTime arrivalTimeLocal = LocalDateTime.parse(userInput, userInputFormatter);
    System.out.println("Arrival time:                           " + arrivalTimeLocal);

Время прибытияe: 2019-04-25T13: 10

    // Convert to Instant in order to have a well-defined point in time
    Instant arrivalTime = arrivalTimeLocal.atZone(arrivalTimeZone).toInstant();
    System.out.println("Arrival point in time (printed in UTC): " + arrivalTime);

Время прибытия (указано в UTC): 2019-04-25T10: 10: 00Z

    // Convert to UTC for storing in SQL database
    OffsetDateTime arrivalTimeUtc = arrivalTime.atOffset(ZoneOffset.UTC);
    System.out.println("Arrival time in UTC:                    " + arrivalTimeUtc);

Время прибытия в UTC: 2019-04-25T10: 10Z

Редактировать: Как я уже говорил, в вашей базе данных SQL вы обычно хотели бысохраните дату и время в формате UTC в столбце типа данных timestamp with time zone.Детали зависят от вашей базы данных и драйвера JDBC.Я считаю, что в MySQL (и, возможно, в других СУБД) тип будет просто timestamp.Это типичный пример:

    // Save into a database column of datatype timestamp with time zone
    PreparedStatement insert = yourDatabaseConnection.prepareStatement(
            "insert into your_table (your_timestamp_with_time_zone_column) values (?);");
    insert.setObject(1, arrivalTimeUtc);
    int rowsInserted = insert.executeUpdate();

Если у SQLite нет типа данных часового пояса или даты-времени, предпочитайте хранить формат ISO 8601 в символьном столбце, поскольку он более читабелен, чем время эпохи, в числовом столбце.Instant.toString() создает строку, необходимую для этого:

    PreparedStatement insert = yourDatabaseConnection.prepareStatement(
            "insert into your_table (your_varchar_column) values (?);");
    insert.setString(1, arrivalTime.toString());
    int rowsInserted = insert.executeUpdate();

Instant.parse проанализирует эту строку после получения.

    // Convert to arrival time zone, e.g., for printing on ticket
    String arrivalTimeForUser = arrivalTime.atZone(arrivalTimeZone)
            .format(userInputFormatter);
    System.out.println("Formatted arrival time in local time:   " + arrivalTimeForUser);

Форматированное время прибытия по местному времени: 25/04/2019 13: 10

Ссылки

...