У меня есть в виде String
следующее:month
в форме 01-12
day
в форме 01-31
year
который находится в форме, например. 2019
time
форму которой я не знаю, может ли она содержать миллисекунды или что-то вроде HH:MM
Все это должно представлять точную «метку времени», то есть не относительно часового пояса, а, например, если бы я делал как упрощенный пример:day/month/year time
это следует считать правильным, независимо от каких-либо проблем с часовым поясом (надеюсь, я ясно объяснил это).
Мой вопрос: как лучше всего создать из них некоторый LocalTime
или аналогичный объект, чтобы он не менялся из-за какой-либо настройки локали и т. д., И я мог правильно выполнять любые манипуляции со строками, которые мне нужны, или получить эпоху?
Вы можете преобразовать свой объект localDateTime в миллисекунды эпохи localDateTimeObj.toInstant(UTC).toEpochMilli();
@MickMnemonic: именно в этом проблема. Как правильно хранить эту информацию. Если я смогу правильно преобразовать его в объект даты, я думаю, что смогу хранить/извлекать позже так, как мне нужно, верно?
@arseniyandru: у меня нет объекта localDateTime. Мой вопрос в том, как я могу правильно создать такой объект на основе этого ввода
Создать объект LocalDateTime
, представляющий дату и время суток, очень просто. Например LocalDateTime.of(2019, 4, 7, 18, 59)
. Но чтобы преобразовать его в секунды или миллисекунды с эпохи, вам нужно будет знать либо часовой пояс, либо смещение от UTC. Я не могу понять, как год+месяц+день+час+минута могут быть точными и не относиться к какому-либо часовому поясу. Для меня это не имеет смысла.
Похоже, это не то, о чем вы просите, но я хочу упомянуть на всякий случай: Instant
— это момент времени, который напрямую переводится в секунды или миллисекунды с начала эпохи и не зависит от часовых поясов. Это нет дата и время суток. Может быть, если вы приведете вполне конкретный пример строки, которая у вас есть, и желаемое время эпохи, это поможет понять?
В любом случае я убежден, что java.time, современный API даты и времени Java, предложит вам все, что вам нужно. Учебник Oracle здесь.
@OleV.V.: Может быть, моя терминология плохая, но при упоминании эпохи я имел в виду некий числовой формат, который облегчил бы запрос, если бы я сохранил его в базе данных. Если эпоха имеет смысл только с некоторым часовым поясом, а у меня нет часового пояса, то как я могу сохранить его таким образом, чтобы было легко запрашивать более ранние и прошлые даты? Извините, если я сбиваю вас с толку, я думаю, что не понял что-то, связанное с манипулированием датами, и в итоге получил неправильные темы.
@OleV.V.: По аналогии, когда вы бронируете рейс, время прибытия указывается по местному времени. Итак, если у вас есть это как строка, она не зависит от часового пояса, верно?
Давайте продолжить обсуждение в чате.
Возможный дубликат Лучшие практики Java для управления/хранения дат для географически разных пользователей
@OleV.V.: Почему-то не могу написать в чат
@ОлеВ.В. Вы правы в приведенном вами примере. Как UTC поможет в этом случае?
Поскольку Стамбул находится со смещением +03:00, вы сохраняете 10:10 (UTC) в своей базе данных. Затем можно легко сравнить с другим временем UTC в вашей базе данных, и при этом вы можете безопасно игнорировать часовые пояса. Когда вам нужно представить время пользователю (например, напечатать его на билете), вы конвертируете время в стамбульское (13:10).
@OleV.V.: Итак, если у меня есть часовой пояс вместе с упомянутой мной информацией, я мог бы преобразовать в UTC (это эпоха, верно?) И сохранить в базе данных и преобразовать по мере необходимости?
В принципе правильно, да. UTC и эпоха не совсем одно и то же, но содержат одну и ту же информацию, поэтому преобразование в обоих направлениях однозначно.
@OleV.V.: возможно ли создать экземпляр класса DateTime или LocalTime с этими данными без часового пояса и использовать его правильно? например, хранить в миллисекундах или преобразовывать в строку? Или из-за отсутствующего часового пояса объект не будет работать должным образом?
Мне кажется, что вы просите здесь невозможного или бессмысленного.
Возьмем пример из комментариев. Пассажир бронирует рейс со временем прибытия в Стамбул 25/04/2019 13:10
. По соглашению время прибытия указывается по местному времени аэропорта прибытия. Поскольку Стамбул находится со смещением UTC +03:00, время прибытия равно 25.04.2019 10:10 UTC.
Общепризнанной хорошей практикой является хранение даты и времени в универсальное глобальное время в вашей базе данных. Если вы используете базу данных 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 предлагает нам следующие типы даты и времени:
LocalDateTime
— это дата и время суток без смещения UTC или часового пояса, например 2019-04-25T13:10
. Таким образом, нет определяет момент времени.Instant
— это момент времени без смещения UTC или часового пояса. Таким образом, нет определяет дату и время суток. Он печатается в формате UTC (например, 2019-04-25T10:10Z
) и внутренне представлен в виде количества секунд и наносекунд с начала эпохи.OffsetDateTime
— это смещение даты и времени дня с участием UTC, например 2018-04-25T13:10+03:00
. Таким образом, этот делает определяет момент времени, а делает определяет дату и время суток.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);
Arrival time: 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);
Arrival point in time (printed in 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);
Arrival time in 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);
Formatted arrival time in local time: 25/04/2019 13:10
Спасибо за ваш полезный ответ. Один вопрос: где у вас есть пример для // Convert to UTC for storing in SQL database
, разве он не должен храниться в миллисекундах?
Если вы имеете в виду миллисекунды с начала эпохи, я попытался возразить против этого в абзаце «Не заморачивайтесь с эпохами…». Детали зависят от возможностей вашей базы данных и драйвера JDBC или того, что вы используете для доступа к базе данных из Java.
Что меня смущает, так это OffsetDateTime
в вашем примере. Это специфично для Java, и если мне нужно сохранить его в MySQL (отметка времени) или SQLite (время int utc) и т. д., мне нужно будет преобразовать его во что-то. Моя первая мысль - преобразовать в миллисекунды, чтобы соответствовать всем случаям. Но из вашего комментария кажется, что это не очень хорошая идея? Не могли бы вы объяснить это мне?
Да помогает спасибо. А как насчет обмена с другими системами. Например. получение его из другой системы и локальное хранение. Какой формат лучше всего подходит для обмена сетевыми API, совместимого со всеми? Кроме того, если я вас понял, пока я сохраняю часовой пояс UTC +, я могу делать с ним все, что мне нужно, верно?
Последнее верно. Для обмена используйте Формат ISO 8601 везде, где это возможно.
Если я использую ISO 8601
, как вы упомянули, я могу сохранить либо (2019-04-25T10:10Z, Europe/Istanbul)
, либо 2019-04-25T10:10Z+03:00
. Но я не уверен, будет ли второй формат хорошо играть или нет. Я имею в виду, что во втором формате это местное время, которое смещено на +03 от UTC, поэтому это полная информация. Так какой хранить?
Это зависит от требований. Для будущих дат безопаснее использовать первое, чтобы учесть возможность того, что политики могут изменить смещение до наступления даты. В противном случае, если вам нужно смещение (+03:00), а не название часового пояса (Европа/Стамбул), сохраните 2019-04-25T13:10+03:00
, поэтому местное время со смещением. Это ISO 8601, и его легко преобразовать в правильный OffsetDateTime
.
На данный момент вопрос кажется слишком широким. Где вы храните дату? Какие манипуляции со строками вам нужно выполнить? Пожалуйста, поделитесь любым кодом, который вы, возможно, написали, чтобы проиллюстрировать поставленную задачу.