JavaRush/Java блог/Random UA/Інтуїтивно зрозуміла, надійна бібліотека для роботи з час...
theGrass
24 рівень

Інтуїтивно зрозуміла, надійна бібліотека для роботи з часом і датами, з'явилася в Java (Частина 1).

Стаття з групи Random UA
учасників
    Нарешті Java з'явився інтуїтивний, надійний метод роботи з датами і часом. Принципи дати та часу є фундаментальними у багатьох додатках. Такі різні речі як дати народження, терміни оренди, час подій та години відкриття магазину, всі засновані на датах та часі, але Java SE не надавала зручного способу роботи з ними. Починаючи з Java SE 8 з'явився набір пакетів java.time - який надає добре структурований API для роботи з датами та часом.
Передісторія
    Коли Java вперше з'явилася, у версії 1.0 єдиним класом для роботи з датами і часом був java.util.Date . Першим на що звернули увагу розробники було те, що він не є «датою». Насправді він є моментом часу з точністю до мілісекунд, відміряний з дати 1-го січня 1970-го року. Однак, на підставі того, що метод toString() у Date виводить дату і час у тому часовому поясі, який зазначений в налаштуваннях java машини, деякі розробники помилково зробабо висновок про те, що Date вміє працювати з часовими поясами. Виправити цей клас виявилося настільки складно (або настільки ліниво) що у версії 1.1 довелося додати новий клас - java.util.Calendar . На жаль, клас Calendar виявився не сильно кращим ніж Date . Ось невеликий список наявних проблем у його реалізації:
  • Змінюваний. Такі класи як дата і час мають бути незмінними.
  • Зміщення. Роки в Date починаються з 1900 року, місяці в обох класах починаються з нуля.
  • Найменування. Date це насправді не дата, і Calendar не є календарем.
  • Форматування. Форматування працює тільки з Date, а не з Calendar і не є потокобезпечним.
    У 2001 році був створений проект Joda-Time . Мета його була проста - створити якісну бібліотеку для роботи з датами і часом у Java . Це зайняло певний час, але зрештою була випущена версія 1.0 і вона швидко стала дуже популярною і широко використовуваною. Згодом розробники все більше вимагали надати аналогічну за зручністю бібліотеку у складі JDK . За участю Michael Nascimento Santos з Бразилії, був запущений проект JSR-310 , який є офіційним процесом створення та інтеграції нового API для роботи з датами та часом у JDK .
Огляд
Новий API java.time містить 5 пакетів:
  • java.time - базовий пакет, що містить об'єкти для зберігання значень
  • java.time.chrono - надає доступ до різних календарів
  • java.time.format - форматування та розпізнавання дати та часу
  • java.time.temporal - низькорівневі бібліотеки та розширений функціонал
  • java.time.zone - класи для роботи з часовими поясами
    Більшість розробників переважно використовуватимуть базовий пакет і форматування, і можливо java.time.temporal . Таким чином, незважаючи на те, що було додано 68 нових типів, розробники будуть використовувати не більше третини з них.
Дати
    Клас LocalDate - один із найголовніших у новому API . Він містить незмінне значення, що є датою. Не можна встановити час або часовий пояс. Назва «local» вам може бути знайома з Joda-Time , і спочатку походить зі стандарту ISO-8601 . Воно означає саме відсутність часового поясу. По суті, LocalDate це опис дати, таке як "5 квітня 2014". Фактичний час цієї дати буде відрізнятися, залежно від вашого часового поясу. Наприклад, в Австралії ця дата буде на 10 годин раніше, ніж у Лондоні, і на 18 годин раніше, ніж у Сан Франциско. У класі LocalDate є всі потрібні методи: LocalDate date = LocalDate.of(2014, Month.JUNE, 10); int year = date.getYear(); // 2014 Month month = date.getMonth(); // Июнь int dom = date.getDayOfMonth(); // 10 DayOfWeek dow = date.getDayOfWeek(); // Вторник int len = date.lengthOfMonth(); // 30 (дней в Июне) boolean leap = date.isLeapYear(); // false (не високосный год)     У нашому прикладі, бачимо дату створену з допомогою метода-фабрики (всі конструктори приватні). Далі ми запитуємо деякі дані у об'єкта. Зверніть увагу, що перерахування Month та DayOfWeek створені для того, щоб робити код більш читаним та надійним. У наступному прикладі ми побачимо, як модифікувати дату. Так як клас незмінний, результатом будуть нові об'єкти, а вихідний залишиться як був. LocalDate date = LocalDate.of(2014, Month.JUNE, 10); date = date.withYear(2015); // 2015-06-10 date = date.plusMonths(2); // 2015-08-10 date = date.minusDays(1); // 2015-08-09     Це відносно прості зміни, але часто вам потрібно зробити складніші модифікації дати. Для цього існує спеціальний механізм в java.time API-TemporalAdjuster . Його мета - надати вбудований інструмент, що дозволяє маніпулювати датами, наприклад отримати об'єкт, що відповідає останньому дню місяця. Деякі з них входять до складу API , але ви можете додавати свої власні. Використовувати модифікатори дуже просто, але для цього потрібні статичні імпорти: import static java.time.DayOfWeek.* import static java.time.temporal.TemporalAdjusters.* LocalDate date = LocalDate.of(2014, Month.JUNE, 10); date = date.with(lastDayOfMonth()); date = date.with(nextOrSame(WEDNESDAY));     Використання модифікаторів дуже спрощує ваш код. Нікому не хочеться бачити велику кількість маніпуляцій із датою вручну. Якщо якась маніпуляція з датою зустрічається у вашому проекті кілька разів, напишіть власний модифікатор, і ваша команда зможе скористатися ним як уже написаним і протестованим компонентом.
Час та дата як значення
    Має сенс витратити трохи часу на те, щоб розібратися, що перетворює LocalDate на значення. Значення - прості типи даних, які повністю взаємозамінні коли вони рівні, ідентичність об'єктів втрачає всякий сенс. Класичний приклад класу-значення - String . Ми порівнюємо рядки через equals() , і нас не хвилює чи об'єкти ідентичні при порівнянні оператором == . Більшість класів для роботи з датами і часом теж є значеннями. Так що порівнювати їх за допомогою оператора == є поганою ідеєю, про що йдеться і в документації. Тим хто хоче знати більше, раджу почитати моє недавнє визначення VALJOs , в якому вказано суворий набір правил, яким повинні дотримуватися об'єкти-значення в Java , включаючи незмінність, наявність методів-фабрик і правильне визначення методів equals() , hashCode , toString() і compareTo () .
Альтернативні календарі
    Клас LocalDate , як і всі головні класи в java.time , прив'язаний до єдиного календаря - описаного в стандарті ISO-8601 . У стандарті 8601 описаний загальносвітовий стандартний календар, також відомий як грегоріанський. Стандартний рік включає 365 днів, високосний - 366. Високосним є кожен четвертий рік, якщо він не ділиться на 100 або ділиться на 400. Рік перед першим роком нової ери вважається нульовим для зручності обчислень. Першим наслідком від того, що ця система прийнята за умовчанням, є те, що результати не завжди збігаються з розрахованими за допомогою GregorianCalendar . У класі GregorianCalendar вбудовано перемикання на юліанську систему всім дат раніше 15го жовтня 1582 року. У юліанській системі високосним роком був щочетвертий рік без винятків. Постає питання, якщо перехід від однієї системи до іншої є історичним фактом, чому java.time його не моделює? Тому що різні країни переходабо на грегоріанську систему в різний час, і з огляду на дату переходу Ватикану ми отримаємо невірні дані для більшості інших країн. Наприклад, Британська Імперія, включаючи колонії в Північній Америці, перейшла на грегоріанський календар 14 вересня 1752 року, майже 200 років потому. Росія не змінювала свій календар до 14 лютого 1918 року, а перехід Швеції взагалі справа темна. В результаті, реальне значення дат до 1918 року сильно залежить від обставин. Авторами коду LocalDate було прийнято цілком раціональне рішення не моделювати перехід від юліанського календаря до грегоріанського взагалі, щоб уникнути різночитань. Другим наслідком використання ISO-8601 як календар за промовчанням у всіх основних класах є необхідність додаткового набору класів для роботи з рештою календарів. Інтерфейс Chronology є основою для роботи з альтернативними календарями, що дозволяє знайти потрібний календар на ім'я локалі. З Java 8 поставляється 4 додаткові календарі - Тайський буддистський, Мінгуо (тайванський), Японський та Ісламський. Інші календарі можуть постачатися з програмами. Для кожного календаря є спеціальний клас дати, такий як ThaiBuddhistDate , MinguoDate , JapaneseDate та HijrahDate . Використовувати їх має сенс у дуже локалізованих додатках, таких як додатки для японського уряду. Додатковий інтерфейс, ChronoLocalDate , застосовується як основна абстракція чотирьох вищезгаданих класів разом з LocalDateщо дозволяє писати код незалежний від використовуваного типу календаря. Незважаючи на існування цієї абстракції, її використання не рекомендується. Розуміння чому використання цієї абстракції не рекомендується, є важливим для розуміння роботи всього java.time API . Суть у тому, що більшість коду який пишуть без прив'язки до конкретного календаря, виявляється неробочою. Наприклад, ви не можете бути впевненими, що в році 12 місяців, але деякі розробники додають 12 місяців і вважають, що додали цілий рік. Ви не можете бути впевнені, що всі місяці містять приблизно однакову кількість днів - у Коптському календарі 12 місяців по 30 днів, і 1 місяць із п'яти або шести днів. Також ви не можете бути впевненими, що номер наступного року буде на 1 більший за поточний, тому що в Японському календарі роки вважаються від проголошення чергового імператора (у даному випадку, навіть 2 дні одного і того ж місяця можуть належати різним рокам). Єдиний спосіб написати якісний і робочий код, що працює з кількома календарями відразу - ядро ​​вашого коду, що робить всі операції над датами і часом, має бути написано з використанням стандартного календаря, і тільки при введенні/виводі дат робити перетворення в інші календарні системи. Тобто, рекомендується використовувати LocalDate для зберігання та всіх маніпуляцій з датами у вашому додатку. І тільки при локалізації дат, що вводяться і виводяться, використовувати ChronoLocalDate , яку зазвичай отримують з класу календаря, що зберігається в профілі користувача. Щоправда, більшість додатків не потребують такої серйозної локалізації. Якщо вам потрібно докладніше обґрунтування всього описаного в цьому розділі - ласкаво просимо до документації класу ChronoLocalDate .      Продовження статті      Оригінал статті
Коментарі
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Для цієї сторінки немає коментарів.