用户可以按初始日期和结束日期/时间(时间戳)过滤报告。假设当前的过滤器:
首字母:2018-01-01 13:00:00
结尾:2018-01-05 04:00:00
如何在Java中计算过滤期间内22:00
和05:00 AM
(第二天)之间的小时和分钟总数。
我们当前正在使用Java 8类(LocalDateTime等)。
上述过滤器的预期结果:27小时0分钟(而不是87小时)!
详细信息:
From day 01 to day 02 we overlap the interested hour range (22h - 5h) so
for day 01 to day 02 we add 7 hours to the total amount.
From day 02 to day 03 we add another 7 hours.
From day 03 to day 04 we add another 7 hours.
From day 04 to day 05 we add 6 hours because the end filter finishes at 04:00 AM so we should not consider the last hour.
如果结束时间戳为2018-01-05 04:30:00
,则最终结果将为27小时30分钟。
此外,解决方案必须考虑DST更改。我们在操作中可以使用客户端时区,因此解决方案可能是使用OffsetDateTime
类。但是我不知道在这种情况下如何正确处理DST。
答案 0 :(得分:3)
误以为这个问题是重复的,@ Luiz纠正了我。向我道歉。
要针对DST计算Duration
,请遵循以下answer。该代码还涵盖了leap年。
编辑:
@XiCoN JFS指出了一些错误。我查看并测试了我的代码。我发布了测试课程 here。我试图涵盖所有我能想到的情况。
这是我的解决方案:
public class Durations {
public static Duration getSumOfHoursOnDays(ZonedDateTime dateTimeFrom, ZonedDateTime dateTimeTo, LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
Duration result = Duration.of(0, ChronoUnit.HOURS);
Duration hoursOnFirstDay = getHoursOnFirstDay(dateTimeFrom, dateTimeTo, dailyTimeFrom, dailyTimeTo);
result = result.plus(hoursOnFirstDay);
long daysBetween = ChronoUnit.DAYS.between(dateTimeFrom.truncatedTo(ChronoUnit.DAYS), dateTimeTo.truncatedTo(ChronoUnit.DAYS));
if (daysBetween > 0) {
for (int i = 1; i < daysBetween; i++) {
ZonedDateTime day = dateTimeFrom.plusDays(i);
Duration hoursOnDay = getHoursOnDay(day, dailyTimeFrom, dailyTimeTo);
result = result.plus(hoursOnDay);
}
Duration hoursOnLastDay = getHoursOnLastDay(dateTimeFrom, dateTimeTo, dailyTimeFrom, dailyTimeTo);
result = result.plus(hoursOnLastDay);
}
return result;
}
protected static Duration getHoursOnFirstDay(ZonedDateTime dateTimeFrom, ZonedDateTime dateTimeTo, LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
ZonedDateTime dateTimeToOnFirstDay = dateTimeTo.truncatedTo(ChronoUnit.DAYS).isAfter(dateTimeFrom.truncatedTo(ChronoUnit.DAYS)) ?
dateTimeFrom.plusDays(1).withHour(0) :
dateTimeTo;
return getHoursOnDay(dateTimeFrom, dateTimeToOnFirstDay, dailyTimeFrom, dailyTimeTo);
}
protected static Duration getHoursOnLastDay(ZonedDateTime dateTimeFrom, ZonedDateTime dateTimeTo, LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
return dateTimeTo.truncatedTo(ChronoUnit.DAYS).isAfter(dateTimeFrom.truncatedTo(ChronoUnit.DAYS)) ?
getHoursOnDay(dateTimeTo.withHour(0), dateTimeTo, dailyTimeFrom, dailyTimeTo) :
Duration.ofHours(0);
}
protected static Duration getHoursOnDay(ZonedDateTime day, LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
ZonedDateTime zoneTimeFrom = day.with(dailyTimeFrom);
ZonedDateTime zoneTimeTo = day.with(dailyTimeTo);
return zoneTimeFrom.isBefore(zoneTimeTo) ?
Duration.between(zoneTimeFrom, zoneTimeTo) :
Duration.between(day.withHour(0), zoneTimeTo).plus(Duration.between(zoneTimeFrom, day.plusDays(1).withHour(0)));
}
protected static Duration getHoursOnDay(ZonedDateTime dateTimeFrom, ZonedDateTime dateTimeTo, LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
ZonedDateTime dailyDateTimeFrom = dateTimeFrom.with(dailyTimeFrom);
ZonedDateTime dailyDateTimeTo = dateTimeFrom.with(dailyTimeTo);
if (dailyDateTimeFrom.isBefore(dailyDateTimeTo)) {
if (dailyDateTimeFrom.isAfter(dateTimeTo) || dailyDateTimeTo.isBefore(dateTimeFrom)) {
return Duration.ofHours(0);
}
ZonedDateTime from = dateTimeFrom.isAfter(dailyDateTimeFrom) ?
dateTimeFrom :
dailyDateTimeFrom;
ZonedDateTime to = dateTimeTo.isBefore(dailyDateTimeTo) ?
dateTimeTo :
dailyDateTimeTo;
return Duration.between(from, to);
}
Duration result = Duration.ofHours(0);
ZonedDateTime to = dateTimeTo.isBefore(dailyDateTimeTo) ?
dateTimeTo :
dailyDateTimeTo;
if (dateTimeFrom.isBefore(dailyDateTimeTo)) {
result = result.plus(Duration.between(dateTimeFrom, to));
}
ZonedDateTime from = dateTimeFrom.isAfter(dailyDateTimeFrom) ?
dateTimeFrom :
dailyDateTimeFrom;
if (from.isBefore(dateTimeTo)) {
result = result.plus(Duration.between(from, dateTimeTo));
}
return result;
}
}
主要挑战是应对开始时间在开始时间之后的白天。问题中给出了一个示例:从22到5。
在这种情况下,一天中可能有两个时间范围:22-24/0-5。这两个时间范围必须单独评估,因为其中可能会有DST更改。
但是也可以有一个时间范围,例如:22到0。对于一天,结果是22到24。
我在链接测试课程中介绍了这些案例以及许多其他案例。我花了一些时间才能将其用于所有测试用例。所以我希望它能为某人服务。
答案 1 :(得分:2)
要了解此解决方案,您应该熟悉位字段和位掩码。如果没有,请考虑进行研究。
这是一个简单的例子:
Bit field: 1 0 1 1 1 0 AND
Bit mask : 1 1 0 1 0 1
______________________
Result : 1 0 0 1 0 0
通过这种方法,您可以将一天中的实际小时显示为位字段,将所需的小时显示为位掩码。
我们从下午22点到凌晨5点的24位掩码(24小时)将如下所示(最后MSB):
23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1
以十六进制表示0xC0001F
。
在计算某些范围内的日历天数与小时数时,我的最后一种方法存在问题。原来,我不得不将问题简化为矩阵以识别模式。
CD\HR <24 =24 >24 <48 =48 >48 <72 =72 >72 <96 =96 >96
0 x
1 x x x x
2 x x x x x
3 x x x x x
4 x x x x
CD = Calendar Days => 2017-01-01 23:00 until 2017-01-02 00:00 is one CD
HR = Hour Range
目标是为bitField
和startDate
创建一个endDate
。对于隔日,我们只需将24个bitMask
中的位数相加即可。 bitField
始终是从startTime
到endTime
的范围。如果startTime
> endTime
,则我们将它们和计算出的bitField
翻转。
三个条件决定在应用bitMask时是否需要忽略日历日:
calendarDays > 0
bitField != 0
startTime > endTime
这将计算24小时掩码范围内的小时数,并考虑可能的DST:
private double getHoursInRange(ZonedDateTime startDate, ZonedDateTime endDate, int bitMask) {
if (!startDate.isBefore(endDate)) throw new InputMismatchException("endDate is before or equal startDate");
ZoneRules rules = startDate.getZone().getRules();
int offset = 0;
ZoneOffsetTransition zoneOffsetTransition = rules.nextTransition(startDate.toInstant());
while (true) {
Instant transitionInstant = zoneOffsetTransition.getInstant();
if (transitionInstant.isBefore(endDate.toInstant()) || transitionInstant.equals(endDate.toInstant())) {
ZoneOffset offsetAfter = zoneOffsetTransition.getOffsetAfter();
offset += offsetAfter.getTotalSeconds() == 3600 ? -1 : 1;
zoneOffsetTransition = rules.nextTransition(transitionInstant);
} else {
break;
}
}
long calendarDays = Duration.between(startDate.withHour(0), endDate.withHour(23)).toDays();
int startTime = startDate.getHour();
int endTime = endDate.getHour();
int bitField = 0;
for (int o = startTime < endTime ? startTime : endTime; startTime < endTime ? o < endTime : o < startTime; o++) {
bitField = bitField | (1 << o);
}
if (startTime > endTime) {
bitField = ~bitField;
}
if (calendarDays > 0 && bitField != 0 && startTime > endTime) {
calendarDays = calendarDays - 1;
}
double hoursInRange = calendarDays * Integer.bitCount(bitMask);
hoursInRange += Integer.bitCount(bitField & bitMask);
hoursInRange += offset;
return hoursInRange;
}
如果您还想检查几分钟,则可以添加以下行:
hoursInRange += (endDate.getMinute() - startDate.getMinute()) / 60.0;
或秒:
hoursInRange += (endDate.getSecond() - startDate.getSecond()) / 3600.0;
通过快速的单元测试进行了测试:
@Test
public void test() {
ZoneId london = ZoneId.of("Europe/London");
LocalDateTime startDate = LocalDateTime.of(2018, 1, 1, 13, 0, 0, 0);
LocalDateTime endDate = LocalDateTime.of(2018, 1, 5, 4, 0, 0, 0);
ZonedDateTime from = startDate.atZone(london);
ZonedDateTime to = endDate.atZone(london);
double hours = getHoursInRange(from, to, 0xC0001F);
double allHours = getHoursInRange(from, to, 0xFFFFFF);
assertEquals(27, hours, 0);
assertEquals(87, allHours, 0);
}
答案 2 :(得分:2)
我已经给了answer。但是已经给出的答案相当复杂。我认为它们并不容易理解。因此,我对这些答案不满意,并想知道是否可以找到一个简单易懂的解决方案。
我想,我找到了一个。方法是定义日期时间范围(如OP所述)并流式传输其单位并过滤适当的单位。
这是我的DateTimeRange
:
public class DateTimeRange {
private final ZonedDateTime from;
private final ZonedDateTime to;
DateTimeRange(ZonedDateTime from, ZonedDateTime to) {
this.from = from;
this.to = to;
}
public static DateTimeRange of(LocalDateTime from, LocalDateTime to, ZoneId zoneId) {
Objects.requireNonNull(from);
Objects.requireNonNull(to);
Objects.requireNonNull(zoneId);
return new DateTimeRange(ZonedDateTime.of(from, zoneId), ZonedDateTime.of(to, zoneId));
}
public Stream<ZonedDateTime> streamOn(ChronoUnit unit) {
Objects.requireNonNull(unit);
ZonedDateTimeSpliterator zonedDateTimeSpliterator = new ZonedDateTimeSpliterator(from, to, unit);
return StreamSupport.stream(zonedDateTimeSpliterator, false);
}
static class ZonedDateTimeSpliterator implements Spliterator<ZonedDateTime> {
private final ChronoUnit unit;
private ZonedDateTime current;
private ZonedDateTime to;
ZonedDateTimeSpliterator(ZonedDateTime from, ZonedDateTime to, ChronoUnit unit) {
this.current = from.truncatedTo(unit);
this.to = to.truncatedTo(unit);
this.unit = unit;
}
@Override
public boolean tryAdvance(Consumer<? super ZonedDateTime> action) {
boolean canAdvance = current.isBefore(to);
if (canAdvance) {
action.accept(current);
current = current.plus(1, unit);
}
return canAdvance;
}
@Override
public Spliterator<ZonedDateTime> trySplit() {
long halfSize = estimateSize() / 2;
if (halfSize == 0) {
return null;
}
ZonedDateTime splittedFrom = current.plus(halfSize, unit);
ZonedDateTime splittedTo = to;
to = splittedFrom;
return new ZonedDateTimeSpliterator(splittedFrom, splittedTo, unit);
}
@Override
public long estimateSize() {
return unit.between(current, to);
}
@Override
public Comparator<? super ZonedDateTime> getComparator() {
// sorted in natural order
return null;
}
@Override
public int characteristics() {
return Spliterator.NONNULL | Spliterator.IMMUTABLE | Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED | Spliterator.SORTED | Spliterator.DISTINCT;
}
}
}
这是经过改编的Durations
类:
public class Durations {
public static Duration getSumOfHoursOnDays(ZoneId zoneId, LocalDateTime dateTimeFrom, LocalDateTime dateTimeTo, LocalTime dailyTimeFrom,
LocalTime dailyTimeTo) {
return getDuration(zoneId, dateTimeFrom, dateTimeTo, dailyTimeFrom, dailyTimeTo, ChronoUnit.HOURS);
}
public static Duration getDuration(ZoneId zoneId, LocalDateTime dateTimeFrom, LocalDateTime dateTimeTo, LocalTime dailyTimeFrom,
LocalTime dailyTimeTo, ChronoUnit precision) {
long count = DateTimeRange.of(dateTimeFrom, dateTimeTo, zoneId)
.streamOn(precision)
.filter(getFilter(dailyTimeFrom, dailyTimeTo))
.count();
return Duration.of(count, precision);
}
protected static Predicate<? super ZonedDateTime> getFilter(LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
return dailyTimeFrom.isBefore(dailyTimeTo) ?
filterFromTo(dailyTimeFrom, dailyTimeTo) :
filterToFrom(dailyTimeFrom, dailyTimeTo);
}
protected static Predicate<? super ZonedDateTime> filterFromTo(LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
return zdt -> {
LocalTime time = zdt.toLocalTime();
return (time.equals(dailyTimeFrom) || time.isAfter(dailyTimeFrom)) && time.isBefore(dailyTimeTo);
};
}
protected static Predicate<? super ZonedDateTime> filterToFrom(LocalTime dailyTimeFrom, LocalTime dailyTimeTo) {
return zdt -> {
LocalTime time = zdt.toLocalTime();
return (time.equals(dailyTimeFrom) || time.isAfter(dailyTimeFrom)) || (time.isBefore(dailyTimeTo));
};
}
}
由于Stream
-接口是众所周知的,因此这种方法应该更容易理解。此外,将其与其他ChronoUnit
一起使用很简单。应用Stream
接口可以轻松计算其他基于日期时间的值。此外,这遵循Java 9 LocalDate.datesUntil
的示例。
虽然更容易理解此解决方案,但它不会像前面提到的两者一样快。我认为,只要多年来没有人能够以纳米精度流动,那是可以接受的;)
我的资源链接:
答案 3 :(得分:1)
Interval
类来表示和比较与时间轴相关的时间跨度。ZonedDateTime
中的分区时间和Instant
中的UTC时间之间切换。相同的时刻,不同的时钟时间。似乎其他答案正在尝试重新创建已经编写的类。有关为Java 8和更高版本中内置的 java.time 框架添加功能的类,请参见 ThreeTen-Extra 项目。
具体来说,我们可以使用Interval
–一对Instant
对象,以UTC为日期时间范围。此类提供了比较方法,例如overlaps
,intersection
等。
从两个输入(日期和时间)开始。将它们解析为LocalDateTime
,因为您忽略了提及时区。用T
代替中间的SPACE,以符合ISO 8601标准格式。
LocalDateTime ldtStart = LocalDateTime.parse( "2018-01-01T13:00:00" );
LocalDateTime ldtStop = LocalDateTime.parse( "2018-01-05T04:00:00" );
LocalDateTime
不能表示时刻,因为它缺少任何时区或UTC偏移量的概念。没有那种背景,它就没有真正的意义。您是要从印度加尔各答,法国巴黎或加拿大蒙特利尔的下午1点开始?那是三个非常不同的时刻。
因此,指定一个时区ZoneId
,以获得ZonedDateTime
。
以continent/region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用3-4个字母的缩写,例如EST
或IST
,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = ldtStart.atZone( z );
ZonedDateTime zdtStop = ldtStop.atZone( z );
使用org.threeten.extra.Interval
类表示与时间轴相关的时间跨度。此类表示一对Instant
对象。根据定义,Instant
采用UTC。我们正在按分区的日期时间进行工作。该怎么办?从我们的Instant
中提取一个ZonedDateTime
,以适应UTC。您可以在概念上将其视为由ZonedDateTime
和Instant
组成的ZoneId
。 Instant
和ZonedDateTime
代表相同的同时时刻,时间轴上的相同点。只有挂钟时间不同。
Interval interval = Interval.of( zdtStart.toInstant() , zdtStop.toInstant() );
您的参考系是在指定时区中感知的我们区间内的日期。因此,从每个LocalDate
中提取一个仅用于日期的值ZonedDateTime
。
LocalDate ldStart = zdtStart.toLocalDate();
LocalDate ldStop = zdtStop.toLocalDate();
我们将按日期循环显示,一次增加一天。因此,将该日期复制到一个递增变量中。
LocalDate localDate = ldStart;
指定您要定位的一天中的时间对。
LocalTime timeStart = LocalTime.of( 22 , 0 );
LocalTime timeStop = LocalTime.of( 5 , 0 );
设置Map
来存储我们的结果。我们将每个日期(在区域中看到的日期)映射到Interval
,这对Instant
对象代表我们从该日期开始的晚上10点到凌晨5点目标区域中的多少输入间隔。
long initialCapacity = ( ChronoUnit.DAYS.between( ldStart , ldtStop ) + 1 );
Map< LocalDate, Interval > dateToIntervalMap = new HashMap<>( ( int ) initialCapacity );
循环显示每个日期,直到输入间隔结束为止。
while ( ! localDate.isAfter( ldStop ) ) {
一个接一个地获取每个日期,并应用我们的目标时间值来确定我们时区中的时刻。
我不知道如何正确处理DST
ZonedDateTime
类的核心目的是处理在不同时间点在各个区域中发现的诸如DST之类的异常。 DST并不是唯一这样的异常。世界各地的政客都对重新定义时区表现出好奇。
当我们将时区应用于给定的日期和时间时,如果该日期在该时区中的那个时间无效(例如夏令时DST转换),则ZonedDateTime
类会根据需要自动调整。请务必阅读文档,以确保您了解并同意其调整逻辑。
ZonedDateTime zdtTargetStart = localDate.atTime( timeStart ).atZone( z );
ZonedDateTime zdtTargetStop = localDate.plusDays( 1 ).atTime( timeStop ).atZone( z );
间隔我们的目标时间范围,即目标Interval
。
Interval target = Interval.of( zdtTargetStart.toInstant() , zdtTargetStop.toInstant() );
测试以查看目标间隔是否与我们的输入间隔重叠。当然,根据我们对问题的定义,我们希望一开始就是这种情况。但是最后,在最后一个日期,情况可能并非如此。在最后一个日期,我们的输入间隔可能会在下一个目标日期发生之前结束。确实,这正是我们在课题中提供的输入所看到的(请参见下面的输出)。
使用intersection
方法产生一个Interval
,该 Interval intersection;
if ( interval.overlaps( target ) ) {
intersection = interval.intersection( target );
} else {
ZonedDateTime emptyInterval = localDate.atTime( timeStart ).atZone( z ); // Better than NULL I suppose.
intersection = Interval.of( emptyInterval.toInstant() , emptyInterval.toInstant() );
}
表示目标间隔和输入间隔之间的公共时间跨度。
dateToIntervalMap.put( localDate , intersection );
// Setup the next loop.
localDate = localDate.plusDays( 1 );
}
将得到的相交间隔存储在我们的地图中,并分配给我们循环的日期。
// Report
System.out.println( "interval: " + interval + " = " + zdtStart + "/" + zdtStop );
int nthDate = 0;
完成业务逻辑。现在我们可以报告结果了。
java.time.Duration
我们使用Duration totalDuration = Duration.ZERO;
类来跟踪每个路口间隔中包含的经过时间。
Map::keySet
我们必须完成一些额外的工作才能按时间顺序设置报告循环。 List< LocalDate > dates = new ArrayList<>( dateToIntervalMap.keySet() );
Collections.sort( dates );
List< LocalDate > keys = List.copyOf( dates );
for ( LocalDate date : keys ) {
nthDate++;
Interval i = dateToIntervalMap.get( date );
Instant startInstant = i.getStart();
Instant stopInstant = i.getEnd();
Duration d = Duration.between( startInstant , stopInstant );
totalDuration = totalDuration.plus( d );
ZonedDateTime start = startInstant.atZone( z );
ZonedDateTime stop = stopInstant.atZone( z );
System.out.println( "Day # " + nthDate + " = " + date + " ➙ " + i + " = " + start + "/" + stop + " = " + d );
}
方法不一定按我们想要的顺序返回结果。
String
报告我们所有路口间隔中包含的总时间。我们的Duration
对象生成的P
使用标准ISO 8601 duration format。 T
标志着开始,而System.out.println("Total duration: " + totalDuration);
则将任何年月日与任何时分秒分开。
LocalDateTime ldtStart = LocalDateTime.parse( "2018-01-01T13:00:00" );
LocalDateTime ldtStop = LocalDateTime.parse( "2018-01-05T04:00:00" );
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = ldtStart.atZone( z );
ZonedDateTime zdtStop = ldtStop.atZone( z );
Interval interval = Interval.of( zdtStart.toInstant() , zdtStop.toInstant() );
LocalDate ldStart = zdtStart.toLocalDate();
LocalDate ldStop = zdtStop.toLocalDate();
LocalDate localDate = ldStart;
LocalTime timeStart = LocalTime.of( 22 , 0 );
LocalTime timeStop = LocalTime.of( 5 , 0 );
long initialCapacity = ( ChronoUnit.DAYS.between( ldStart , ldtStop ) + 1 );
Map< LocalDate, Interval > dateToIntervalMap = new HashMap<>( ( int ) initialCapacity );
while ( ! localDate.isAfter( ldStop ) ) {
ZonedDateTime zdtTargetStart = localDate.atTime( timeStart ).atZone( z );
ZonedDateTime zdtTargetStop = localDate.plusDays( 1 ).atTime( timeStop ).atZone( z );
Interval target = Interval.of( zdtTargetStart.toInstant() , zdtTargetStop.toInstant() );
Interval intersection;
if ( interval.overlaps( target ) ) {
intersection = interval.intersection( target );
} else {
ZonedDateTime emptyInterval = localDate.atTime( timeStart ).atZone( z ); // Better than NULL I suppose.
intersection = Interval.of( emptyInterval.toInstant() , emptyInterval.toInstant() );
}
dateToIntervalMap.put( localDate , intersection );
// Setup the next loop.
localDate = localDate.plusDays( 1 );
}
// Report
System.out.println( "interval: " + interval + " = " + zdtStart + "/" + zdtStop );
int nthDate = 0;
Duration totalDuration = Duration.ZERO;
List< LocalDate > dates = new ArrayList<>( dateToIntervalMap.keySet() );
Collections.sort( dates );
List< LocalDate > keys = List.copyOf( dates );
for ( LocalDate date : keys ) {
nthDate++;
Interval i = dateToIntervalMap.get( date );
Instant startInstant = i.getStart();
Instant stopInstant = i.getEnd();
Duration d = Duration.between( startInstant , stopInstant );
totalDuration = totalDuration.plus( d );
ZonedDateTime start = startInstant.atZone( z );
ZonedDateTime stop = stopInstant.atZone( z );
System.out.println( "Day # " + nthDate + " = " + date + " ➙ " + i + " = " + start + "/" + stop + " = " + d );
}
System.out.println("Total duration: " + totalDuration);
为方便起见,让我们在一个块中再次查看相同的代码。
java.sql.*
运行时,我们得到此输出。我们:
时间间隔:2018-01-01T18:00:00Z / 2018-01-05T09:00:00Z = 2018-01-01T13:00-05:00 [美国/蒙特利尔] / 2018-01-05T04:00- 05:00 [美国/蒙特利尔]
第1天= 2018-01-01➙2018-01-02T03:00:00Z / 2018-01-02T10:00:00Z = 2018-01-01T22:00-05:00 [美国/蒙特利尔] / 2018-01-02T05:00-05:00 [美国/蒙特利尔] = PT7H
第2天= 2018-01-02➙2018-01-03T03:00:00Z / 2018-01-03T10:00:00Z = 2018-01-02T22:00-05:00 [美国/蒙特利尔] / 2018-01-03T05:00-05:00 [美国/蒙特利尔] = PT7H
第3天= 2018-01-03➙2018-01-04T03:00:00Z / 2018-01-04T10:00:00Z = 2018-01-03T22:00-05:00 [美国/蒙特利尔] / 2018-01-04T05:00-05:00 [美国/蒙特利尔] = PT7H
第4天= 2018-01-04➙2018-01-05T03:00:00Z / 2018-01-05T09:00:00Z = 2018-01-04T22:00-05:00 [美国/蒙特利尔] / 2018-01-05T04:00-05:00 [美国/蒙特利尔] = PT6H
第5天= 2018-01-05➙2018-01-06T03:00:00Z / 2018-01-06T03:00:00Z = 2018-01-05T22:00-05:00 [美国/蒙特利尔] / 2018-01-05T22:00-05:00 [美国/蒙特利尔] = PT0S
总持续时间:PT27H
java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
目前位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要{{1}}类。
在哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如Interval
,YearWeek
,YearQuarter
和more。
答案 4 :(得分:-1)
我尝试过:
LocalDateTime startDate = LocalDateTime.now().minusDays(1);
LocalDateTime endDate = LocalDateTime.now();
long numberOfHours = Duration.between(startDate, endDate).toHours();
并按预期工作
输出:24小时
或者您可以乔达,上面的代码将像这样:
DateTime startDate = new DateTime().minusDays(1);
DateTime endDate = new DateTime();
Hours hours = Hours.hoursBetween(startDate, endDate);
int numberOfHours = hours.getHours();