代码之家  ›  专栏  ›  技术社区  ›  Luiz

如何计算时间戳范围之间的小时和分钟总数(在特定范围内)

  •  3
  • Luiz  · 技术社区  · 8 年前

    用户可以按初始和结束日期/时间(时间戳)筛选报表假设电流滤波器:

    首字母: 2018-01-01 13:00:00

    结束: 2018-01-05 04:00:00

    如何在java中计算发生的小时和分钟总数 之间 22:00 05:00 AM (第二天的) 在筛选期间内的所有天。

    我们目前正在使用Java 8类(LocalDateTime等)。

    上述过滤器的预期结果:27小时零分钟 (而不是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。

    5 回复  |  直到 8 年前
        1
  •  2
  •   LuCio    7 年前

    我已经给了 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 S.应用 溪流 -接口使计算其他基于日期和时间的值变得容易。此外,下面是Java9的例子 LocalDate.datesUntil 是的。

    虽然更容易理解这个解决方案,但速度不会像前面提到的那样快。我认为,只要多年来没有人能达到纳米精度,这应该是可以接受的;)


    我的资源链接:

        2
  •  3
  •   LuCio    8 年前

    在错误地认为这个问题是重复的之后,@luiz纠正了我。我的道歉来了。

    计算 Duration 关于夏令时我在跟踪 answer . 守则还包括闰年。

    编辑:
    @西康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。

    我在链接测试类中讨论了这些和许多其他情况。我花了一些时间才使它适用于所有的测试用例。所以我希望它能为某个人服务。

        3
  •  2
  •   XiCoN JFS    8 年前

    为了理解这个解决方案,您应该熟悉位字段和位掩码如果没有,请考虑研究。

    下面是一个简单的例子:

    Bit field: 1 0 1 1 1 0  AND
    Bit mask : 1 1 0 1 0 1
    ______________________
    Result   : 1 0 0 1 0 0
    

    使用此方法,可以将一天中的实际小时显示为位字段,将所需小时显示为位掩码。

    我们的24位掩码(24小时)从晚上22点到早上5点将如下所示(msb last):

    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 是的。这个 位场 总是从 startTime endTime . 如果 开始时间 > 结束时间 然后我们翻转它们,计算出 位域 也。

    有三个条件决定对日历日应用位掩码时是否需要忽略它:

    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);
    }
    
        4
  •  1
  •   Basil Bourque    8 年前

    tl;博士

    • 不要工作太辛苦使用 Interval 在中找到类 额外310 表示和比较与时间线关联的时间跨度的项目。
    • 在分区时间之间切换 ZonedDateTime 和UTC时间 Instant 是的。同样的时刻,不同的挂钟时间。

    额外310 项目

    似乎其他的答案是试图重新创建已经编写好的类。见 额外310 为将功能添加到 java.time公司 Java 8及更高版本中内置的框架。

    具体来说,我们可以使用 间隔 一对 瞬间 对象,以UTC为单位的日期时间范围。此类提供比较方法,例如 overlaps , intersection ,等等。

    从你的两个输入开始,日期和时间。将这些解析为 LocalDateTime 因为你忘了提到时区。将中间的空格替换为 T 符合ISO 8601标准格式。

    LocalDateTime ldtStart = LocalDateTime.parse( "2018-01-01T13:00:00" );
    LocalDateTime ldtStop = LocalDateTime.parse( "2018-01-05T04:00:00" );
    

    一个 本地日期时间 表示一个时刻,因为它缺少时区或与UTC的偏移量的概念。没有这种背景,就没有真正意义你是说下午一点在加尔各答印度,法国巴黎,还是加拿大蒙特拉开始这是三个截然不同的时刻。

    所以指定一个时区,一个 ZoneId ,以获得 分区日期 是的。

    指定 proper time zone name 格式为 continent/region ,例如 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 类来表示与时间线关联的时间跨度这个类表示 瞬间 物体。一个 瞬间 根据定义,以UTC为单位我们在规定的约会时间工作。怎么办?通过提取 瞬间 从我们的 分区日期 . 你可以在概念上认为 分区日期 包括 瞬间 以及 佐尼 . 这个 瞬间 以及 分区日期 两者代表同一时刻,时间线上的同一点。只有挂钟的时间不同。

    Interval interval = Interval.of( zdtStart.toInstant() , zdtStop.toInstant() );
    

    你的参照系是我们在指定时区的时间间隔。所以提取 LocalDate ,仅日期值,从每个 分区日期 是的。

    LocalDate ldStart = zdtStart.toLocalDate();
    LocalDate ldStop = zdtStop.toLocalDate();
    

    我们将按日期循环,一次递增一天因此,将该日期复制到递增变量中。

    LocalDate localDate = ldStart;
    

    指定我们的目标时间对。

    LocalTime timeStart = LocalTime.of( 22 , 0 );
    LocalTime timeStop = LocalTime.of( 5 , 0 );
    

    建立一个 Map 来存储我们的结果。我们将每个日期(在我们的区域中看到的日期)映射到 间隔 ,一对 瞬间 表示从该日期开始的10 pm到5 am目标区域中有多少被我们的输入间隔覆盖的对象。

    long initialCapacity = ( ChronoUnit.DAYS.between( ldStart , ldtStop ) + 1 );
    Map< LocalDate, Interval > dateToIntervalMap = new HashMap<>( ( int ) initialCapacity );
    

    循环每个日期,直到输入间隔结束。

    while ( ! localDate.isAfter( ldStop ) ) {
    

    逐个记录每个日期,并应用我们的目标时间值来确定时区中的某个时刻。

    我不知道怎么处理DST

    其核心目的 分区日期 类是处理在不同时间点在不同区域中发现的异常(如DST)DST并不是唯一的异常情况;世界各地的政治家都表现出了重新定义时区的奇怪倾向。

    当我们对给定的日期和时间应用时区时,如果该区域中该日期的该时间无效(例如夏令时DST转换),则 分区日期 类根据需要自动调整。请务必阅读文档以确保您理解并同意其调整逻辑。

        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;
    

    我们使用 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 对象使用标准 ISO 8601 duration format 是的。这个 P 标志着开始,而 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);
    

    当运行时,我们得到这个输出我们:

    • 在大多数日子里要有整整七个小时
    • 倒数第二天降到6小时
    • 最后看到最后一天的零小时,因为我们的输入间隔在那天的开始时间之前结束。

    时间间隔: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.time 框架构建在Java8和更高版本中这些类取代了麻烦的旧类 legacy 日期时间类,如 java.util.Date , Calendar ,& SimpleDateFormat 是的。

    这个 Joda-Time 项目,现在在 maintenance mode ,建议迁移到 java.time 上课。

    要了解更多信息,请参阅 Oracle Tutorial . 和搜索堆栈溢出的许多例子和解释。规格是 JSR 310 是的。

    你可以交换 java.time公司 直接使用数据库的对象。使用 JDBC driver 符合 JDBC 4.2 或者以后不需要弦,不需要 java.sql.* 上课。

    在哪里获得java.time类?

    这个 ThreeTen-Extra project使用其他类扩展java.time这个项目是将来可能添加java.time的一个试验场。你可以在这里找到一些有用的类,比如 Interval 我是说, YearWeek 我是说, YearQuarter ,和 more .

        5
  •  -1
  •   sandy.narwal    8 年前

    我试过这个:

    LocalDateTime startDate = LocalDateTime.now().minusDays(1);
    LocalDateTime endDate = LocalDateTime.now();
    
    long numberOfHours = Duration.between(startDate, endDate).toHours();
    

    按预期工作

    输出:24小时

    或者你可以选择joda,上面的代码如下:

    DateTime startDate = new DateTime().minusDays(1);
    DateTime endDate = new DateTime();
    Hours hours = Hours.hoursBetween(startDate, endDate);
    int numberOfHours = hours.getHours();