ProfilePeriod implements Serializable { public static ProfilePeriod fromPeriodId(long periodId, long duration, TimeUnit units) { if(periodId < 0) { throw new IllegalArgumentException(format("period id must be >= 0; got '%d'", periodId)); } return new ProfilePeriod(periodId, duration, units); } ProfilePeriod(); private  ProfilePeriod(long periodId, long duration, TimeUnit units); static ProfilePeriod fromTimestamp(long epochMillis, long duration, TimeUnit units); static ProfilePeriod fromPeriodId(long periodId, long duration, TimeUnit units); long getStartTimeMillis(); long getEndTimeMillis(); ProfilePeriod next(); long getPeriod(); long getDurationMillis(); @Override boolean equals(Object o); @Override int hashCode(); @Override String toString(); static List<T> visitPeriods(long startEpochMillis
                                        , long endEpochMillis
                                        , long duration
                                        , TimeUnit units
                                        , Optional<Predicate<ProfilePeriod>> inclusionPredicate
                                        , Function<ProfilePeriod,T> transformation
                                        );  }
@Test public void testFromPeriodId() { ProfilePeriod expected = ProfilePeriod.fromTimestamp(AUG2016, 1, TimeUnit.HOURS); long periodId = expected.getPeriod(); long duration = expected.getDurationMillis(); ProfilePeriod actual = ProfilePeriod.fromPeriodId(periodId, duration, TimeUnit.MILLISECONDS); assertEquals(expected, actual); }
@Test public void testWithNegativePeriodId() { assertThrows( IllegalArgumentException.class, () -> ProfilePeriod.fromPeriodId(-1, 1, TimeUnit.HOURS) ); }