Between: примеры (JAVA)
between(Temporal startInclusive, Temporal endExclusive): longОбщее описание методов между значениями (between) в Java
В Java явной функции с именем between как универсального оператора не существует, однако в стандартной библиотеке присутствуют несколько методов с именем between, используемых для вычисления разницы между временными значениями и для измерения промежутков. Основные реализations находятся в пакете java.time:
- java.time.Duration.between(Temporal, Temporal) - вычисляет временной интервал между двумя моментами во времени с точностью до наносекунд. Аргументы: два объекта, реализующие интерфейс
Temporal(например,Instant,LocalDateTime,ZonedDateTime). ВозвращаетDuration. Семантика: первый аргумент - начало (включительно), второй - конец (исключительно). Возможные исключения:DateTimeException,ArithmeticException,NullPointerException. - java.time.Period.between(LocalDate, LocalDate) - вычисляет период в целых годах, месяцах и днях между двумя датами. Аргументы: два
LocalDate(startInclusive, endExclusive). ВозвращаетPeriod. Исключения:DateTimeException,NullPointerException. - java.time.temporal.ChronoUnit.between(Temporal, Temporal) - универсальный способ получить разницу в выбранных единицах (дни, часы, месяцы и т. п.). Возвращает
long. Поддерживает отрицательные значения при обратном порядке аргументов. Исключения:UnsupportedTemporalTypeException,DateTimeException,ArithmeticException,NullPointerException.
Ключевые особенности поведения:
- Семантика границ обычно: начало включается, конец исключается.
Durationотражает точную величину во времени (секунды и наносекунды),Period- календарные компоненты (годы, месяцы, дни),ChronoUnit.betweenвозвращает количество выбранных единиц.- При работе с зонами и DST следует использовать типы с зоной (
ZonedDateTime) и учитывать смещения. - Некорректные сочетания типов (например, попытка использовать
Period.betweenдляInstant) приводят к исключениям.
Короткие примеры использования между значениями
Примеры демонстрируют типичные варианты: Period, Duration и ChronoUnit.
// 1. Period.between - разница в годах/месяцах/днях
import java.time.LocalDate;
import java.time.Period;
LocalDate d1 = LocalDate.of(1980, 1, 1);
LocalDate d2 = LocalDate.of(2024, 4, 20);
Period p = Period.between(d1, d2);
System.out.println(p); // P44Y3M19D
System.out.println(p.getYears() + " years, " + p.getMonths() + " months, " + p.getDays() + " days");
P44Y3M19D 44 years, 3 months, 19 days
// 2. Duration.between - точный интервал между LocalDateTime
import java.time.Duration;
import java.time.LocalDateTime;
LocalDateTime t1 = LocalDateTime.of(2024, 4, 20, 10, 0);
LocalDateTime t2 = LocalDateTime.of(2024, 4, 21, 12, 30);
Duration d = Duration.between(t1, t2);
System.out.println(d); // PT26H30M
System.out.println(d.toHours() + " hours, " + d.toMinutesPart() + " minutes");
PT26H30M 26 hours, 30 minutes
// 3. ChronoUnit.between - количество дней
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
long days = ChronoUnit.DAYS.between(LocalDate.of(2024, 4, 20), LocalDate.of(2024, 4, 25));
System.out.println(days);
5
// 4. Отрицательный результат при обратном порядке
long rev = ChronoUnit.DAYS.between(LocalDate.of(2024, 4, 25), LocalDate.of(2024, 4, 20));
System.out.println(rev);
-5
// 5. Duration.between с Instant и вывод в секундах
import java.time.Duration;
import java.time.Instant;
Instant i1 = Instant.parse("2024-04-20T10:00:00Z");
Instant i2 = Instant.parse("2024-04-21T12:30:00Z");
Duration dd = Duration.between(i1, i2);
System.out.println(dd.toSeconds());
95400
Похожие подходы в Java и их особенности
- compareTo / isBefore / isAfter у типов
LocalDate,LocalDateTime,Instant. Предназначены для сравнения, но не дают разницу в числах или в компонентах периода. - Temporal.until(Temporal endExclusive, TemporalUnit unit) - метод экземпляра, похожий на
ChronoUnit.between, используется когда требуется вызвать от конкретного объекта. - java.time.temporal.ChronoUnit vs Period/Duration: ChronoUnit удобен для получения целого количества выбранных единиц, Period и Duration - для представления результата в специализированной форме.
- Библиотеки (Guava, Joda-Time) предлагают свои способы работы с промежутками и диапазонами; встроенные java.time обычно предпочтительнее при работе с датами/временем в современных проектах.
Аналоги в других языках и отличия от Java
Краткие примеры для распространенных языков с выводом результата.
-- SQL: BETWEEN (инклюзивно)
SELECT * FROM orders WHERE order_date BETWEEN '2024-04-20' AND '2024-04-25';
-- оператор возвращает строки с датами в диапазоне включительно
-- Результат зависит от данных в таблице; оператор проверяет включительно обе границы
// JavaScript: разница в днях (нативный Date)
const d1 = new Date('2024-04-20');
const d2 = new Date('2024-04-25');
const days = (d2 - d1) / (1000 * 60 * 60 * 24);
console.log(days);
5
# Python: datetime и timedelta
from datetime import date
d1 = date(2024,4,20)
d2 = date(2024,4,25)
print((d2 - d1).days)
5
// C#: DateTime.Subtract -> TimeSpan
using System;
var d1 = new DateTime(2024,4,20);
var d2 = new DateTime(2024,4,25);
Console.WriteLine((d2 - d1).TotalDays);
5
-- PHP: DateTime::diff
$d1 = new DateTime('2024-04-20');
$d2 = new DateTime('2024-04-25');
$diff = $d1->diff($d2);
echo $diff->days; // общее количество дней
5
// Go: time.Sub
package main
import (
"fmt"
"time"
)
func main() {
t1 := time.Date(2024,4,20,0,0,0,0,time.UTC)
t2 := time.Date(2024,4,25,0,0,0,0,time.UTC)
fmt.Println(t2.Sub(t1).Hours() / 24)
}
5
Отличия от Java:
- SQL BETWEEN - оператор уровня запроса и по умолчанию инклюзивен, в то время как java.time методы имеют семантику включительно-исключительно для вычислений.
- Во многих динамических языках (JS, Python, PHP) разницы выполняются через вычитание дат, результат часто представлен как число дней или объект timedelta/TimeSpan. Java предлагает специализированные типы
PeriodиDurationс богатым набором методов. - Короткие операции в других языках обычно работают в миллисекундах/секундах по умолчанию; java.time гарантирует точность и явную семантику компонентов.
Типичные ошибки при использовании между методами
- Путаница с включением границ: ожидание, что обе границы включаются, тогда как многие методы считают конец исключительным. Пример: ожидание 6 дней вместо 5 при неверной интерпретации.
- Использование неподходящих типов:
Period.betweenпринимает именноLocalDate. Попытка передатьInstantприводит к ошибке компиляции или исключению времени выполнения. - Игнорирование часовых поясов: при работе с локальным временем и ZonedDateTime разница может измениться из-за перехода на летнее/зимнее время.
- Отсутствие проверки на null - типичные
NullPointerException.
// Пример ошибки: смешение типов
import java.time.Instant;
import java.time.Period;
Instant a = Instant.now();
Instant b = Instant.now().plusSeconds(3600);
// Period p = Period.between(a, b); // Ошибка компиляции: нет подходящего метода
// Компилятор выдаст сообщение об отсутствующем методе для типов Instant
// Пример: влияние часового пояса (переход DST)
import java.time.ZonedDateTime;
import java.time.Duration;
import java.time.ZoneId;
ZonedDateTime z1 = ZonedDateTime.of(2024,3,30,1,0,0,0, ZoneId.of("Europe/Berlin"));
ZonedDateTime z2 = ZonedDateTime.of(2024,3,30,4,0,0,0, ZoneId.of("Europe/Berlin"));
System.out.println(Duration.between(z1, z2).toHours());
// Результат может быть 2 или 3 в зависимости от перехода на летнее время в этой зоне
Изменения и эволюция методов для вычисления промежутков
Основная функциональность методов between в пакете java.time была введена в Java 8 и с тех пор в базовой логике осталась стабильной. В последующих версиях JDK проводились расширения и фиксы вокруг поддержки дополнительных хронологий и удобных утилит, однако принципы работы Duration.between, Period.between и ChronoUnit.between не претерпели принципиальных изменений. Рекомендация для новых проектов - использование java.time вместо устаревших API (например, java.util.Date или Joda-Time).
Расширенные и редкие сценарии применения
Несколько подробных примеров с пояснениями.
// 1. Подсчет рабочих дней (без учета праздников)
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
LocalDate start = LocalDate.of(2024,4,1);
LocalDate end = LocalDate.of(2024,4,15);
long workDays = 0;
for (LocalDate d = start; d.isBefore(end); d = d.plusDays(1)) {
int dow = d.getDayOfWeek().getValue(); // 1 = Monday ... 7 = Sunday
if (dow >= 1 && dow <= 5) workDays++;
}
System.out.println("Рабочих дней: " + workDays);
Рабочих дней: 11
// 2. Разница в месяцах с учётом конца месяца
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
LocalDate d1 = LocalDate.of(2024,1,31);
LocalDate d2 = LocalDate.of(2024,2,29); // високосный год
long months = ChronoUnit.MONTHS.between(d1.withDayOfMonth(1), d2.withDayOfMonth(1));
System.out.println(months); // способ считать полные месяцы, учитывая края
1
// 3. Совмещение Period и Duration для человекочитаемого результата
import java.time.*;
ZonedDateTime s = ZonedDateTime.of(2023,1,15,10,0,0,0, ZoneId.of("Europe/Moscow"));
ZonedDateTime e = ZonedDateTime.of(2024,4,20,12,30,0,0, ZoneId.of("Europe/Moscow"));
Period per = Period.between(s.toLocalDate(), e.toLocalDate());
ZonedDateTime interm = s.plus(per);
Duration dur = Duration.between(interm, e);
System.out.println(per); // P1Y3M5D
System.out.println(dur); // PT2H30M
P1Y3M5D PT2H30M
// 4. Учет перехода на летнее/зимнее время при вычислении интервала
import java.time.*;
ZonedDateTime a = ZonedDateTime.of(2024,3,30,1,30,0,0, ZoneId.of("Europe/Berlin"));
ZonedDateTime b = ZonedDateTime.of(2024,3,30,3,30,0,0, ZoneId.of("Europe/Berlin"));
System.out.println(Duration.between(a, b).toHours()); // может вернуть 1 или 2 в зависимости от перехода
1 или 2 (в зависимости от того, произошёл ли сдвиг времени в указанной зоне)
// 5. Подсчет возраста в годах и месяцах с помощью ChronoUnit
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
LocalDate birth = LocalDate.of(1990,7,10);
LocalDate now = LocalDate.of(2024,4,20);
long years = ChronoUnit.YEARS.between(birth, now);
LocalDate tmp = birth.plusYears(years);
long months = ChronoUnit.MONTHS.between(tmp, now);
System.out.println(years + " years, " + months + " months");
33 years, 9 months
Пояснения: в сложных случаях рекомендуется разделять календарную и временную составляющие: Period для дат, Duration - для времени. ChronoUnit удобен для получения количества единиц, когда нужен простой числовой ответ.