public class Main {
public static void main(String[] args) {
String date = "11 червня 2018 року";
System.out.println(date);
}
}
Але такий підхід має багато недоліків. Клас String
створено до роботи з текстом, і методи в нього відповідні. Якщо нам потрібно буде якось керувати датою (додати до неї 2 години, наприклад), String
тут не впорається. Або, наприклад, вивести в консоль поточну дату та час на момент компіляції програми. Тут String
теж не допоможе: поки ти напишеш код і запустиш його - час зміниться і консоль буде виведено неактуальне. Тому Java його творцями були передбачені кілька класів до роботи з датами і часом. Перший з них – це класjava.util.Date
Клас Date Java
Ми вказали для нього повну назву, оскільки в іншому пакеті Java є ще класjava.sql.Date
. Не переплутай! Перше, що потрібно про нього знати, він зберігає дату в мілісекундах , які пройшли з 1 січня 1970 року. Для цієї дати є навіть окрема назва - "Unix-час" Досить цікавий спосіб, згоден? :) Друге, що варто запам'ятати: якщо створити об'єкт Date
з порожнім конструктором - результатом буде поточна дата і час на момент створення об'єкта . Пам'ятаєш, ми писали, що для дати у форматі String
таке завдання буде проблематичним? Клас Date
її легко вирішує.
public class Main {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
}
}
Запусти цей код кілька разів, і побачиш, як час щоразу змінюватиметься:) Це можливо саме завдяки зберіганню в мілісекундах: вони є найменшою одиницею часу, тому результати настільки точні. Існує й інший конструктор для Date
: можна вказати точну кількість мілісекунд, яка пройшла з 00:00 1 січня 1970 до необхідної дати, і вона буде створена:
public class Main {
public static void main(String[] args) {
Date date = new Date(1212121212121L);
System.out.println(date);
}
}
Виведення в консоль:
Fri May 30 08:20:12 MSD 2008
В нас вийшло 30 травня 2008 року. "Fri" означає день тижня - "Friday" (п'ятниця), а MSD - "Moscow Daylight Saving" (московський літній час). Мілісекунди передаються у форматі long
, оскільки їх кількість найчастіше не влазить у int
. Отже, які операції з датами нам можуть знадобитися у роботі? Ну, найочевидніше, звісно — порівняння . Визначити чи була одна дата пізніше чи раніше за іншу. Це можна зробити по-різному. Наприклад, можна викликати метод Date.getTime()
. Він поверне кількість мілісекунд, що пройшли з півночі 1 січня 1970 року. Просто викличемо його у двох об'єктів Date і порівняємо між собою:
public class Main {
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
System.out.println((date1.getTime() > date2.getTime())?
"date1 пізніше date2" : "date1 раніше date2");
}
}
Висновок:
date1 раньше date2
Але є й зручніший спосіб, саме — використовувати спеціальні методи класу Date
: before()
, after()
і equals()
. Усі вони повертають результат у форматі boolean
. Метод before()
перевіряє, чи була наша дата раніше тієї, яку ми передаємо як аргумент:
public class Main {
public static void main(String[] args) throws InterruptedException {
Date date1 = new Date();
Thread.sleep(2000);//припинимо роботу програми на 2 секунди
Date date2 = new Date();
System.out.println(date1.before(date2));
}
}
Виведення в консоль:
true
Схожим чином працює і метод after()
, він перевіряє, чи була наша дата пізніше тієї, яку ми передаємо як аргумент:
public class Main {
public static void main(String[] args) throws InterruptedException {
Date date1 = new Date();
Thread.sleep(2000);//припинимо роботу програми на 2 секунди
Date date2 = new Date();
System.out.println(date1.after(date2));
}
}
Виведення в консоль:
false
У прикладах ми “присипляємо” програму на 2 секунди, щоб дві дати гарантовано відрізнялися. На швидких комп'ютерах час між створенням date1
і date2
може бути меншим за одну мілісекунду, і в такому випадку обидва методи — і before()
, і after()
— повертатимуть false
. А ось метод equals()
у такій ситуації поверне true
! Адже він порівнює саме кількість мілісекунд, що пройшли з 00:00 1 січня 1970 року для кожної дати. Об'єкти вважатимуться рівними тільки в тому випадку, якщо збігаються аж до мілісекунди:
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
System.out.println(date1.getTime());
System.out.println(date2.getTime());
System.out.println(date1.equals(date2));
}
Ось ще на що треба звернути увагу. Якщо ти відкриєш документацію класу Date
на сайті Oracle, то побачиш, що багато його методів та конструкторів були позначені словом Deprecated
(“нерекомендований”). Ось, подивися: Class Date Ось що самі творці Java говорять про ті частини класів, які стали deprecated: “Програмний елемент, анотований @Deprecated, є тим, що програмістам не рекомендується використовувати, як правило, тому, що це небезпечно, або тому, що є найкраща альтернатива.” Не означає, що цими методами взагалі не можна користуватися. Більше того, якщо ти сам спробуєш запустити код з їх використанням в IDEA - він, швидше за все, буде працювати Візьмемо для прикладу deprecated методDate.getHours()
, який повертає кількість годинників з об'єкта Date
.
public static void main(String[] args) {
Date date1 = new Date();
System.out.println(date1.getHours());
}
Якщо на момент запуску коду у вас, наприклад, час 14:21 він виведе число 14. Як бачите, deprecated-метод закреслено, але він цілком собі працює. Це методи не стали прибирати зовсім, щоб не зламати купу вже написаного з використанням коду. Тобто ці методи не "зламані" і не "віддалені", просто їх не рекомендують використовувати через наявність більш зручної альтернативи. Про неї, до речі, написано прямо в документації: Більшість методів класу Date було перенесено до його покращеної, розширеної версії — класу Calendar
. З ним ми й познайомимося далі:)
Java Calendar
У версії Java 1.1 з'явився новий класCalendar
. Він зробив роботу з дат в Java дещо простіше, ніж вона виглядала раніше. Єдиною реалізацією класу Calendar
, з якою ми і працюватимемо, є клас GregorianCalendar
(він реалізує Григоріанський календар, за яким живе більшість країн світу). Його основна зручність полягає в тому, що він вміє працювати з датами у зручнішому форматі. Наприклад, він може:
- Додати до поточної дати місяць або день
- Перевірити, чи рік високосним;
- Отримати окремі компоненти дати (наприклад, одержати з цілої дати номер місяця)
- А також усередині нього розроблена дуже зручна система констант (багато з них ми побачимо нижче).
Calendar
є те, що в ньому реалізована константа Calendar.Era
: ти можеш встановити для дати еру BC ("Before Christ" - до Різдва Христового, тобто "до нашої ери") або AC ("After Christ" - " Наша ера"). Давайте розглянемо все це на прикладах. Створимо календар із датою 25 січня 2017 року:
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
Місяці в класі Calendar
(як і в Date
, до речі) починаються з нуля, тому ми передали число 0 як другий аргумент. Головне при роботі з класом Calendar
— розуміти, що це календар , а не окрема дата. Дата — це кілька чисел, що позначають конкретний проміжок часу. А календар - це цілий пристрій, за допомогою якого можна багато чого робити з датами:) Це досить добре видно, якщо спробувати вивести об'єкт Calendar в консоль: Висновок:
java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?]
Бачиш, скільки інформації! У календаря є купа властивостей, які не має звичайна дата, і всі вони виводяться в консоль (так працює метод toString()
у класі Calendar
). Якщо під час роботи тобі потрібно отримати з календаря просту дату, тобто. об'єкт Date
— це робиться за допомогою методу Calendar.getTime()
(назва не логічна, але тут вже нічого не поробиш):
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2017, 0 , 25);
Date date = calendar.getTime();
System.out.println(date);
}
Висновок:
Wed Jan 25 00:00:00 MSK 2017
Ось тепер ми спростабо календар до звичайної дати. Поїхали далі. Крім цифрових позначень місяців у класі Calendar
можна використовувати константи. Константи - це статичні поля класу Calendar
з встановленим значенням, яке не можна змінити. Цей варіант насправді навіть кращий, оскільки таке написання покращує читання коду.
public static void main(String[] args) {
GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY
- Одна з констант для позначення місяця. При такому варіанті іменування ніхто не забуде, наприклад, що цифра "3" означає квітень, а не звичний нам третій місяць - березень. Просто пишеш Calendar.APRIL
- і все:) Усі поля календаря (число, місяць, хвабони, секунди тощо) можна встановлювати окремо за допомогою методу set()
. Він дуже зручний, оскільки в класі Calendar
для кожного поля виділено свою константу, і підсумковий код буде виглядати максимально просто. Наприклад, у минулому прикладі ми створабо дату, але не встановабо для неї поточний час. Давай встановимо час 19:42:12
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.YEAR, 2017);
calendar.set(Calendar.MONTH, 0);
calendar.set(Calendar.DAY_OF_MONTH, 25);
calendar.set(Calendar.HOUR_OF_DAY, 19);
calendar.set(Calendar.MINUTE, 42);
calendar.set(Calendar.SECOND, 12);
System.out.println(calendar.getTime());
}
Висновок:
Wed Jan 25 19:42:12 MSK 2017
Ми викликаємо метод set()
, передаємо до нього константу (залежно від того поля, яке хочемо змінити) та нове значення для цього поля. Виходить, що метод set()
- такий собі "супер-сеттер", який вміє встановлювати значення не для одного поля, а для безлічі полів:) Додавання та віднімання значень у класі Calendar
здійснюється за допомогою методу add()
. У нього необхідно передати те поле, яке ти хочеш змінити, і число - скільки саме ти хочеш додати/зменшити від поточного значення. Наприклад, повернемо дату, яку ми створабо, на 2 місяці тому:
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
calendar.set(Calendar.HOUR, 19);
calendar.set(Calendar.MINUTE, 42);
calendar.set(Calendar.SECOND, 12);
calendar.add(Calendar.MONTH, -2);//щоб відібрати значення - метод потрібно передати негативне число
System.out.println(calendar.getTime());
}
Висновок:
Fri Nov 25 19:42:12 MSK 2016
Чудово! Ми повернули дату на 2 місяці тому. В результаті змінився не лише місяць, а й рік, з 2017 на 2016 рік. Підрахунок поточного року при перенесенні дат, звичайно, виконується автоматично і його не треба контролювати вручну. Але якщо для якихось цілей тобі потрібно відключити цю поведінку, то можна й так. Спеціальний метод roll()
може додавати й зменшувати значення, не торкаючись у своїй інші значення. Наприклад, ось так:
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
calendar.set(Calendar.HOUR, 10);
calendar.set(Calendar.MINUTE, 42);
calendar.set(Calendar.SECOND, 12);
calendar.roll(Calendar.MONTH, -2);
System.out.println(calendar.getTime());
}
Ми зробабо те саме, що й у попередньому прикладі — відібрали 2 місяці від поточної дати. Але тепер код спрацював по-іншому: місяць змінився з січня на листопад, але рік як був 2017-м, так і залишився! Висновок:
Sat Nov 25 10:42:12 MSK 2017
Далі. Як ми й казали вище, всі поля об'єкта Calendar
можна отримати окремо. За це відповідає метод get()
:
public static void main(String[] args) {
GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
calendar.set(Calendar.HOUR, 10);
calendar.set(Calendar.MINUTE, 42);
calendar.set(Calendar.SECOND, 12);
System.out.println("Рік:" + calendar.get(Calendar.YEAR));
System.out.println("Місяць:" + calendar.get(Calendar.MONTH));
System.out.println("Порядковий номер тижня на місяці:" + calendar.get(Calendar.WEEK_OF_MONTH));//порядковий номер тижня на місяці
System.out.println("Кількість: " + calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("Годинник:" + calendar.get(Calendar.HOUR));
System.out.println("Хвабони:" + calendar.get(Calendar.MINUTE));
System.out.println("Секунди:" + calendar.get(Calendar.SECOND));
System.out.println(Мілісекунди: + calendar.get(Calendar.MILLISECOND));
}
Висновок:
Год: 2017
Месяц: 0
Порядковый номер недели в месяце: 4
Число: 25
Часы: 10
Минуты: 42
Секунды: 12
Миллисекунды: 0
Тобто крім "супер-сеттера" в класі Calendar
є ще й "супер-геттер" Ще один цікавий момент - це, звичайно, робота з ерами. Для створення дати "до нашої ери" потрібно використовувати поле Calendar.Era
Наприклад, створимо дату, що позначає битву при Каннах, в якій Ганнібал переміг військо Риму. Це сталося 2 серпня 216 р. до зв. е.:
public static void main(String[] args) {
GregorianCalendar cannes = new GregorianCalendar(216, Calendar.AUGUST, 2);
cannes.set(Calendar.ERA, GregorianCalendar.BC);
DateFormat df = new SimpleDateFormat("dd MMM yyy GG");
System.out.println(df.format(cannes.getTime()));
}
Тут ми використовували клас SimpleDateFormat
, щоб вивести дату у більш зрозумілому нам форматі (літери “GG” відповідають саме за виведення ери). Висновок:
02 авг 216 до н.э.
У класі Calendar
є ще багато методів та констант, почитай про них у документації:
Переклад рядка у Date
Для перекладу String в Date можна скористатися допоміжним класом Java - SimpleDateFormat . Це клас, який потрібен для приведення дати в визначальний формат. У свою чергу, він дуже схожий на DateFormat . Єдина помітна різниця між ними полягає в тому, що SimpleDateFormat можна використовувати для форматування (перетворення дати в рядок) та для парсингу рядка в дату з підтримкою мовного стандарту, тоді як DateFormat не підтримує мовний стандарт. Крім того, DateFormat – це абстрактний клас, який забезпечує базову підтримку для форматування та аналізу дат, а SimpleDateFormat – це конкретний клас, який розширює клас DateFormat. Ось так виглядає приклад створення об'єкта SimpleDateFormat та форматування Date:SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(1212121212121L);
System.out.println(formatter.format(date));
У наведеному вище прикладі ми використовували шаблон "yyyy-MM-dd HH:mm:ss", який означає:
- 4 цифри на рік (yyyy);
- 2 цифри на місяць (ММ);
- 2 цифри на день (dd);
- 2 цифри для годинника у 24-годинному форматі (HH);
- 2 цифри на хвабони (mm);
- 2 цифри за секунди (ss).
2008-05-30 08:20:12
Шаблонних букв для класу SimpleDateFormat досить багато. Щоб ти не заплутався, ми зібрали їх у таблицю:
Символ | Опис | приклад |
---|---|---|
G | ера (в англійській локалізації - AD і BC) | н.е. |
y | рік (4-х значне число) | 2020 |
yy | рік (останні 2 цифри) | 20 |
yyyy | рік (4-х значне число) | 2020 |
M | номер місяця (без лідируючих нулів) | 8 |
MM | номер місяця (з лідируючими нулями, якщо порядковий номер місяця < 10) | 04 |
MMM | трилітерне скорочення місяця (відповідно до локалізації) | січ |
MMMM | повна назва місяця | Червень |
w | тиждень у році (без лідируючих нулів) | 4 |
ww | тиждень у році (з лідируючими нулями) | 04 |
W | тиждень у місяці (без лідируючих нулів) | 3 |
WW | тиждень у місяці (з лідируючим нулем) | 03 |
D | день у році | 67 |
d | день місяця (без лідируючих нулів) | 9 |
dd | день місяця (з лідируючими нулями) | 09 |
F | день тижня на місяці (без лідируючих нулів) | 9 |
FF | день тижня на місяці (з лідируючими нулями) | 09 |
E | день тижня (скорочення) | Вт |
EEEE | день тижня (повністю) | п'ятниця |
u | номер дня тижня (без лідируючих нулів) | 5 |
uu | номер дня тижня (з лідируючими нулями) | 05 |
a | маркер AM/PM | AM |
H | годинник у 24-годинному форматі без лідируючих нулів | 6 |
HH | годинник у 24-годинному форматі з лідируючим нулем | 06 |
k | кількість годин у 24-годинному форматі | 18 |
K | кількість годин у 12-годинному форматі | 6 |
h | час у 12-годинному форматі без лідируючих нулів | 6 |
hh | час у 12-годинному форматі з лідируючим нулем | 06 |
m | хвабони без лідируючих нулів | 32 |
mm | хвабони з лідируючим нулем | 32 |
s | секунди без лідируючих нулів | 11 |
ss | секунди з лідируючим нулем | 11 |
S | мілісекунди | 297 |
z | часовий пояс | EET |
Z | часовий пояс у форматі RFC 822 | 300 |
Шаблон | приклад |
---|---|
dd-MM-yyyy | 01-11-2020 |
yyyy-MM-dd | 2019-10-01 |
HH:mm:ss.SSS | 23:59.59.999 |
yyyy-MM-dd HH:mm:ss | 2018-11-30 03:09:02 |
yyyy-MM-dd HH:mm:ss.SSS | 2016-03-01 01:20:47.999 |
yyyy-MM-dd HH:mm:ss.SSS Z | 2013-13-13 23:59:59.999 +0100 |
-
Створюємо рядок, з якого потрібно задати дату:
String strDate = "Sat, April 4, 2020";
-
Створюємо новий об'єкт SimpleDateFormat із шаблоном, який збігається з тим, що у нас у рядку (інакше розпарсувати не вийде):
SimpleDateFormat formatter = new SimpleDateFormat("EEE, MMMM d, yyyy", Locale.ENGLISH);
Як ви бачите, у нас тут виник аргумент Locale. Якщо ми його опустимо, він буде використовувати значення Locale за умовчанням, яке завжди є англійською.
Якщо мовний стандарт не збігається з вхідним рядком, то рядкові дані, прив'язані до мови, як у нас Mon або April , не будуть розпізнані та викликатимуть падіння - java.text.ParseException, навіть якщо шаблон підходить.
Проте, можна не вказувати формат, якщо ми використовуємо шаблон, який не прив'язаний до мови. Як приклад - yyyy-MM-dd HH:mm:ss
-
Створюємо дату за допомогою формату, який у свою чергу парсить її з вхідного рядка:
try { Date date = formatter.parse(strDate); System.out.println(date); } catch (ParseException e) { e.printStackTrace(); }
Виведення в консоль:
Sat Apr 04 00:00:00 EEST 2020
Хммм….Але формат уже не той!
Щоб зробити той самий формат, знову використовуємо форматтер:
System.out.println(formatter.format(date));
Виведення в консоль:
Sat, April 4, 2020
SimpleDateFormat та Calendar
SimpleDateFormat дозволить тобі форматувати всі створювані об'єкти Date та Calendar для подальшого використання. Розглянемо такий цікавий момент, як робота з ерами. Для створення дати "до нашої ери" потрібно використовувати поле Calendar.Era Наприклад, створимо дату, що означає битву при Каннах, в якій Ганнібал переміг військо Риму. Це сталося 2 серпня 216 р. до зв. е.:public static void main(String[] args) {
GregorianCalendar cannes = new GregorianCalendar(216, Calendar.AUGUST, 2);
cannes.set(Calendar.ERA, GregorianCalendar.BC);
DateFormat df = new SimpleDateFormat("dd MMM yyy GG");
System.out.println(df.format(cannes.getTime()));
}
Тут ми використовували клас SimpleDateFormat, щоб вивести дату у більш зрозумілому нам форматі (як зазначено вище, літери “GG” відповідають саме за виведення ери). Висновок:
02 авг 216 до н.э.
Java Date Format
А ось ще один випадок. Припустимо, що цей формат дати нас не влаштовує:
Sat Nov 25 10:42:12 MSK 2017
Так ось. За допомогою наших можливостей у java date format його можна поміняти його на свій власний, без особливих складнощів:
public static void main(String[] args) {
SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, d MMMM yyyy");
Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
calendar.set(Calendar.HOUR, 10);
calendar.set(Calendar.MINUTE, 42);
calendar.set(Calendar.SECOND, 12);
calendar.roll(Calendar.MONTH, -2);
System.out.println(dateFormat.format(calendar.getTime()));
}
Висновок:
суббота, 25 Ноябрь 2017
Набагато краще, так? :)