检查给定的Instant是否适合定义的Period

时间:2015-10-20 11:45:10

标签: java java-time

我们得到的是一个Instant和一个" date-grid"由句点定义(定义数据点的间隔,例如:每月,每3个月等)以及我们开始该网格的开始日期。

private Instant getValidDate(Instant request, Instant start, Period period) {
    if(isOnGrid(request, start, period)) {
        return request;
    }
    else {
        return getNextPriorDateOnGrid(request, start, period);
    }
}

一个例子: 给出以下参数:

request = Instant("2000-05-02T07:42:00.000Z") //Second May of 2000 7:42 AM
start = Instant("2000-01-01T06:00:00.000Z") //First January of 2000 6:00 AM
period = Period("3M") //Every 3 Months

isOnGrid(request, start, period); //Should return false
getNextPriorDate(request, start, period) //Should return the First April of 2000 6:00 AM

我真的不知道如何以合理的性能获得它(它在代码中的关键位置)

如何检查遥远的未来日期(由Instant给出)是否正好在此网格上,如果不是,那么网格上的下一个过去日期是什么?

编辑:我忘了提及:所有时间和日期都假定为UTC时区

2 个答案:

答案 0 :(得分:2)

这是一个符合您要求的简单测试用例:

package test;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;

    public class Java8PeriodAndInstant2 {

        public static void main(String[] args) {
            // LocalDate request=LocalDate.of(2000, 5, 2);
            // LocalDate start=LocalDate.of(2000, 1, 1);
            LocalDateTime start = Instant.parse("2000-01-01T06:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime();
            LocalDateTime request = Instant.parse("2000-05-02T07:42:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime();
            Period period = Period.ofMonths(3);
            System.out.println("is on grid " + isOnGrid(request, start, period));
            System.out.println("is on grid " + isOnGrid(LocalDateTime.of(2000, 4, 2,0,0), start, period));
            System.out.println("is on grid " + isOnGrid(LocalDateTime.of(2000, 4, 1,0,0), start, period));
            System.out.println("getNextPriorDate " + getNextPriorDate(request, start, period));
            System.out.println("isOnGrid " + isOnGrid(Instant.parse("2000-01-03T05:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime(), start, Period.ofDays(1)));
            System.out.println("isOnGrid " + isOnGrid(Instant.parse("2000-01-03T06:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime(), start, Period.ofDays(1)));
            System.out.println("getNextPriorDate " + getNextPriorDate(Instant.parse("2000-01-03T05:00:00.000Z").atZone(ZoneId.of("UTC")).toLocalDateTime(), start, Period.ofDays(1)));
        }

        private static boolean isOnGrid(LocalDateTime start, LocalDateTime request, Period period) {
            if (period.getDays() != 0) {
                return ((Duration.between(start, request).toHours()%period.getDays())==0);
            }
            Period diffPeriod = Period.between(start.toLocalDate(), request.toLocalDate());
            if (diffPeriod.getDays()!=0) {
                return false;
            }
            if (period.getMonths() != 0) {
                return ((diffPeriod.toTotalMonths()) % (period.toTotalMonths()) == 0);
            }
            if (diffPeriod.getMonths()!=0) {
                return false;
            }               
            if (period.getYears() != 0) {
                return ((diffPeriod.getYears()) % (period.getYears()) == 0);
            }   
            return false;
        }

        private static LocalDateTime getNextPriorDate(LocalDateTime request, LocalDateTime start, Period period) {
            if (period.getDays() != 0) {
                long hoursDiff=Duration.between(start, request).toHours();
                return start.plusDays(hoursDiff/24);
            }
            Period diffPeriod = Period.between(start.toLocalDate(), request.toLocalDate());
            if (period.getMonths() != 0) {
                diffPeriod = diffPeriod.withDays(0);
                long monthDiff = diffPeriod.toTotalMonths() % period.toTotalMonths();
                return start.plus(diffPeriod).minusMonths(monthDiff);
            }
            if (period.getYears() != 0) {
                diffPeriod = diffPeriod.withDays(0);
                diffPeriod.withMonths(0);
                long yearsDiff = diffPeriod.getYears() % period.getYears();
                return start.plus(diffPeriod).minusYears(yearsDiff);
            }               
            return null;
        }

    }

它适用于几天或几个月或几年。

答案 1 :(得分:0)

您无法将Period添加到Instant。他们有不同的范围"。

Instant i 只是代表时间轴中的一个点,计算一个特定时间点的毫秒数/毫微秒数" Epoch"。br /> 在这个时刻 i ,墙上的时钟(甚至是日历中的日期)的时间在世界各地不同。这取决于你所在的时区。

Period尊重不同时区的不同长度,从不同的日期开始。例如:一个月在六月持续30天,在八月持续31天。如果发生夏令时变化则更加复杂 Instant不知道,"月"实际上是。您可以从String解析它并将其输出到它,但在内部它不代表一个月的人类可理解的形式,例如' Jan',' 2月' ,. ..。

这就是为什么您必须使用InstantLocalDateTimeZonedDateTimeZoneIdZoneOffset对齐。这些课程理解并可以使用Period s。

以下代码会将您的Instant转换为LocalDateTime,以考虑上述评论:

private static Instant getValidDate2(Instant request, Instant start, Period period)
{
    assert(!request.isBefore(start));

    // multiplication of period only works with days exclusive or
    // zero daypart of period
    assert(period.getDays() == 0 || (period.getMonths() == 0 && period.getYears() == 0));

    ZoneId utcZone = ZoneOffset.UTC;

    LocalDateTime ldstart = LocalDateTime.ofInstant(start, utcZone);
    LocalDateTime ldreq = LocalDateTime.ofInstant(request, utcZone);

    // calculate an approximation of how many periods have to be applied to get near request
    Duration simpleDuration = Duration.between(ldstart, ldstart.plus(period));
    Duration durationToReq = Duration.between(ldstart, ldreq);
    int factor = (int) (durationToReq.toDays() / simpleDuration.toDays()); // rough approximation

    // go near to request by a multiple of period 
    Period jump = Period.of(period.getYears() * factor, period.getMonths() * factor, period.getDays() * factor);
    LocalDateTime ldRunning = ldstart.plus(jump);

    // make sure ldRunning < request
    while (ldRunning.isAfter(ldreq)) {
        ldRunning = ldRunning.minus(period);
    }

    // make sure we pass request and 
    // save the the last date before or equal to request on the grid
    LocalDateTime ldLastbefore = ldRunning;
    while (!ldRunning.isAfter(ldreq)) {            
        ldLastbefore = ldRunning;
        ldRunning = ldRunning.plus(period);
    }

    return ldLastbefore.equals(ldreq) ? request : ldLastbefore.atZone(utcZone).toInstant();
}

说明:
要避免循环添加period,直到它到达request,我们会对period必须添加startrequest的频率进行粗略近似。 。然后添加并对齐新的句点作为请求period的倍数,以获得小于或等于request的网格的最后一个值。根据最后一个值与request之间的比较,返回相应的时刻。事实上,除了request == request它在网格上而不仅仅是equal时,检查是没用的。

在这里您可以找到有关java时间的更多信息:https://docs.oracle.com/javase/tutorial/datetime/overview/index.html