Как получить разницу между двумя временами в joda.time (правильно использовать класс Period)

Java задняя часть открытый источник
Как получить разницу между двумя временами в joda.time (правильно использовать класс Period)

предисловие

Дата и календарь, используемые Java для обработки времени и даты, подверглись критике. Основная проблема календаря заключается в том, что объекты являются переменными, а классы, такие как время и дата, должны быть неизменными. Кроме того, его концептуальная модель также неоднозначна. Расчет месяца начинается с 0 для начала и так далее.

Библиотека времени/даты с открытым исходным кодом JodaTimeЭто хорошая замена, кроме того, в Java8 также запущена новая библиотека java.time, а концепция дизайна похожа на JodaTime.

Joda-Time позволяет легко управлять значениями времени и даты, манипулировать ими и понимать их. Простота использования — основная цель дизайна Joda. Основной класс Joda-Time DateTime и старые классы JDK Date и Calendar могут быть преобразованы друг в друга. Таким образом обеспечивается совместимость с фреймворком JDK.

В этой статье в основном рассказывается о классе Period и о том, как правильно расширять разницу между двумя значениями времени путем анализа метода построения.Подробности см. ниже.

Общие операции

Наиболее часто используется org.joda.timeлогика компьютерной программыиИзящная обработка даты и времени с помощью Joda-TimeЗдесь есть хорошие вводные, поэтому я не буду повторять их здесь, давайте в основном рассмотрим класс Period, который обрабатывает период времени (разницу между двумя датами).

Введение в период

неизменяемый период времени, определяемый набором значений поля продолжительности

Период времени представлен несколькими полями, такими как год, месяц и день. Эти поля определяются классом PeriodType. Значением по умолчанию для PeriodType является стандартный тип, который поддерживает год, месяц, неделю, день, час, минуту. , секунды и миллисекунды.

Конструктор

第一种:直接声明
public Period() {
	super(0L, (PeriodType)null, (Chronology)null);
}
第二种:传入年、月、周、日、时、分、秒、毫秒等值 此处一定要注意中间的周这个值的定义
public Period(int var1, int var2, int var3, int var4) {
	super(0, 0, 0, 0, var1, var2, var3, var4, PeriodType.standard());
}
第三种:获取两个ReadableInstant实现类(如DateTime)之间的时间段
public Period(ReadableInstant var1, ReadableInstant var2, PeriodType var3) {
	super(var1, var2, var3);
}

Правильно получить разницу между двумя временами

В Интернете есть много случаев, которые говорят о решении получить информацию, например, о промежутке между двумя временами:

Вычислите разницу между двумя временами

DateTime start = new DateTime(2016,8,18,10,58);
DateTime end = new DateTime(2016,9,19,12,3);
Period period = new Period(start,end);        
System.out.println(period.getMonths()+"月"+period.getDays()+"天"
       +period.getHours()+"小时"+period.getMinutes()+"分");

Результат:

1月1天1小时5分

Пока задано время начала и окончания, Period может автоматически вычислять, сколько месяцев, сколько дней, сколько часов и так далее между двумя временами.

Здесь есть проблема, которая не объяснена, возьмем случай в качестве примера.

DateTime d1 = new DateTime(2018,1,27,0,30);
DateTime d2 = new DateTime(2018,2,26,2,30);
Period p0 = new Period(d1, d2);
System.out.println(p0.getYears() + "年" + p0.getMonths() + "月"  + p0.getWeeks() + "周" + p0.getDays() + "天");

Результат:

0年0月4周2天

Не соответствует ли это ожидаемым 30 дням? Давайте посмотрим, почему

使用new Period(ReadableInstant var1, ReadableInstant var2)进行Period的实例化
这个构造方法默认使用的PeriodType
public Period(ReadableInstant var1, ReadableInstant var2) { 
   super(var1, var2, (PeriodType)null);
}
这时候指定的PeriodType为null,再继续向下看Period的上级抽象父类BasePeriod的构造方法
protected BasePeriod(ReadableInstant var1, ReadableInstant var2, PeriodType var3) {
   //1. 检查一下var3对应的值
   var3 = this.checkPeriodType(var3);
   if (var1 == null && var2 == null) {
   	//6. 这里将iType赋值为var3指定的Period
   	this.iType = var3;
       this.iValues = new int[this.size()];
    } else {
    	long var4 = DateTimeUtils.getInstantMillis(var1);
       long var6 = DateTimeUtils.getInstantMillis(var2);
       Chronology var8 = DateTimeUtils.getIntervalChronology(var1, var2);
       this.iType = var3;
       this.iValues = var8.get(this, var4, var6);
    }
}
protected PeriodType checkPeriodType(PeriodType var1) {
   //2. 调用DateTimeUtils工具类的getPeriodType()传入var1 也就是上一代码块的var3(也就是null)
   return DateTimeUtils.getPeriodType(var1);
}
public static final PeriodType getPeriodType(PeriodType var0) {
   //3. 在这里就可以看到 传入的null最终转化为了PeriodType.standard()
   return var0 == null ? PeriodType.standard() : var0;
}
//4. 在PeriodType看到静态方法 实例standard并返回对应的Period
public static PeriodType standard() {
   PeriodType var0 = cStandard;
   if (var0 == null) {
   	//5. 这里可以看到在Standard中指定了年月周天时分秒(有兴趣的可以看下PeriodType的实例的设计)
   	//这里的猫腻就在于Standard中指定了周!!!
  		var0 = new PeriodType("Standard", new DurationFieldType[]{DurationFieldType.years(), DurationFieldType.months(), DurationFieldType.weeks(), DurationFieldType.days(), DurationFieldType.hours(), DurationFieldType.minutes(), DurationFieldType.seconds(), DurationFieldType.millis()}, new int[]{0, 1, 2, 3, 4, 5, 6, 7});
   	cStandard = var0;
   }

   return var0;
}

Таким образом, правильная поза для получения разницы между двумя датами, такими как год, месяц и день, должна быть следующей.

DateTime d1 = new DateTime(2018,1,27,0,30);
DateTime d2 = new DateTime(2018,2,26,2,30);
//指定PeriodType为yearMonthDayTime
Period p2 = new Period(d1, d2, PeriodType.yearMonthDayTime());
System.out.println(p2.getYears() + "年" + p2.getMonths() + "月"+ p2.getWeeks() + "周" + p2.getDays() + "天");

Введите результат:

0年0月0周30天

Запросить официальную документацию

public Period(ReadableInstant startInstant,
              ReadableInstant endInstant)

Most calculations performed by this method have obvious results. The special case is where the calculation is from a "long" month to a "short" month. Here, the result favours increasing the months field rather than the days. For example, 2013-01-31 to 2013-02-28 is treated as one whole month. By contrast, 2013-01-31 to 2013-03-30 is treated as one month and 30 days (exposed as 4 weeks and 2 days). The results are explained by considering that the start date plus the calculated period result in the end date.

В основном это означает две вещи:

  1. 2013-01-31 to 2013-03-30 is treated as one month and 30 days (exposed as 4 weeks and 2 days)

Считается, что с 31 января 2013 г. по 30 марта 2013 г. один месяц и 30 дней (где 30-дневная экспозиция равна 4 неделям и 2 дням).

Причины и реализация были проанализированы выше, и если желаемое значение получено

  1. Расчет от большого месяца к маленькому месяцу требует внимания, например, 2013-01-31 до 2013-02-28 - это месяц.

    2013-01-29 по 2013-02-28 тоже месяц

    2013-01-28 по 2013-02-28 еще месяц

    2013-01-27 по 2013-02-28 один месяц и один день

    Это тоже стоит отметить

Суммировать

Автор также использовал класс Period для обработки периода времени при использовании joda.time для обработки разницы между двумя временами в проекте, но возникла проблема, которая отличалась от ожидаемого значения в процессе использования, а затем прочитал данные для исправления сводки использования Взгляните, я надеюсь, что это может вам помочь. Дополнительные сведения об использовании см. в официальной документации:User Guide.

использованная литература

официальный API joda.time

логика компьютерной программы