JavaRush/Java блог/Архив info.javarush/Интуитивно понятная, надежная библиотека для работы с вре...
theGrass
24 уровень

Интуитивно понятная, надежная библиотека для работы с временем и датами, наконец-то появилась в Java (Часть 2).

Статья из группы Архив info.javarush
участников
Время суток
    Итак, идем дальше. Следующей сущностью, после даты, является время суток, представляемое классом LocalTime. Классический пример - представление времени работы магазина, скажем с 7:00 до 23:00. Магазины открываются в это время по всей стране, но фактическое время будет различное, в зависимости от часового пояса.     LocalTime это класс-значение, в котором хранится только время, без ассоциированной даты и часового пояса. При добавлении или вычитании временного промежутка, он обрежется по полночи. То есть, 20:00 плюс 6 часов это 2:00.     Использование LocalTime похоже на LocalDate: LocalTime time = LocalTime.of(20, 30); int hour = date.getHour(); // 20 int minute = date.getMinute(); // 30 time = time.withSecond(6); // 20:30:06 time = time.plusMinutes(3); // 20:33:06     Модификаторы могут работать и с LocalTime, однако операции с временем обычно не настолько сложны чтобы требовалось использование модификаторов.
Комбинирование даты и времени
    Следующий класс который мы рассмотрим - LocalDateTime. Этот класс-значение является комбинацией LocalDate и LocalTime. Он представляет и дату и время, без часового пояса.     LocalDateTime может быть создан или напрямую, или комбинируя дату и время: LocalDateTime dt1 = LocalDateTime.of(2014, Month.JUNE, 10, 20, 30); LocalDateTime dt2 = LocalDateTime.of(date, time); LocalDateTime dt3 = date.atTime(20, 30); LocalDateTime dt4 = date.atTime(time);     Третий и четвертый варианты используют метод atTime(), который предоставляет гибкий способ комбинировать дату и время. Большинство системных классов даты и времени имеют «at» методы, которые могут быть использованы при комбинировании вашего объекта с другим чтобы создать более сложный.     Другие методы класса LocalDateTime похожи на аналогичные из LocalDate и LocalTime. Сходные шаблоны наименования методов помогают легче изучить API. В этой таблице перечислены все задействованные префиксы методов: Интуитивно понятная, надежная библиотека для работы с временем и датами, наконец-то появилась в Java (Часть 2). - 1
Instant
    Когда мы имеем дело с датами и временем, обычно мы работаем с годами, месяцами, днями, часами, минутами, секундами. Однако, это лишь одна модель времени, которую можно назвать «человеческая». Вторая часто используемая модель - «машинного» или «непрерывного» времени. В этой модели точка на оси времени представлена одним большим числом. Данный подход упрощает алгоритмы рассчета, и используется для хранения времени в операционной системе Unix, там время представлено числом секунд прошедших с 1го января 1970го года. Аналогично, в Java время хранится как число миллисекунд прошедших с 1го января 1970го года.     Машинный подход к рассчетам времени в java.time API обеспечивается классом-значением Instant. Он предоставляет возможность представить точку на оси времени без всей сопутствующей информации, такой как часовой пояс. Фактически, данный класс содержит в себе число наносекунд прошедшее с полуночи 1го января 1970го года. Instant start = Instant.now(); // произведем вычисления Instant end = Instant.now(); assert end.isAfter(start); //машина времени не сработала     Обычно класс Instant используется для хранения и сравнения моментов времени, когда вам нужно сохранить когда случилось какое-то событие но вас не волнует часовой пояс в котором это случилось.     В большинстве случаев, интереснее то чего мы не можем сделать с классом Instant чем то что мы сможем с ним сделать. К примеру, следующие строки кода вызовут исключения: instant.get(ChronoField.MONTH_OF_YEAR); instant.plus(6, ChronoUnit.YEARS);     Исключения происходят потому, что объект instant хранит только количество наносекунд и не предоставляет возможность работать с единицами времени более полезными человеку. Для того чтобы воспользоваться другими единицами измерения, вам как минимум нужно указать часовой пояс.
Часовые пояса
    Принцип часовых поясов был разработан в Англии, когда изобретение железных дорог и улучшение других способов связи позволило людям перемещаться на расстояния, достаточные для того чтобы разница в солнечном времени была заметной. До этого времени, каждая деревня и город жили по своему времени, которое чаще всего измеряли по солнечным часам.     На этой картинке виден пример того, к каким сложностям это приводило - красные стрелки на часах показывают время по Гринвичу, а черная - местное время, отличающееся на 10 минут: Интуитивно понятная, надежная библиотека для работы с временем и датами, наконец-то появилась в Java (Часть 2). - 2     Система часовых поясов развивалась, заменяя собой локальное солнечное время. Но ключевой факт - часовые пояса созданы политиками, и часто используются чтобы демонстрировать политический контроль над территорией. Как и любая политика, правила связанные с часовыми поясами часто противоречат логике. А также, эти правила могут меняться, и часто меняются, без каких-то предупреждений.     Правила часовых поясов собраны международной группой, опубликовавшей базу часовых поясов IANA. Там содержится идентификатор каждого региона Земли, и история изменений часовых поясов для него. Идентификаторы выглядят как “Europe/London” или “America/New_York”.     До выхода java.time API, для представления часового пояса использовался класс TimeZone. Теперь вместо него используется ZoneId. Между ними есть два ключевых различия. Первое - ZoneId является неизменяемым, что дает возможность хранить объекты этого класса в статических переменных среди всего прочего. Второе - сами правила хранятся в классе ZoneRules, а не в самом ZoneId, и чтобы получить их нужно вызвать метод getRules() у объекта класса ZoneId.     Общей чертой всех часовых поясов является фиксированное смещение от UTC/Greenwich. Чаще всего вы используете это, когда говорите о разнице во времени между разными городами, такими как «Нью Йорк на 5 часов отстает от Лондона». Класс ZoneOffset, являющийся наследником ZoneId, представляет разницу во времени с нулевым мередианом, проходящим через Гринвич в Лондоне.     С точки зрения разработчика, было бы отлично не возиться с часовыми поясами и их сложностями. java.time API позволяет вам делать это до тех пор пока это в принципе возможно. Везде где есть возможность, используйте классы LocalDate, LocalTime, LocalDateTime и Instant. Там же где без часовых поясов нельзя, используйте класс ZonedDateTime.     Класс ZonedDateTime позволяет преобразовывать даты и время с человеческих единиц измерения, которые мы видим на календарях и часах, в машинные единицы. Как следствие, создать ZonedTimeDate вы можете как из Local класса, так и из Instant: ZoneId zone = ZoneId.of("Europe/Paris"); LocalDate date = LocalDate.of(2014, Month.JUNE, 10); ZonedDateTime zdt1 = date.atStartOfDay(zone); Instant instant = Instant.now(); ZonedDateTime zdt2 = instant.atZone(zone);     Одной из самых неприятных особенностей часовых поясов является так называемое летнее время. С переходом на летнее время и обратно, разница вашего часового пояса с Гринвичем меняется дважды (или больше) в год, обычно увеличиваясь весной и уменьшаясь осенью. Когда это происходит, мы должны перевести все часы у нас дома. В классах java.time данные смещения представлены как «преобразования смещения». Весной это вызывает «разрыв» во времени, когда некоторые значения времени невозможны, а осенью наоборот - некоторые значения времени возникают дважды.     Все это поддерживается классом ZonedDateTime с помощью его методов-фабрик и методов преобразователей. К примеру, прибавление одного дня добавляет логический день, который может быть представлен больше или меньше чем 24 часами, если мы переходим на летнее время или обратно. Аналогично, метод atStartOfDay() назван так потому, что мы не можем гарантировать что день начнется ровно в полночь - надо учитывать разрыв времени при переходе на летнее.     И последняя подсказка касающаяся летнего времени. Если вы хотите продемонстрировать что вы учли нахлест времени при переходе с летнего на зимнее (когда одно и то же значение времени возникает дважды), вы можете использовать один из двух специальных методов, предназначенных для таких ситуаций: zdt = zdt.withEarlierOffsetAtOverlap(); zdt = zdt.withLaterOffsetAtOverlap();     Данные методы вернут более раннее или более позднее значение, если объект угодил в нахлест при переходе с летнего времени на зимнее. Во всех остальных ситуациях, возвращаемые значения будут одинаковыми.
Промежутки времени
    Все классы которые мы обсуждали раньше, работают точками на шкале времени. Два дополнительных класса-значения нужны для представления промежутков времени.     Класс Duration представляет отрезок времени, измеряемый в секундах и наносекундах. К примеру, «23.6 секунд».     Класс Period представляет промежуток времени, измеряемый в годах, месяцах и днях. К примеру - «3 года, 2 месяца и 6 дней».     Эти промежутки могут быть добавлены или вычтены из даты или времени: Period sixMonths = Period.ofMonths(6); LocalDate date = LocalDate.now(); LocalDate future = date.plus(sixMonths);
Форматирование и разбор
    Целый пакет предназначен для форматирования и вывода дат и времени - java.time.format. Пакет вращается вокруг класса DateTimeFormatter и его фабрики DateTimeFormatterBuilder.     Самыми распространенными способами создания форматировщика являются статические методы и константы в DateTimeFormatter, включая:
  • Константы для распространенных форматов описанных в ISO, таких как ISO_LOCAL_DATE.
  • Шаблоны обозначаемые буквами, такие как ofPattern("dd/MM/uuuu").
  • Локализованые стили, такие как ofLocalizedDate(FormatStyle.MEDIUM).
    После того как вы создали форматировщик, обычно вы используете его передав в соответствующий метод класса даты: DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/uuuu"); LocalDate date = LocalDate.parse("24/06/2014", f); String str = date.format(f);     Таким образом, код отвечающий за форматированый вывод даты изолирован в отдельный класс.     Если вам нужно отдельно указать локаль для форматирования даты, используйте метод форматировщика withLocale(Locale). Аналогичные методы есть у классов отвечающих за календарь, часовой пояс, ввод/вывод дробных чисел.     Если вам нужна более тонкая настройка параметров, смотрите документацию на класс DateTimeFormatterBuilder, позволяющий создавать сложные форматировщики шаг за шагом. Также он позволяет задавать регистронезависимый разбор текста, игнорировать часть ошибок разбора, задавать смещения и необязательные элементы.
Итог
    java.time API - новая всеобъемлющая модель для работы с датой и временем в Java SE 8. Она берет идеи и реализации из Joda-Time на следующий уровень и наконец-то позволяет разработчикам не использовать java.util.Date и Calendar. Теперь работа с датами и временем может доставлять удовольствие!     Оригинал статьи
Комментарии (2)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
merom3ro
Уровень 28
17 июня 2014, 22:33
Не очень, меж тем, надежной оказалась. Недавно встретил баг: при передаче в конструктор даты (Util.Date) со временем 0:00:00, до 31 марта даты преобразовывались в даты не день раньше (31 марта становилось 30 и т.п.), после 31 марта все ок. Не разобрался, почему, к сожалению.
provisota
Уровень 33
4 июня 2014, 18:07
Спасибо за перевод!
На этот раз разрабы действительно потрудились на славу над этим пакетом.
Наконец — то теперь можно будет в java работать с датой/временем без деприкейтед методов, подключения сторонних либ и прочих танцев с бубном :)