检查给定的 Instant 是否符合定义的 Period
Check whether a given Instant fits a defined Period
我们得到的是一个 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 时区
这是一个应该符合您要求的简单测试用例:
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;
}
}
它适用于几天、几个月或几年的周期。
您不能将 Period
添加到 Instant
。他们有不同的"scope"。
一个Instant
i简单的表示时间轴上的一个点,从一个叫做[=58=的特定时间点开始计算millis/nanos的数量].
此刻i,世界各地墙上挂钟的时间(甚至日历中的日期)都不同。这取决于您所在的时区。
A Period
尊重从不同日期开始的不同时区的不同表示长度。例如:一个月在 6 月持续 30 天,但在 8 月持续 31 天。如果发生夏令时变化,那就更复杂了。
Instant
不知道 "month" 实际上是什么。您可以从 String
解析它并将其输出给它,但在内部它并不代表人类可以理解的月份形式,例如 'Jan'、'Feb'、... [=36] =]
这就是为什么您必须使用 ZoneId
或 ZoneOffset
将 Instant
与 LocalDateTime
或 ZonedDateTime
对齐的原因。论文 类 理解并可以与 Period
一起使用。
以下代码将您的 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
添加到 start
以达到 request
的频率.然后添加一个新周期作为请求 period
的倍数并对齐以获得小于或等于 request
的网格的最后一个值。根据最后一个值和 request
之间的比较,返回相应的时刻。事实上,除了 request == request
在网格上而不仅仅是 equal
.
之外,检查是无用的
在这里您可以找到关于 java 时间的更多信息:https://docs.oracle.com/javase/tutorial/datetime/overview/index.html
我们得到的是一个 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 时区
这是一个应该符合您要求的简单测试用例:
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;
}
}
它适用于几天、几个月或几年的周期。
您不能将 Period
添加到 Instant
。他们有不同的"scope"。
一个Instant
i简单的表示时间轴上的一个点,从一个叫做[=58=的特定时间点开始计算millis/nanos的数量].
此刻i,世界各地墙上挂钟的时间(甚至日历中的日期)都不同。这取决于您所在的时区。
A Period
尊重从不同日期开始的不同时区的不同表示长度。例如:一个月在 6 月持续 30 天,但在 8 月持续 31 天。如果发生夏令时变化,那就更复杂了。
Instant
不知道 "month" 实际上是什么。您可以从 String
解析它并将其输出给它,但在内部它并不代表人类可以理解的月份形式,例如 'Jan'、'Feb'、... [=36] =]
这就是为什么您必须使用 ZoneId
或 ZoneOffset
将 Instant
与 LocalDateTime
或 ZonedDateTime
对齐的原因。论文 类 理解并可以与 Period
一起使用。
以下代码将您的 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
添加到 start
以达到 request
的频率.然后添加一个新周期作为请求 period
的倍数并对齐以获得小于或等于 request
的网格的最后一个值。根据最后一个值和 request
之间的比较,返回相应的时刻。事实上,除了 request == request
在网格上而不仅仅是 equal
.
在这里您可以找到关于 java 时间的更多信息:https://docs.oracle.com/javase/tutorial/datetime/overview/index.html