Java 检查给定的瞬间是否符合定义的时间段

Java 检查给定的瞬间是否符合定义的时间段,java,java-time,Java,Java Time,我们得到的是一个瞬间和一个“日期网格”,由一个周期(定义数据点的间隔,例如:每个月、每3个月等)和我们开始该网格的开始日期定义 private Instant getValidDate(Instant request, Instant start, Period period) { if(isOnGrid(request, start, period)) { return request; } else { return getNextPr

我们得到的是一个瞬间和一个“日期网格”,由一个周期(定义数据点的间隔,例如:每个月、每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
我真的不知道如何获得合理的性能(这是代码中的一个关键位置)

你如何检查一个遥远的未来日期(由瞬间给出)是否正好在这个网格上,如果没有,网格上的下一个过去的日期是什么


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

这里有一个简单的测试用例,它应该符合您的要求:

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;
        }

    }

它以天、月或年为周期。

您不能将
周期
s添加到
即时
s。它们有不同的“范围”

一个
瞬间
i只是表示时间线上的一个点,从一个称为“历元”的特定时间点开始计算毫秒/纳米的数量。
在这一刻,我发现墙上的时钟上的时间(甚至日历上的日期)在世界各地都是不同的。这取决于你所在的时区

一个
时段
从不同日期开始,在不同的时区中表示不同的长度。例如:一个月在六月持续30天,但在八月持续31天。如果夏令时转换发生,情况就更复杂了。
即时的不知道“月”是什么。您可以从
字符串中解析它,并将其输出到它,但在内部,它并不表示一种人类可以理解的月份形式,如“一月”、“二月”等等

这就是为什么必须使用
ZoneId
ZoneOffset将
Instant
LocalDateTime
ZoneDateTime
对齐的原因。这些类理解并可以使用
Period
s

以下代码将您的
Instant
s转换为
LocalDateTime
s,以考虑上述注释:

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();
}
私有静态即时GetValidData2(即时请求、即时启动、期间)
{
断言(!request.isBefore(start));
//周期的乘法仅适用于天数为异或的情况
//零日时段
断言(period.getDays()==0 | |(period.getMonths()==0&&period.getYears()==0));
ZoneId utcZone=ZoneOffset.UTC;
LocalDateTime ldstart=LocalDateTime.ofInstant(start,utcZone);
LocalDateTime ldreq=LocalDateTime.ofInstant(请求,utcZone);
//计算接近请求所需的周期数的近似值
Duration simpleDuration=Duration.between(ldstart,ldstart.plus(period));
Duration durationToReq=持续时间(ldstart,ldreq);
int factor=(int)(durationToReq.toDays()/simpleDuration.toDays());//粗略近似值
//以周期的倍数接近请求
期间跳转=期间(Period.getYears()*因子,Period.getMonths()*因子,Period.getDays()*因子);
LocalDateTime ldRunning=ldstart.plus(跳转);
//确保ldRunning<请求
while(ldRunning.isAfter(ldreq)){
ldRunning=ldRunning.减(周期);
}
//确保我们通过了请求并
//在网格上保存请求之前或等于请求的最后日期
LocalDateTime ldLastbefore=ldRunning;
当(!ldRunning.isAfter(ldreq)){
ldLastbefore=ldRunning;
ldRunning=ldRunning.plus(期间);
}
返回ldLastbefore.equals(ldreq)?请求:ldLastbefore.atZone(utcZone.toInstant();
}
说明:
为了避免循环添加
周期
,直到到达
请求
,我们粗略估计了
周期
必须添加到
开始
才能到达
请求
的频率。然后添加一个新的周期,该周期是请求的倍数
period
,并对齐以获得网格的最后一个值,该值小于或等于
request
。根据上一个值和
请求
之间的比较,返回相应的瞬间。事实上,除了在网格上时
request==request
而不仅仅是
equal
之外,该检查是无用的


在这里,您可以找到有关java时间的更多信息:

如何计算网格上的下一个过去日期?从样本数据来看,我会说是2000年4月1日,而不是2000年4月2日。我正在处理OP的问题。2000年4月1日是对的,一定是打字错误。我会编辑答案,但我们必须等待他接受。如果请求在开始之前,会发生什么?同样是2000年4月1日?将抓住“开始前”的案例,并抛出一个exception@Giovanni4月2日是前一个例子留下的打字错误。我们希望请求的日期超过开始日期,否则正如Abbel所说,它将在方法执行之前被捕获。非常感谢!此解决方案的工作原理如+1所述。但我们需要一个解决方案,它也可以处理天数(如果可能的话,甚至可以考虑每天的时间)。该期间将有若干天、若干个月或若干年。这意味着不会有任何像“P1Y3M”这样的混杂的东西。编辑:我注意到这不清楚:以OP:2000-01-01T06:00:00.000Z为例,以一天为开始,间隔应映射2000-01-03T05:00:00.000Z到2000-01-02T06:00:00.000Z,因为映射的值在下一天没有达到6:00day@Abbel是对的,,混合的间隔不应该被考虑,因为我们根本不打算支持它们。@ abbeli用处理几天和几年来更新响应,这似乎是一个非常好的借口,遗憾的是,似乎你的ISONGRID()不考虑不同的时间,也不考虑请求的不同时间。因此,4月2日仍然是以月为间隔的一天,而从技术上讲,它应该始终是每个月的1日(如果sta