ТЛ; др
LocalDateTime.of( // A `LocalDateTime` represents a set of *potential* moments along a range of about 26-27 hours. Not an actual moment, not a point on the timeline.
LocalDate.systemDefault() , // Get the JVM’s current default time zone. Can change at any moment *during* runtime. When crucial, always confirm with the user.
LocalTime.parse( "14:57" ) // Parse a string in standard ISO 8601 format as a time-of-day without regard for time zone or offset-from-UTC.
) // Returns a `LocalDateTime` object.
.atZone( // Determine an actual moment, a point on the timeline.
ZoneId( "Africa/Tunis" ) // Specify a time zone as `Continent/Region`, never as 3-4 letter pseudo-zones such as `PST`, `CST`, or `IST`.
) // Returns a `ZonedDateTime` object.
.toInstant() // Extracts a `Instant` object from the `ZonedDateTime` object, always in UTC by default.
Подробнее
На данный момент это немного сбивает с толку
Обработка даты и времени - очень запутанная работа.
Советы:
- Забудьте о своем часовом поясе. Думайте в терминах UTC , а не в своем собственном приходском часовом поясе.
- Узнайте разницу между реальными моментами (точками на временной шкале) и приближениями даты и времени, которые не на временной шкале, часто называемыми «локальными» значениями.
- Будьте внимательны при чтении переполнения стека или других мест в Интернете о дате и времени. Вы столкнетесь с плохим советом и многими неправильными решениями.
со всеми вариантами выбора; Отметка времени, дата, местное время и т. Д.
Никогда не используйте проблемные старые унаследованные классы даты и времени, включенные в самые ранние версии Java. Никогда не используйте java.sql.Timestamp
, java.util.Date
, java.util.Calendar
и т. Д.
➡ Использовать только классы в java.time пакете .
Классы java.time являются лидирующей в отрасли средой обработки данных и времени. Чрезвычайно хорошо продуманный и продуманный, с уроками, извлеченными из проекта Joda-Time , он добился успеха.
Кто-нибудь знает, как это сделать, или может дать некоторые советы / лучшие практики?
Вы можете сожалеть, что спросили. Продолжайте читать.
На данный момент я получил класс 'Flight' с типами данных Date; дата отправления и прибытия.
Итак, определите класс Flight
.
В реальной жизни полеты в будущем происходят достаточно далеко, и мы рискуем, что политики изменят определение часового пояса. Чаще всего эти изменения включают принятие / удаление / изменение Летнее время (DST) . Но произвольные изменения периодически вносятся по разным причинам. Мы могли бы обсудить мудрость / здравомыслие таких изменений, но факт в том, что они случаются. Они случаются довольно часто, так как политики, кажется, странно склонны к внесению этих изменений во всем мире во многих странах. И почти все они делают это с небольшим предупреждением, иногда всего за несколько недель. Или даже без предупреждения, как Северная Корея на этой неделе .
У меня нет понимания того, как на самом деле работают авиалинии, но из просмотра расписаний авиакомпаний и различных чтений, кажется, они пытаются сохранить свои расписания, используя зонированное время вылетающего населенного пункта. Таким образом, если вылет рейса запланирован на 6 часов утра, они сохраняют расписание рейсов на день раньше, в день и на следующий день после перехода на летнее время. Если это действительно общее намерение, это означает, что время убийства при одной посадке на летнее время усаживается без присмотра, и в то же время пытается сэкономить час на противоположном отключении летнего времени. Очевидно, Amtrak использует эту практику для своих поездов. Давайте продолжим с этим подходом.
Использование этого «мнимого» подхода к расписанию означает, что мы не можем точно определить точный момент, когда наступит 6 часов утра в будущем. Поэтому нам нужно записать наше желание на эту дату и это время суток без применения часового пояса. Но мы должны записать желаемый часовой пояс, чтобы мы знали, в каком контексте мы можем позже определить точный момент, когда достаточно близко по времени, чтобы нам не пришлось беспокоиться об изменении зоны.
Поэтому мы используем LocalDate
и LocalTime
типов, поскольку у них намеренно отсутствует какое-либо понятие часовой пояс (имя в Continent/Region
формат) или смещение от UTC (количество часов, минут и секунд).
Класс ZoneId
представляет часовой пояс.
Я использую слово Unzoned
в именах, чтобы напомнить нам, что эти значения не представляют фактические моменты на временной шкале. Слово «местный» обычно сбивает с толку новичков.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId ;
}
Что касается прибытияКроме того, сохраните ожидаемый промежуток времени для этого рейса, а не дату и время прибытия. Вы можете рассчитать прибытие, поэтому нет необходимости хранить его. Класс Duration
отслеживает количество часов, минут, секунд и доли секунды.
Чтобы вычислить прибытие, давайте вернем одно значение, используя класс LocalDateTime
, который просто комбинирует LocalDate
с LocalTime
. Мы могли бы использовать этот тип для создания единственной departureUnzoned
переменной-члена в нашем определении класса. Я использовал отдельные LocalDate
и LocalTime
в качестве строительных блоков, чтобы вы могли понять их. Поэтому многие программисты используют свою интуицию, а не документацию, чтобы предположить, что LocalDateTime
означает определенный момент в местности, когда на самом деле это означает прямо противоположное. (Вы найдете много неправильных ответов на переполнение стека, советуя LocalDateTime
, когда на самом деле Instant
или ZonedDateTime
следует использовать.)
Давайте добавим метод для расчета этого прибытия.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
public LocalDateTime arrivalDateTimeUnzoned () {
LocalDateTime departureUnzoned = LocalDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned );
LocalDateTime ldt = departureUnzoned.plus( this.duration );
return ldt;
}
}
Но возвращенное значение LocalDateTime
не учитывает часовой пояс. Обычно авиакомпании и поезда сообщают клиентам ожидаемое время прибытия, скорректированное с учетом часового пояса этого региона. Итак, нам нужен часовой пояс прибытия. И мы можем использовать эту зону при расчете прибытия, таким образом получая ZonedDateTime
. ZonedDateTime
- это особый момент, - точка на временной шкале, в отличие от LocalDateTime
. Но помните, что если мы планируем полеты в будущее, рассчитанный ZonedDateTime
изменится, если наш код будет запущен после того, как политики переопределят часовой пояс.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
}
Вернуться к части вашего вопроса об автоматическом определении даты. Это требует часового пояса. В любой данный момент дата меняется по всему миру. Подумай об этом. Через несколько минут после полуночи в Париже у Франции новый день, а в Монреале-Квебеке «вчера».
Мы можем запросить текущий часовой пояс JVM по умолчанию.
ZoneId userZoneId = ZoneId.systemDefault() ;
Но когда это важно, вы должны подтвердить это с пользователем.
ZoneId userZoneId = ZoneId.of( "America/Montreal" ) ;
Так что теперь мы можем добавить запрошенный вами конструктор, передав время дня (LocalTime
и угадав часовой пояс, используя текущее значение по умолчанию в JVM.
Но нам все еще нужны все остальные части. Так что дефолт с датой нас не сильно спасет.
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
// Constructor
public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
this.flightNumber = flightNumber;
this.departureTimeUnzoned = departureTimeUnzoned;
this.departureZoneId = departureZoneId;
this.duration = duration;
this.arrivalZoneId = arrivalZoneId;
// Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
ZoneId z = ZoneId.systemDefault();
LocalDate today = LocalDate.now( z );
this.departureDateUnzoned = today;
}
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
}
Давайте добавим toString
метод отчетности.
Мы представляем значения даты и времени в виде строк в стандартных форматах ISO 8601 . Классы java.time используют эти стандартные форматы при разборе / генерации строк. Z
на конце произносится Zulu
и означает UTC
.
Несмотря на то, что авиакомпании и поезда сообщают о времени своих клиентов в часовых поясах регионов, мы можем предположить, что они используют только UTC для своих целей. Класс Instant
специально представляет значения в формате UTC. Таким образом, наши toString
извлекают Instant
объекты из ZonedDateTime
объектов.
И мы добавляем main
метод для демонстрации. Вот полный класс, с import
и т. Д.
package com.basilbourque.example;
import java.time.*;
public class Flight {
private String flightNumber;
private LocalDate departureDateUnzoned;
private LocalTime departureTimeUnzoned;
private ZoneId departureZoneId;
private Duration duration;
private ZoneId arrivalZoneId;
// Constructor
public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
this.flightNumber = flightNumber;
this.departureTimeUnzoned = departureTimeUnzoned;
this.departureZoneId = departureZoneId;
this.duration = duration;
this.arrivalZoneId = arrivalZoneId;
// Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
ZoneId z = ZoneId.systemDefault();
LocalDate today = LocalDate.now( z );
this.departureDateUnzoned = today;
}
public ZonedDateTime arrivalDateTimeZoned () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
ZonedDateTime zdt = departureZoned.plus( this.duration );
return zdt;
}
@Override
public String toString () {
ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
String flightInUtc = departureZoned.toInstant().toString() + "/" + this.arrivalDateTimeZoned().toInstant().toString();
return "Flight{ " +
"flightNumber='" + this.flightNumber + '\'' +
" | departureDateUnzoned=" + this.departureDateUnzoned +
" | departureTimeUnzoned=" + this.departureTimeUnzoned +
" | departureZoneId=" + this.departureZoneId +
" | departureZoned=" + departureZoned +
" | duration=" + this.duration +
" | arrivalZoneId=" + this.arrivalZoneId +
" | calculatedArrival=" + this.arrivalDateTimeZoned() +
" | flightInUtc=" + flightInUtc +
" }";
}
public static void main ( String[] args ) {
LocalTime lt = LocalTime.of( 6 , 0 ); // 6 AM.
Flight f = new Flight( "A472" , lt , ZoneId.of( "America/Los_Angeles" ) , Duration.parse( "PT6H37M" ) , ZoneId.of( "America/Montreal" ) );
String output = f.toString();
System.out.println( output );
}
}
При запуске.
Flight {flightNumber = 'A472' | flightDateUnzoned = 2018-05-06 | выслать время отправлено = 06:00 | вылетZoneId = Америка / Лос-Анджелес | вылет = 2018-05-06T06: 00-07: 00 [America / Los_Angeles] | длительность = PT6H37M | прибытиеZoneId = Америка / Монреаль | рассчитанный доход = 2018-05-06T12: 37-07: 00 [America / Los_Angeles] | flightInUtc = 2018-05-06T13: 00: 00Z / 2018-05-06T19: 37: 00Z}
Чтобы использовать это с консоли, спросите пользователя о времени суток в 24-часовых часах. Разбор входной строки.
String input = "14:56" ; // 24-hour clock.
LocalTime lt = LocalTime.parse( input ) ;
Это далеко не полно для реальной работы. Но, надеюсь, это послужит наглядным примером.
О 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?
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval
, YearWeek
, YearQuarter
и more .