Обработка дат и времени довольно сложная задача. В мире существует большое количество часовых поясов, которые периодически меняются. Нужно учитывать переход на зимнее и летнее время, секунды координации, високосные года и многое другое.
Java уже довольно много лет, и попыток создать классы для обработки дат было несколько. Здесь я лишь в общих чертах опишу три способа:
-
- java.util.Date — самый древний способ. Большая часть методов отмечена устаревшими, но сам класс до сих пор используется довольно часто.
- java.util.Calendar — обычно используется совместно с java.util.Date.
- Пакет java.time — новый способ, сильно превосходит предыдущие по удобству и функционалу, но используется пока не так часто (по крайней мере мне редко приходилось сталкиваться.
java.util.Date
Самый древний способ. Если вы посмотрите на описание методов java.util.Date, то вы заметите, что большая часть методов указана устаревшими. Реально полезного и работающего всего чуть-чуть.
Создание экземпляров:
1
2
3
4
5
6
7
8
|
// Создаём экземпляр с текущей датой и временем.
java.util.Date currentDate = new java.util.Date();
// Создаём экземпляр, содержащий время, получившееся по
// прошествии указанного количества миллисекунд от
// 1 января 1970 года 00:00 GMT. Обычно используется
// для конвертации из других форматов даты и времени.
java.util.Date dateByMillis = new java.util.Date(29368498236L);
|
Полезные методы класса java.util.Date:
1
|
public boolean after(Date when)
|
Возвращает true, если дата объекта находится позже указанной.
1
|
public boolean before(Date when)
|
Возвращает true, если дата объекта находится перед указанной.
1
|
public int compareTo(Date anotherDate)
|
Сравнение дат. Возвращает 0, если даты равны. Возвращает -1, если дата объекта находится до anotherDate. Возвращает 1, если дата объекта позже anotherDate.
1
|
public static Date from(Instant instant)
|
1
|
public Instant toInstant()
|
Используются для конвертации в Instant из пакета java.time и обратно.
1
|
public long getTime()
|
1
|
public void setTime(long time)
|
Возвращает и устанавливает значение java.util.Date в количестве миллисекунд, прошедших с 1 января 1970 года 00:00 GMT.
java.util.Calendar
Класс java.util.Calendar является абстрактным классом. Единственная реализация — java.util.GregorianCalendar. Новый экземпляр создаётся фабричным методом:
1
|
Calendar rightNow = Calendar.getInstance();
|
Calendar предоставляет методы установки значений различных полей вроде YEAR, MONTH, DAY_OF_MONTH, HOUR и т. д, манипуляции этими полями, например добавление дня или месяца. Дата и время представляется количеством миллисекунд, прошедших с 1 января 1970 года 00:00:00.000 GMT. Обычно класс java.util.Calendarиспользуется для создания экземпляров java.util.Date, представляющих необходимую дату и время.
Calendar имеет два режима интерпретации полей: мягкий (lenient) и жёсткий (non-lenient). Когда Calendar в мягком (lenient) режиме, то он принимает больший диапазон значений полей, чем возвращает. Когда Calendar пересчитывает значения полей, возвращаемых get(), то все поля нормализуются. Например, мягкий GregorianCalendarинтерпретирует MONTH == JANUARY, DAY_OF_MONTH == 32 как 1 февраля.
Когда Calendar в жёстком (non-lenient) режиме, то он бросает исключение в подобных случаях. Например, GregorianCalendar всегда возвращает значение поля DAY_OF_MONTH в диапазоне от 1 до длины месяца. Жёсткий GregorianCalendar бросает исключение во время выполнения вычислений своего времени или поля, если хоть одно поле выходит за допустимые границы.
Полезные методы:
1
2
|
public void set(int field,
int value)
|
Устанавливает новое значения поля.
1
|
public int get(int field)
|
Возвращает значение поля. Для мягкого режима значения полей нормализуются, а в случае жёсткого режима и выхода значения какого-либо поля за допустимый диапазон бросается исключение
1
2
|
public void roll(int field,
int amount)
|
Добавляет amount (может быть отрицательным) к указанному полю.
1
|
public final Date getTime()
|
1
|
public final void setTime(Date date)
|
Конвертация в java.util.Date и обратно.
1
|
public void setLenient(boolean lenient)
|
Устанавливает мягкий (lenient) или жёсткий (non-lenient) режим.
1
|
public final Instant toInstant()
|
Конвертирует в Instant из пакета java.time.
1
|
public void setTimeZone(TimeZone value)
|
Позволяет установить часовой пояс. По умолчанию используется часовой пояс системы, на которой запущена программа. С помощью этого метода можно указать другой часовой пояс. Например: calender.setTimeZone(TimeZone.getTimeZone(«America/Los_Angeles»));
1
|
public TimeZone getTimeZone()
|
Возвращает часовой пояс, ассоциированный с данным экземпляром Calendar.
java.time
Наиболее удобный и современный способ работы с датой и временем. Берёт своё начало от библиотеки Joda-Time.
Есть два базовых способа представления времени. Один способ представляет время в терминах человека, таких как год, месяц, день, час, минуты и секунды. Второй способ представляет машинное время, измеряя время непрерывно с начала, называемого эпохой, в наносекундах. Пакет Date-Time содержит большое количество классов, представляющий дату и время. Некоторые классы в Date-Time API представляют машинное время, некоторые человеческое.
Сначала определите, какие аспекты даты и времени вам нужны, затем выберите класс или классы, которые подходят под ваши нужды.
Например, вы можете выбрать java.time.LocalDate для хранения даты рождения, так как многие люди празднуют день рождения в тот же день, независимо от того, находятся ли они в месте рождения или на другом конце Земли. Если вам нужно астрологическое время, то вы можете использовать java.time.LocalDateTime, чтобы показать дату и день рождения, либо java.time.ZonedDateTime, который дополнительно содержит часовой пояс. Если вы создаёте временную отметку, то вероятнее всего вы захотите использовать java.time.Instant, который позволяет сравнивать одну временную отметку с другой.
Суммарная таблица классов пакета java.time:
Класс или перечисление | Год | Месяц | День | Часы | Минуты | Секунды* | Смещ. врем. зоны | Zone ID | toString Output |
---|---|---|---|---|---|---|---|---|---|
Instant | 2013-08-20T15:16:26.355Z | ||||||||
LocalDate | 2013-08-20 | ||||||||
LocalDateTime | 2013-08-20T08:16:26.937 | ||||||||
ZonedDateTime | 2013-08-21T00:16:26.941+09:00[Asia/Tokyo] | ||||||||
LocalTime | 08:16:26.943 | ||||||||
MonthDay | --08-20 | ||||||||
Year | 2013 | ||||||||
YearMonth | 2013-08 | ||||||||
Month | AUGUST | ||||||||
OffsetDateTime | 2013-08-20T08:16:26.954-07:00 | ||||||||
OffsetTime | 08:16:26.957-07:00 | ||||||||
Duration | ** | ** | ** | PT20H (20 hours) | |||||
Period | *** | *** | P10D (10 days) |
* секунды считаются с точностью до наносекунд
** Этот класс не сохраняет эту информацию, но имеет методы для получения времени в них.
*** Когда Period добавляется к ZonedDateTime , то учитывается переход на зимнее/летнее время и отличие локального времени.
java.time.DayOfWeek
Перечисление java.time.DayOfWeek состоит из семи констант, описывающих дни недели. Целочисленные значения для констант начинаются с 1 (Понедельник) и заканчиваются 7 (Воскресенье).
Список констант:
MONDAY (понедельник, 1)
TUESDAY (вторник, 2)
WEDNESDAY (среда, 3)
THURSDAY (четверг, 4)
FRIDAY (пятница, 5)
SATURDAY (суббота, 6)
SUNDAY (воскресенье, 7)
Вы можете использовать метод public String getDisplayName(TextStyle style, Localelocale) для получения названий дней недели в соответствии с региональными настройками пользователя. Перечислениеjava.time.format.TextStyle позволяет указать тип строки: FULL, NARROW (обычно одна буква), SHORT (аббревиатура).
Пример:
1
2
3
4
5
|
DayOfWeek dow = DayOfWeek.MONDAY;
Locale locale = Locale.getDefault();
System.out.println(dow.getDisplayName(TextStyle.FULL, locale));
System.out.println(dow.getDisplayName(TextStyle.NARROW, locale));
System.out.println(dow.getDisplayName(TextStyle.SHORT, locale));
|
java.time.Month
Перечисление java.time.Month содержит константы для двенадцати месяцев, пронумерованных от 1 до 12:
JANUARY (январь, 1)
FEBRUARY (февраль, 2)
MARCH (март, 3)
APRIL (апрель, 4)
MAY (май, 5)
JUNE (июнь, 6)
JULY (июль, 7)
AUGUST (август, 8)
SEPTEMBER (сентябрь, 9)
OCTOBER (октябрь, 10)
NOVEMBER (ноябрь, 11)
DECEMBER (декабрь, 12)
Перечисление java.time.Month содержит несколько полезных методов. Например, метод maxLength() возвращает максимально возможное количество дней в месяце:
System.out.printf(«%d%n», Month.FEBRUARY.maxLength());
Также есть метод public String getDisplayName(TextStyle style, Locale locale), позволяющий получить текстовое название месяца в соответствии с указанной локалью:
1
2
3
4
5
|
Month month = Month.AUGUST;
Locale locale = Locale.getDefault();
System.out.println(month.getDisplayName(TextStyle.FULL, locale));
System.out.println(month.getDisplayName(TextStyle.NARROW, locale));
System.out.println(month.getDisplayName(TextStyle.SHORT, locale));
|
java.time.LocalDate
Класс java.time.LocalDate хранит год, месяц и день. Он используется для хранения и обработки даты без времени. Примеры создания:
1
2
|
LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20);
LocalDate nextWed = date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
|
В дополнение к обычным методам класс java.time.LocalDate содержит методы для получения информации о дате. Метод getDayOfWeek возвращает день недели. Например, следующий код вернёт MONDAY:
1
|
DayOfWeek dotw = LocalDate.of(2012, Month.JULY, 9).getDayOfWeek();
|
Следующий пример использует TemporalAdjuster, чтобы следующую среду от указанной даты:
1
2
3
4
5
|
LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20);
TemporalAdjuster adj = TemporalAdjusters.next(DayOfWeek.WEDNESDAY);
LocalDate nextWed = date.with(adj);
System.out.printf(«For the date of %s, the next Wednesday is %s.%n»,
date, nextWed);
|
java.time.YearMonth
Класс java.time.YearMonth представляет месяц с годом. Следующие примеры используют YearMonth.lengthOfMonth(), чтобы определить количество дней в конкретном годе и месяце:
1
2
3
4
5
6
7
8
|
YearMonth date = YearMonth.now();
System.out.printf(«%s: %d%n», date, date.lengthOfMonth());
YearMonth date2 = YearMonth.of(2010, Month.FEBRUARY);
System.out.printf(«%s: %d%n», date2, date2.lengthOfMonth());
YearMonth date3 = YearMonth.of(2012, Month.FEBRUARY);
System.out.printf(«%s: %d%n», date3, date3.lengthOfMonth());
|
Этот код выведет в консоль:
1
2
3
|
2013-06: 30
2010-02: 28
2012-02: 29
|
java.time.MonthDay
Класс java.time.MonthDay содержит день с месяцем. Следующий пример использует MonthDay.isValidYear, чтобы определить, является ли 29 февраля корректной датой для 2010 года. Этот вызов вернёт false, так как 2010 год не является високосным.
1
2
|
MonthDay date = MonthDay.of(Month.FEBRUARY, 29);
boolean validLeapYear = date.isValidYear(2010);
|
java.time.Year
Класс java.time.Year хранит год. Следующий пример использует метод Year.isLeap , чтобы определить, является ли год високосным. Этот вызов вернёт true, так как 2012 год високосный.
1
|
boolean validLeapYear = Year.of(2012).isLeap();
|
java.time.LocalTime
Класс java.time.LocalTime оперирует только временем. Он полезен для хранения времени открытия/закрытия магазина и т. д. Пример:
1
2
3
4
5
6
7
8
|
LocalTime thisSec;
for (;;) {
thisSec = LocalTime.now();
// Предположим, что метод display уже есть
display(thisSec.getHour(), thisSec.getMinute(), thisSec.getSecond());
}
|
Класс LocalTime не сохраняет информацию о часовой поясе и летнем/зимнем времени.
java.time.LocalDateTime
Класс java.time.LocalDateTime хранит дату и время. Он является нечтом вроде комбинации LocalDate и LocalTime. В дополнение к методу now(), который есть у каждого временного класса, классLocalDateTime содержит большое количество методов of, которые позволяют создать экземпляры LocalDateTime. Метод from конвертирует экземпляр другого класса в LocalDateTime. Также есть методы для добавления и вычитания часов, минут, дней и недель Пример:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
System.out.printf(«now: %s%n», LocalDateTime.now());
System.out.printf(«Apr 15, 1994 @ 11:30am: %s%n»,
LocalDateTime.of(1994, Month.APRIL, 15, 11, 30));
System.out.printf(«now (from Instant): %s%n»,
LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()));
System.out.printf(«6 months from now: %s%n»,
LocalDateTime.now().plusMonths(6));
System.out.printf(«6 months ago: %s%n»,
LocalDateTime.now().minusMonths(6));
|
Этот код выведет в консоль:
1
2
3
4
5
|
now: 2013-07-24T17:13:59.985
Apr 15, 1994 @ 11:30am: 1994-04-15T11:30
now (from Instant): 2013-07-24T17:14:00.479
6 months from now: 2014-01-24T17:14:00.480
6 months ago: 2013-01-24T17:14:00.481
|
java.time.ZoneId и java.time.ZoneOffset
Часовой пояс — это участок земной поверхности, на котором используется одно и то же стандартное время. Каждый часовой пояс определяется идентификатором, имеющим формат регион/город (Asia/Tokyo), и смещением от Гринвича/UTC. Например, смещение для Токио +9:00.
Date-Time API содержит два класса для указания часового пояса или смещения:
- java.time.ZoneId указывает идентификатор часового пояса и поставляет правила для конвертирования java.time.Instant вjava.time.LocalDateTime.
- java.time.ZoneOffset указывает смещение часового пояса от Гринвича/UTC.
Смещения от Гринвича/UTC обычно определяются в полных часах, но есть исключения. Следующий код выводит в консоль все часовые пояса, которые используют смещение от Гринвича/UTC, указанные не в полных часах.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
Set<String> allZones = ZoneId.getAvailableZoneIds();
LocalDateTime dt = LocalDateTime.now();
// Создаём список с зонами и сортируем его.
List<String> zoneList = new ArrayList<String>(allZones);
Collections.sort(zoneList);
...
for (String s : zoneList) {
ZoneId zone = ZoneId.of(s);
ZonedDateTime zdt = dt.atZone(zone);
ZoneOffset offset = zdt.getOffset();
int secondsOfHour = offset.getTotalSeconds() % (60 * 60);
String out = String.format(«%35s %10s%n», zone, offset);
// Пишем только часовые пояса, которые используют смещение
// в неполных часах.
if (secondsOfHour != 0) {
System.out.printf(out);
}
...
}
|
Этот пример выведет в консоль:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
America/Caracas -04:30
America/St_Johns -02:30
Asia/Calcutta +05:30
Asia/Colombo +05:30
Asia/Kabul +04:30
Asia/Kathmandu +05:45
Asia/Katmandu +05:45
Asia/Kolkata +05:30
Asia/Rangoon +06:30
Asia/Tehran +04:30
Australia/Adelaide +09:30
Australia/Broken_Hill +09:30
Australia/Darwin +09:30
Australia/Eucla +08:45
Australia/LHI +10:30
Australia/Lord_Howe +10:30
Australia/North +09:30
Australia/South +09:30
Australia/Yancowinna +09:30
Canada/Newfoundland -02:30
Indian/Cocos +06:30
Iran +04:30
NZ-CHAT +12:45
Pacific/Chatham +12:45
Pacific/Marquesas -09:30
Pacific/Norfolk +11:30
|
java.time.ZonedDateTime
Класс java.time.ZonedDateTime можно рассматривать как комбинацию java.time.LocalDateTime и java.time.ZoneId. Он представляет собой полную дату, время и часовой пояс.
Следующий код определяет время вылета из Сан-Франциско в Токио как java.time.ZonedDateTime в часовом поясе America/Los Angeles. Для получения экземпляра java.time.ZonedDateTime, содержащего время прибытия в Токио после 650 минут полёта, используются методыwithZoneSameInstant и plusMinutes. Метод ZoneRules.isDaylightSavings определяет, используется ли летнее время по прибытии в Токио.
Для форматированного вывода java.time.ZonedDateTime используется объект java.time.format.DateTimeFormatter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
DateTimeFormatter format = DateTimeFormatter.ofPattern(«MMM d yyyy hh:mm a»);
// Отправление из Сан-Франциско 20 июля 2013 в 19:30.
LocalDateTime leaving = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneId leavingZone = ZoneId.of(«America/Los_Angeles»);
ZonedDateTime departure = ZonedDateTime.of(leaving, leavingZone);
try {
String out1 = departure.format(format);
System.out.printf(«LEAVING: %s (%s)%n», out1, leavingZone);
} catch (DateTimeException exc) {
System.out.printf(«%s can’t be formatted!%n», departure);
throw exc;
}
// Полёт длится 10 часов и 50 минут (650 минут)
ZoneId arrivingZone = ZoneId.of(«Asia/Tokyo»);
ZonedDateTime arrival = departure.withZoneSameInstant(arrivingZone)
.plusMinutes(650);
try {
String out2 = arrival.format(format);
System.out.printf(«ARRIVING: %s (%s)%n», out2, arrivingZone);
} catch (DateTimeException exc) {
System.out.printf(«%s can’t be formatted!%n», arrival);
throw exc;
}
if (arrivingZone.getRules().isDaylightSavings(arrival.toInstant()))
System.out.printf(» (%s daylight saving time will be in effect.)%n»,
arrivingZone);
else
System.out.printf(» (%s standard time will be in effect.)%n»,
arrivingZone);
|
Этот код выведет в консоль:
1
2
3
|
LEAVING: Jul 20 2013 07:30 PM (America/Los_Angeles)
ARRIVING: Jul 21 2013 10:20 PM (Asia/Tokyo)
(Asia/Tokyo standard time will be in effect.)
|
java.time.OffsetDateTime
Класс java.time.OffsetDateTime можно рассматривать как комбинацию java.time.LocalDateTime и java.time.ZoneOffset . Он содержит полную дату, время и смещение от Гринвича/UTC (+/-часы:минуты).
Следующий пример использует класс java.time.OffsetDateTime и методTemporalAdjuster.lastDay, чтобы найти последний четверг в июле 2013 года.
1
2
3
4
5
6
7
8
9
|
// ищем последний четверг июля 2013.
LocalDateTime localDate = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneOffset offset = ZoneOffset.of(«-08:00»);
OffsetDateTime offsetDate = OffsetDateTime.of(localDate, offset);
OffsetDateTime lastThursday =
offsetDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY));
System.out.printf(«The last Thursday in July 2013 is the %sth.%n»,
lastThursday.getDayOfMonth());
|
Этот код выведет в консоль следующее:
1
|
The last Thursday in July 2013 is the 25th.
|
java.time.OffsetTime
Класс java.time.OffsetTime можно рассматривать как комбинациюjava.time.LocalTime и java.time.ZoneOffset . Он содержит время и смещение от Гринвича/UTC (+/-часы:минуты).
java.time.Instant
Класс java.time.Instant — это один из самых основных классов Date-Time API, который представляет наносекунды от 1 января 1970 года 00:00:00 по UTC. Класс java.time.Instant может содержать отрицательное значение, если он представляет время до 1 января 1970 года.
Пример создания:
1
2
3
|
import java.time.Instant;
Instant timestamp = Instant.now();
|
Класс содержит полезные константы: Instant.EPOCH (1 января 1970 00:00:00Z), Instant.MIN (самое прошлое время из возможных),Instant.MAX (самое будущее время из возможных).
Класс java.time.Instant содержит большое количество методов для манипулирования Instant. Методы plus и minus используются для добавления и вычитания времени. Следующий код добавляет один час к текущему времени:
1
|
Instant oneHourLater = Instant.now().plusHours(1);
|
Есть методы для сравнения экземпляров Instant, например isAfter и isBefore. Метод until возвращает время между двумя экземплярамиInstant. Следующий код сообщает количество секунд, прошедщих с начала эпохи:
1
2
|
long secondsFromEpoch = Instant.ofEpochSecond(0L).until(Instant.now(),
ChronoUnit.SECONDS);
|
Класс java.time.Instant работает с машинным представлением времени. Он не работает с годами, месяцами, часами и т. д. Если вам нужно работать с ними, то вам нужно преобразовать его вjava.time.Instant в другой класс, например java.time.LocalDateTime илиjava.time.ZonedDateTime, связав java.time.Instant с часовым поясом. Пример:
1
2
3
4
5
|
Instant timestamp;
...
LocalDateTime ldt = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault());
System.out.printf(«%s %d %d at %d:%d%n», ldt.getMonth(), ldt.getDayOfMonth(),
ldt.getYear(), ldt.getHour(), ldt.getMinute());
|
Этот код выводит в консоль что-то вроде этого:
1
|
MAY 30 2013 at 18:21
|
Классы java.time.ZonedDateTime и java.time.OffsetDateTime могут быть преобразованы в экземпляр java.time.Instant, так как они указывают на конкретный момент во времени. Однако для обратного преобразования нужно указать часовой пояс или смещение.
Форматирование и преобразование из строки
Временные классы из Date-Time API содержит методы parse и format для разбора даты и/или времени из строки и форматированного вывода в строку. Эти методы принимают экземпляр класса java.time.format.DateTimeFormatter. Класс DateTimeFormatter содержит большое количество предопределённых форматировщиков, вы также можете определить свой.
Методы parse и format бросают исключение, если во время конвертации возникает какая-нибудь проблема. Ваш код разбора строки должен отлавливать исключение java.time.format.DateTimeParseException, а форматирующий код должен отлавливать java.time.format.DateTimeException.Эти исключения непроверяемые, так что компилятор не будет требовать их обязательной обработки.
Класс java.time.format.DateTimeFormatter неизменяемый и потокобезопасный. Его можно присвоить константе, если нужно.
Классы из java.time можно использовать и в java.util.Formatter и вString.format, как и старые классы java.util.Date и java.util.Calendar.
java.time.temporal.TemporalAdjuster
Интерфейс java.time.temporal.TemporalAdjuster содержит методы, которые принимают значение времени и возвращает его выровненное по какому-либо принципу значение.
Класс java.time.temporal.TemporalAdjusters содержит предопределённые выравниватели даты и времени для поиска первого или последнего дня в месяце, первого или последнего дня года, последней среды месяца и т. д.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
LocalDate date = LocalDate.of(2000, Month.OCTOBER, 15);
DayOfWeek dotw = date.getDayOfWeek();
System.out.printf(«%s is on a %s%n», date, dotw);
System.out.printf(«first day of Month: %s%n»,
date.with(TemporalAdjusters.firstDayOfMonth()));
System.out.printf(«first Monday of Month: %s%n»,
date.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)));
System.out.printf(«last day of Month: %s%n»,
date.with(TemporalAdjusters.lastDayOfMonth()));
System.out.printf(«first day of next Month: %s%n»,
date.with(TemporalAdjusters.firstDayOfNextMonth()));
System.out.printf(«first day of next Year: %s%n»,
date.with(TemporalAdjusters.firstDayOfNextYear()));
System.out.printf(«first day of Year: %s%n»,
date.with(TemporalAdjusters.firstDayOfYear()));
|
Это выведет в консоль:
1
2
3
4
5
6
7
|
2000-10-15 is on a SUNDAY
first day of Month: 2000-10-01
first Monday of Month: 2000-10-02
last day of Month: 2000-10-31
first day of next Month: 2000-11-01
first day of next Year: 2001-01-01
first day of Year: 2000-01-01
|
java.time.temporal.TemporalQuery
Интерфейс java.time.temporal.TemporalQuery может использоваться для получения информации об объекте времени.
Класс java.time.temporal.TemporalQueries содержит предопределённые TemporalQuery. Например, в следующем примере получаем наименьшую java.time.temporal.ChronoUnit, которая может быть возвращена объектом времени:
1
2
3
4
5
6
7
8
9
10
11
|
TemporalQueries query = TemporalQueries.precision();
System.out.printf(«LocalDate precision is %s%n»,
LocalDate.now().query(query));
System.out.printf(«LocalDateTime precision is %s%n»,
LocalDateTime.now().query(query));
System.out.printf(«Year precision is %s%n»,
Year.now().query(query));
System.out.printf(«YearMonth precision is %s%n»,
YearMonth.now().query(query));
System.out.printf(«Instant precision is %s%n»,
Instant.now().query(query));
|
Вывод будет следующим:
1
2
3
4
5
|
LocalDate precision is Days
LocalDateTime precision is Nanos
Year precision is Years
YearMonth precision is Months
Instant precision is Nanos
|
java.time.Duration
Класс java.time.Duration используется для измерения промежутка времени между двумя машинными временными объектами, например Instant-ами.
Примеры:
1
2
3
|
Instant t1, t2;
...
long ns = Duration.between(t1, t2).toNanos();
|
1
2
3
4
|
Instant start;
...
Duration gap = Duration.ofSeconds(10);
Instant later = start.plus(gap);
|
Duration не связана с датой и временем, он не хранит информацию о зонах и переходе на летнее/зимнее время. Добавление Duration, эквивалентного 1 дню, к ZonedDateTime добавит ровно 24 часа, независимо от часового пояса и зимнего/летнего времени.
java.time.temporal.ChronoUnit
Перечисление java.time.temporal.ChronoUnit объявляет единицы измерения для времени. Метод ChronoUnit.between используется, когда вам нужно измерить количество времени только в днях, только в секундах или только в других одних единицах измерения времени. Метод between работает с объектами дат и времени, но возвращает только количество в определённых единицах. Пример:
1
2
3
4
5
6
7
8
9
10
11
|
import java.time.Instant;
import java.time.temporal.Temporal;
import java.time.temporal.ChronoUnit;
Instant previous, current, gap;
...
current = Instant.now();
if (previous != null) {
gap = ChronoUnit.MILLIS.between(previous,current);
}
...
|
java.time.Period
Класс java.time.Period определяет период времени в человеческих единицах: месяцах, днях, годах. Класс java.time.Period содержит методы getMonths, getDays, getYears, которые используются для получения соответствующих единиц времени из периода.
Общий период времени представляется суммой всех трёх частей: месяцев, дней, годов. Чтобы получить период только в одной единице времени, используйте ChronoUnit.between.
Следующий год сообщает ваш возраст в годах, месяцах, днях, а затем общее количество прожитых дней.
1
2
3
4
5
6
7
8
|
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);
Period p = Period.between(birthday, today);
long p2 = ChronoUnit.DAYS.between(birthday, today);
System.out.println(«You are « + p.getYears() + » years, « + p.getMonths() +
» months, and « + p.getDays() +
» days old. (« + p2 + » days total)»);
|
Код выводит в консоль следующее:
1
|
You are 53 years, 4 months, and 29 days old. (19508 days total)
|
Следующий пример считает, сколько осталось до следующего дня рождения:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);
LocalDate nextBDay = birthday.withYear(today.getYear());
// Если в этом году День Рождения уже прошёл, то добавляем один год.
if (nextBDay.isBefore(today) || nextBDay.isEqual(today)) {
nextBDay = nextBDay.plusYears(1);
}
Period p = Period.between(today, nextBDay);
long p2 = ChronoUnit.DAYS.between(today, nextBDay);
System.out.println(«There are « + p.getMonths() + » months, and « +
p.getDays() + » days until your next birthday. (« +
p2 + » total)»);
|
Вывод будет примерно таким:
1
|
There are 7 months, and 2 days until your next birthday. (216 total)
|
Эти вычисления не учитывают разницу в часовых поясах. Например, если вы родились в Австралии, а сейчас живёте в Бангалоре, то это может немного повлиять на вычисление вашего возраста. В подобных ситуациях используйте Period вместе сZonedDateTime.
java.time.Clock
Многие классы с датой и временем содержат метод now(), который создаёт объект с текущей датой и временем, используя системные часы и часовой пояс по умолчанию. Эти же объекты также содержат метод now(Clock), который позволяет передать другойjava.time.Clock.
Текущая дата и время зависят от часового пояса и для глобальных приложений необходим Сlock, чтобы гарантировать создание даты/времени с корректным часовым поясом. Несмотря на то что использование java.time.Clock необязательно, но эта возможность помогает тестировать ваш код с другими часовыми зонами или с фиксированным временем.
Класс Clock абстрактный, поэтому вы не можете создать его экземпляров. Следующие фабричные методы могут быть полезны для тестирования:
- Clock.offset(Clock, Duration) возвращает clock, который является смещением на указанный Duration.
- Clock.systemUTC() возвращает clock, содержащий часовой пояс Гринвич/UTC.
- Clock.fixed(Instant, ZoneId) всегда возвращает один и тот же Instant. Для этого Clock время не идёт, оно остановилось.