public static List<ZonedDateTime> findMissingBars(BarSeries barSeries, boolean findOnlyNaNBars) {
        List<Bar> bars = barSeries.getBarData();
        if (bars == null || bars.isEmpty())
            return new ArrayList<>();
        Duration duration = bars.iterator().next().getTimePeriod();
        List<ZonedDateTime> missingBars = new ArrayList<>();
        for (int i = 0; i < bars.size(); i++) {
            Bar bar = bars.get(i);
            if (!findOnlyNaNBars) {
                Bar nextBar = i + 1 < bars.size() ? bars.get(i + 1) : null;
                Duration incDuration = Duration.ZERO;
                if (nextBar != null) {
                    // market closing times are also treated as missing bars
                    while (nextBar.getBeginTime().minus(incDuration).isAfter(bar.getEndTime())) {
                        missingBars.add(bar.getEndTime().plus(incDuration).plus(duration));
                        incDuration = incDuration.plus(duration);
                    }
                }
            }
            boolean noFullData = bar.getOpenPrice().isNaN() || bar.getHighPrice().isNaN() || bar.getLowPrice().isNaN();
            if (noFullData) {
                missingBars.add(bar.getEndTime());
            }
        }
        return missingBars;
    }
@Test
    public void findMissingBarsTest() {

        final List<Bar> bars = new ArrayList<>();
        time = ZonedDateTime.of(2019, 6, 1, 1, 1, 0, 0, ZoneId.systemDefault());

        final Bar bar0 = new MockBar(time, 1d, 2d, 3d, 4d, 5d, 0d, 7, numFunction);
        final Bar bar1 = new MockBar(time.plusDays(1), 1d, 1d, 1d, 1d, 1d, 1d, 1, numFunction);
        final Bar bar4 = new MockBar(time.plusDays(4), 3d, 4d, 4d, 5d, 6d, 4d, 4, numFunction);
        final Bar bar5 = new MockBar(time.plusDays(5), 5d, 5d, 5d, 5d, 5d, 5d, 5, numFunction);
        final Bar bar7 = new MockBar(time.plusDays(7), 0, 0, 0, 0, 0, 0, 0, numFunction);
        Bar bar8 = BaseBar.builder(DoubleNum::valueOf, Double.class)
                .timePeriod(Duration.ofDays(1))
                .endTime(time.plusDays(8))
                .openPrice(NaN.NaN)
                .highPrice(NaN.NaN)
                .lowPrice(NaN.NaN)
                .closePrice(NaN.NaN)
                .volume(NaN.NaN)
                .build();

        bars.add(bar0);
        bars.add(bar1);
        bars.add(bar4);
        bars.add(bar5);
        bars.add(bar7);
        bars.add(bar8);

        series = new BaseBarSeriesBuilder().withNumTypeOf(numFunction).withName("Series Name").withBars(bars).build();

        // return the beginTime of each missing bar
        List<ZonedDateTime> missingBars = BarSeriesUtils.findMissingBars(series, false);

        // there must be 3 missing bars (bar2, bar3, bar6)
        assertEquals(missingBars.get(0), time.plusDays(2));
        assertEquals(missingBars.get(1), time.plusDays(3));
        assertEquals(missingBars.get(2), time.plusDays(6));
        // there must be 1 bar with invalid data (e.g. price, volume)
        assertEquals(missingBars.get(3), bar8.getEndTime());
    }