├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── ginsberg │ └── timestream │ ├── AbstractComparableStream.java │ ├── LocalDateStream.java │ ├── LocalDateTimeStream.java │ ├── TakeWhile.java │ ├── YearMonthStream.java │ └── ZonedDateTimeStream.java └── test └── java └── com └── ginsberg └── timestream ├── LocalDateStreamTest.java ├── LocalDateTimeStreamTest.java ├── TakeWhileTest.java ├── YearMonthStreamTest.java ├── ZonedDateTimeStreamTest.java └── util └── Assertions.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | *.iml 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Todd Ginsberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java-Timestream 2 | A set of builders that create streams of java.time objects. 3 | 4 | To use this library in your project, add this to your dependencies: 5 | 6 | ```xml 7 | 8 | com.ginsberg 9 | java-timestream 10 | 1.1.0 11 | 12 | ``` 13 | 14 | These are fully functional streams, so you can use them like any other stream, once created. 15 | 16 | The lack of a convenient `takeWhile(Predicate predicate)` method in Java 8 is what led to the creation 17 | of this library. Now that Java 9 has `Stream.takeWhile()` support, there is no reason to add this as a dependency. 18 | 19 | This library can create streams of the following java.time classes: 20 | 21 | + `LocalDate` via `LocalDateStream` 22 | + `LocalDateTime` via `LocalDateTimeStream` 23 | + `YearMonth` via `YearMonthStream` 24 | + `ZonedDateTime` via `ZoneDateTimeStream` 25 | 26 | And has support for... 27 | 28 | + Inclusive end point (`to`) 29 | + Exclusive end point (`until`) 30 | + Configurable period between stream elements (`every`) 31 | + Streams can move forward or backward through time 32 | + Infinite streams (by not providing an end point) 33 | 34 | ## Usage 35 | 36 | Every builder needs a non-null point in time to begin the stream. Therefore, each 37 | builder can be created using one of two methods: 38 | 39 | + `.fromNow()` - Assumes 'now' 40 | + `.from(T from)` - Type-specific starting point, provided by caller 41 | 42 | To set the optional point in time where the stream ends (inclusive) you can call one of two methods: 43 | 44 | + `.to(T to)` - Type-specific end point. Can be null to indicate forever 45 | + `.to(amount, units)` - Where `amount` is a positive integer (for forward through time) or a negative integer (for backward through time), and `unit` is a valid `ChronoUnit` 46 | 47 | To make the optional end of the stream exclusive, you can call one of two methods: 48 | 49 | + `.until(T to)` - Type-specific end point. Can be null to indicate forever 50 | + `.until(amount, units)` - Where `amount` is a positive integer (for forward through time) or a negative integer (for backward through time), and `unit` is a valid `ChronoUnit` 51 | 52 | To indicate how much time should be skipped in each iteration: 53 | 54 | + `.every(amount, units)` - Where `amount` is an integer representing the number of units, and `unit` is a valid `ChronoUnit` 55 | + `.every(period)` - Where `period` is a valid `Period` object. (Supported on `LocalDateStream` and `YearMonthStream` only). 56 | + `.every(duration)` - Where `duration` is a valid `Duration` object. (Supported on everything other than `LocalDateStream` and `YearMonthStream`). 57 | 58 | 59 | Note that providing an end time (via `to` or `until`) is optional. In that case, the stream will 60 | have no end and should produce values until you stop it. 61 | 62 | ## Examples 63 | 64 | Create a stream of `LocalDateTime` objects, between now and hour from now, every two minutes: 65 | 66 | ```java 67 | final Stream stream = LocalDateTimeStream 68 | .fromNow() 69 | .to(1, ChronoUnit.HOURS) 70 | .every(2, ChronoUnit.MINUTES) 71 | .stream(); 72 | ``` 73 | 74 | Or (equivalent): 75 | 76 | ```java 77 | final Stream stream = LocalDateTimeStream 78 | .from(LocalDateTime.now()) 79 | .to(LocalDateTime.now().plusHours(1)) 80 | .every(2, ChronoUnit.MINUTES) 81 | .stream(); 82 | ``` 83 | 84 | Create a stream of `YearMonth` objects from today, to a year ago (backward through time), stopping before the end (exclusive), 85 | every month: 86 | 87 | ```java 88 | final Stream stream = YearMonthStream 89 | .fromNow() 90 | .until(-12, ChronoUnit.MONTHS) 91 | .every(1, ChronoUnit.MONTHS) // This is the default 92 | .stream(); 93 | ``` 94 | 95 | Replace this code that does something with every minute of time over the last hour, going backwards: 96 | 97 | ```java 98 | final LocalDateTime end = LocalDateTime.now().minusHours(1); 99 | LocalDateTime when = LocalDateTime.now(); 100 | 101 | while(when.isAfter(end)) { 102 | doSomething(when); 103 | when = when.minusMinutes(1); 104 | } 105 | ``` 106 | 107 | ... with this: 108 | 109 | ```java 110 | LocalDateTimeStream 111 | .fromNow() 112 | .until(-1, ChronoUnit.HOURS) 113 | .every(1, ChronoUnit.MINUTES) 114 | .stream() 115 | .forEach(this::doSomething); 116 | ``` 117 | 118 | It's not less code, but it certainly makes it easier to understand. 119 | 120 | There are also plenty of examples in the unit tests. 121 | 122 | ## Contributing and Issues 123 | 124 | Please feel free to file issues for change requests or bugs. If you would like to contribute new functionality, please contact me first! 125 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 24 | 25 | 27 | 28 | 4.0.0 29 | java-timestream 30 | com.ginsberg 31 | java-timestream 32 | A set of builders that create streams of java.time objects 33 | https://github.com/tginsberg/java-timestream 34 | 1.1.1-SNAPSHOT 35 | jar 36 | 37 | 38 | https://github.com/tginsberg/java-timestream/issues 39 | GitHub Issues 40 | 41 | 42 | 43 | https://github.com/tginsberg/java-timestream 44 | scm:git:git://github.com/tginsberg/java-timestream.git 45 | scm:git:git@github.com:tginsberg/java-timestream.git 46 | HEAD 47 | 48 | 49 | 50 | 51 | Todd Ginsberg 52 | todd@ginsberg.com 53 | https://github.com/tginsberg/ 54 | tginsberg 55 | 56 | 57 | 58 | 59 | 60 | MIT License 61 | http://www.opensource.org/licenses/mit-license.php 62 | repo 63 | 64 | 65 | 66 | 67 | 1.8 68 | UTF-8 69 | 70 | 71 | 72 | 73 | ossrh 74 | https://oss.sonatype.org/content/repositories/snapshots 75 | 76 | 77 | ossrh 78 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-compiler-plugin 87 | 3.5.1 88 | 89 | ${java.version} 90 | ${java.version} 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-release-plugin 97 | 2.5.3 98 | 99 | true 100 | false 101 | release 102 | deploy 103 | 104 | 105 | 106 | 107 | org.sonatype.plugins 108 | nexus-staging-maven-plugin 109 | 1.6.7 110 | true 111 | 112 | ossrh 113 | https://oss.sonatype.org/ 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | release 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-source-plugin 129 | 3.0.1 130 | 131 | 132 | attach-sources 133 | 134 | jar-no-fork 135 | 136 | 137 | 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-javadoc-plugin 143 | 2.10.4 144 | 145 | 146 | attach-javadocs 147 | 148 | jar 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-gpg-plugin 157 | 1.6 158 | 159 | 160 | sign-artifacts 161 | verify 162 | 163 | sign 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | org.assertj 178 | assertj-core 179 | 3.4.1 180 | test 181 | 182 | 183 | junit 184 | junit 185 | 4.12 186 | test 187 | 188 | 189 | 190 | 191 | 192 | 193 | org.apache.maven.plugins 194 | maven-javadoc-plugin 195 | 2.10.4 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /src/main/java/com/ginsberg/timestream/AbstractComparableStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import java.util.Objects; 28 | import java.util.function.Predicate; 29 | import java.util.function.UnaryOperator; 30 | import java.util.stream.Stream; 31 | import java.util.stream.StreamSupport; 32 | 33 | import static java.util.stream.Stream.iterate; 34 | 35 | /** 36 | * Provides generic support for open and closed-ended ranges of objects 37 | * that can be compared. 38 | * 39 | * @param A type that implements `Comparable`. 40 | * @author Todd Ginsberg (todd@ginsberg.com) 41 | */ 42 | abstract class AbstractComparableStream> { 43 | private final T from; 44 | private T to; 45 | private boolean closedRange = false; 46 | 47 | AbstractComparableStream(final T from) { 48 | Objects.requireNonNull(from); 49 | this.from = from; 50 | } 51 | 52 | void setTo(final T to) { 53 | this.to = to; 54 | this.closedRange = false; 55 | } 56 | 57 | void setUntil(final T until) { 58 | this.to = until; 59 | this.closedRange = true; 60 | } 61 | 62 | boolean isForward() { 63 | return to == null || from.compareTo(to) <= 0; 64 | } 65 | 66 | T getFrom() { 67 | return from; 68 | } 69 | 70 | /** 71 | * Provide an operator that returns the next value in the series. 72 | * @return A non-null UnaryOperator 73 | */ 74 | abstract UnaryOperator next(); 75 | 76 | /** 77 | * Produce a stream between the dates given, skipping 78 | * by the amount specified. 79 | * 80 | * @return A non-null stream of time/date. 81 | */ 82 | public Stream stream() { 83 | return StreamSupport.stream( 84 | TakeWhile.of( 85 | iterate(from, next()).spliterator(), 86 | canTake()), 87 | false); 88 | 89 | } 90 | 91 | /** 92 | * Method that generates a predicate that is used by the Spliterator to 93 | * implement a limitation on the stream. We can take the next value in 94 | * the stream if one of the following are true: 95 | * 96 | * 1) There is no bound to the stream (to is null). 97 | * 2) We are before the end when we are moving forward through time. 98 | * 3) We are after the end when we are moving backward through time. 99 | * 4) The current time equals the end and we are not a closed range. 100 | * 101 | * @return A Predicate 102 | */ 103 | private Predicate canTake() { 104 | return x -> { 105 | if (to == null) { 106 | return true; 107 | } else { 108 | final int compare = x.compareTo(to); 109 | if (compare < 0) { 110 | return isForward(); 111 | } else if (compare > 0) { 112 | return !isForward(); 113 | } else { 114 | return !closedRange; 115 | } 116 | } 117 | }; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/ginsberg/timestream/LocalDateStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import java.time.LocalDate; 28 | import java.time.Period; 29 | import java.time.temporal.ChronoUnit; 30 | import java.util.Objects; 31 | import java.util.function.UnaryOperator; 32 | 33 | /** 34 | * A builder that creates a stream of LocalDate objects. 35 | *

36 | *

 37 |  * {@code
 38 |  * // Print all of the LocalDates between now and a year from now, every other day.
 39 |  * LocalDateStream
 40 |  *     .fromNow()
 41 |  *     .to(1, ChronoUnit.YEARS)
 42 |  *     .every(2, ChronoUnit.DAYS)
 43 |  *     .stream()
 44 |  *     .forEach(System.out::println);
 45 |  * }
 46 |  * 
47 | * 48 | * @author Todd Ginsberg (todd@ginsberg.com) 49 | */ 50 | public class LocalDateStream extends AbstractComparableStream { 51 | private long amount = 1; 52 | private ChronoUnit unit = ChronoUnit.DAYS; 53 | 54 | private LocalDateStream(final LocalDate from) { 55 | super(from); 56 | } 57 | 58 | /** 59 | * Create a LocalDateStream, starting at LocalDate.now(). 60 | * 61 | * @return A non-null LocalDateStream. 62 | */ 63 | public static LocalDateStream fromNow() { 64 | return new LocalDateStream(LocalDate.now()); 65 | } 66 | 67 | /** 68 | * Create a LocalDateStream, starting at the given LocalDate. 69 | * 70 | * @param from A non-null LocalDate to begin the stream with. 71 | * @return A non-null LocalDateStream. 72 | */ 73 | public static LocalDateStream from(final LocalDate from) { 74 | return new LocalDateStream(from); 75 | } 76 | 77 | /** 78 | * Set the inclusive end point of the stream, using an absolute LocalDate. 79 | * 80 | * @param to A nullable LocalDate to end the stream with (null means infinite). 81 | * @return A non-null LocalDateStream. 82 | */ 83 | public LocalDateStream to(final LocalDate to) { 84 | setTo(to); 85 | return this; 86 | } 87 | 88 | /** 89 | * Set the inclusive end point of the stream, using a relative duration. 90 | * 91 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 92 | * @param unit The non-null unit the amount is denominated in. May not be null. 93 | * @return A non-null LocalDateStream. 94 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 95 | * @see ChronoUnit 96 | */ 97 | public LocalDateStream to(int amount, 98 | final ChronoUnit unit) { 99 | Objects.requireNonNull(unit); 100 | setTo(getFrom().plus(amount, unit)); 101 | return this; 102 | } 103 | 104 | /** 105 | * Set the exclusive end point of the stream, using an absolute LocalDate. 106 | * 107 | * @param until A nullable LocalDate to end the stream before (null means infinite). 108 | * @return A non-null LocalDateStream. 109 | */ 110 | public LocalDateStream until(final LocalDate until) { 111 | setUntil(until); 112 | return this; 113 | } 114 | 115 | /** 116 | * Set the exclusive end point of the stream, using a relative duration. 117 | * 118 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 119 | * @param unit The non-null unit the amount is denominated in. 120 | * @return A non-null LocalDateStream. 121 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 122 | * @see ChronoUnit 123 | */ 124 | public LocalDateStream until(int amount, 125 | final ChronoUnit unit) { 126 | Objects.requireNonNull(unit); 127 | setUntil(getFrom().plus(amount, unit)); 128 | return this; 129 | } 130 | 131 | /** 132 | * Set the duration between successive elements produced by the stream. The default 133 | * for this builder is 1 Day. 134 | * 135 | * @param amount The number of units to use when calculating the next element of the stream. 136 | * @param unit The non-null unit the amount is denominated in. 137 | * @return A non-null LocalDateStream. 138 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 139 | * @see ChronoUnit 140 | */ 141 | public LocalDateStream every(int amount, 142 | final ChronoUnit unit) { 143 | Objects.requireNonNull(unit); 144 | this.amount = Math.abs(amount); 145 | this.unit = unit; 146 | LocalDate.now().plus(0, unit); // Fail fast test 147 | if (this.amount == 0) { 148 | throw new IllegalArgumentException("Amount must be non-zero"); 149 | } 150 | return this; 151 | } 152 | 153 | /** 154 | * Set the duration between successive elements produced by the stream. The default 155 | * for this builder is 1 Day. 156 | * 157 | * @param period The interval to use when calculating the next element of the stream. 158 | * @return A non-null LocalDateStream. 159 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the period is not supported. 160 | * @see ChronoUnit 161 | */ 162 | public LocalDateStream every(final Period period) { 163 | Objects.requireNonNull(period); 164 | this.unit = ChronoUnit.DAYS; 165 | this.amount = Math.abs(period.get(this.unit)); 166 | LocalDate.now().plus(0, unit); // Fail fast test 167 | if (this.amount == 0) { 168 | throw new IllegalArgumentException("Effective amount must be non-zero (Period resolves to zero duration)"); 169 | } 170 | return this; 171 | } 172 | 173 | @Override 174 | UnaryOperator next() { 175 | return date -> date.plus(isForward() ? amount : 0 - amount, unit); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/com/ginsberg/timestream/LocalDateTimeStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import java.time.Duration; 28 | import java.time.LocalDateTime; 29 | import java.time.temporal.ChronoUnit; 30 | import java.util.Objects; 31 | import java.util.function.UnaryOperator; 32 | 33 | /** 34 | * A builder that creates a stream of LocalDateTime objects. 35 | *

36 | *

 37 |  * {@code
 38 |  * // Print all of the LocalDateTimes between now and a day from now, every other minute.
 39 |  * LocalDateTimeStream
 40 |  *     .fromNow()
 41 |  *     .to(1, ChronoUnit.DAYS)
 42 |  *     .every(2, ChronoUnit.MINUTES)
 43 |  *     .stream()
 44 |  *     .forEach(System.out::println);
 45 |  * }
 46 |  * 
47 | * 48 | * @author Todd Ginsberg (todd@ginsberg.com) 49 | */ 50 | public class LocalDateTimeStream extends AbstractComparableStream { 51 | private long amount = 1; 52 | private ChronoUnit unit = ChronoUnit.SECONDS; 53 | 54 | private LocalDateTimeStream(final LocalDateTime from) { 55 | super(from); 56 | } 57 | 58 | /** 59 | * Create a LocalDateTimeStream, starting at LocalDateTime.now(). 60 | * 61 | * @return A non-null LocalDateTimeStream. 62 | */ 63 | public static LocalDateTimeStream fromNow() { 64 | return new LocalDateTimeStream(LocalDateTime.now()); 65 | } 66 | 67 | /** 68 | * Create a LocalDateTimeStream, starting at the given LocalDateTime. 69 | * 70 | * @param from A non-null LocalDateTime to begin the stream with. 71 | * @return A non-null LocalDateTimeStream. 72 | */ 73 | public static LocalDateTimeStream from(final LocalDateTime from) { 74 | return new LocalDateTimeStream(from); 75 | } 76 | 77 | /** 78 | * Set the inclusive end point of the stream, using an absolute LocalDateTime. 79 | * 80 | * @param to A nullable LocalDateTime to end the stream with (null means infinite). 81 | * @return A non-null LocalDateTimeStream. 82 | */ 83 | public LocalDateTimeStream to(final LocalDateTime to) { 84 | setTo(to); 85 | return this; 86 | } 87 | 88 | /** 89 | * Set the inclusive end point of the stream, using a relative duration. 90 | * 91 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 92 | * @param unit The non-null unit the amount is denominated in. May not be null. 93 | * @return A non-null LocalDateTimeStream. 94 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 95 | * @see ChronoUnit 96 | */ 97 | public LocalDateTimeStream to(int amount, 98 | final ChronoUnit unit) { 99 | Objects.requireNonNull(unit); 100 | setTo(getFrom().plus(amount, unit)); 101 | return this; 102 | } 103 | 104 | /** 105 | * Set the exclusive end point of the stream, using an absolute LocalDateTime. 106 | * 107 | * @param until A nullable LocalDateTime to end the stream before (null means infinite). 108 | * @return A non-null LocalDateTimeStream. 109 | */ 110 | public LocalDateTimeStream until(final LocalDateTime until) { 111 | setUntil(until); 112 | return this; 113 | } 114 | 115 | /** 116 | * Set the exclusive end point of the stream, using a relative duration. 117 | * 118 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 119 | * @param unit The non-null unit the amount is denominated in. 120 | * @return A non-null LocalDateTimeStream. 121 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 122 | * @see ChronoUnit 123 | */ 124 | public LocalDateTimeStream until(int amount, 125 | final ChronoUnit unit) { 126 | Objects.requireNonNull(unit); 127 | setUntil(getFrom().plus(amount, unit)); 128 | return this; 129 | } 130 | 131 | /** 132 | * Set the duration between successive elements produced by the stream. The default 133 | * for this builder is 1 Second. 134 | * 135 | * @param amount The number of units to use when calculating the next element of the stream. 136 | * @param unit The non-null unit the amount is denominated in. 137 | * @return A non-null LocalDateTimeStream. 138 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 139 | * @see ChronoUnit 140 | */ 141 | public LocalDateTimeStream every(int amount, 142 | final ChronoUnit unit) { 143 | Objects.requireNonNull(unit); 144 | this.amount = Math.abs(amount); 145 | this.unit = unit; 146 | if (this.amount == 0) { 147 | throw new IllegalArgumentException("Amount must be non-zero"); 148 | } 149 | return this; 150 | } 151 | 152 | /** 153 | * Set the duration between successive elements produced by the stream. The default 154 | * for this builder is 1 Second. 155 | * 156 | * @param duration The interval to use when calculating the next element of the stream. 157 | * @return A non-null LocalDateTimeStream. 158 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 159 | * @see ChronoUnit 160 | */ 161 | public LocalDateTimeStream every(final Duration duration) { 162 | Objects.requireNonNull(unit); 163 | this.unit = ChronoUnit.SECONDS; 164 | this.amount = Math.abs(duration.get(this.unit)); 165 | if (this.amount == 0) { 166 | throw new IllegalArgumentException("Effective amount must be non-zero (Duration resolves to zero duration)"); 167 | } 168 | return this; 169 | } 170 | 171 | @Override 172 | UnaryOperator next() { 173 | return date -> date.plus(isForward() ? amount : 0 - amount, unit); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/com/ginsberg/timestream/TakeWhile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import java.util.Spliterator; 28 | import java.util.Spliterators; 29 | import java.util.function.Consumer; 30 | import java.util.function.Predicate; 31 | 32 | /** 33 | * Implementation of TakeWhile, which makes most of this library possible. 34 | * 35 | * This behavior (along with the complementary dropWhile) is coming 36 | * in JDK 9, but since I need it now, here it is. 37 | * 38 | * @param Type that the predicate and spliterator handle. 39 | * @see java.util.Spliterators.AbstractSpliterator 40 | * @author Todd Ginsberg (todd@ginsberg.com) 41 | */ 42 | public class TakeWhile extends Spliterators.AbstractSpliterator { 43 | 44 | private final Spliterator spliterator; 45 | private final Predicate predicate; 46 | private boolean hasEnded = false; 47 | 48 | public static TakeWhile of(final Spliterator spliterator, 49 | final Predicate predicate) { 50 | return new TakeWhile<>(spliterator, predicate); 51 | } 52 | 53 | private TakeWhile(final Spliterator spliterator, 54 | final Predicate predicate) { 55 | super(spliterator.estimateSize(), 0); 56 | this.spliterator = spliterator; 57 | this.predicate = predicate; 58 | } 59 | 60 | @Override 61 | public boolean tryAdvance(final Consumer consumer) { 62 | if (!hasEnded) { 63 | final boolean hadNext = spliterator.tryAdvance(e -> { 64 | if (predicate.test(e)) { 65 | consumer.accept(e); 66 | } else { 67 | hasEnded = true; 68 | } 69 | }); 70 | return hadNext && !hasEnded; 71 | } 72 | return false; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/ginsberg/timestream/YearMonthStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import java.time.Period; 28 | import java.time.YearMonth; 29 | import java.time.temporal.ChronoUnit; 30 | import java.util.Objects; 31 | import java.util.function.UnaryOperator; 32 | 33 | /** 34 | * A builder that creates a stream of YearMonth objects. 35 | *

36 | *

 37 |  * {@code
 38 |  * // Print all of the YearMonths between now and a year from now, every other month.
 39 |  * YearMonthStream
 40 |  *     .fromNow()
 41 |  *     .to(1, ChronoUnit.YEARS)
 42 |  *     .every(2, ChronoUnit.MONTHS)
 43 |  *     .stream()
 44 |  *     .forEach(System.out::println);
 45 |  * }
 46 |  * 
47 | * 48 | * @author Todd Ginsberg (todd@ginsberg.com) 49 | */ 50 | public class YearMonthStream extends AbstractComparableStream { 51 | private long amount = 1; 52 | private ChronoUnit unit = ChronoUnit.MONTHS; 53 | 54 | private YearMonthStream(final YearMonth from) { 55 | super(from); 56 | } 57 | 58 | /** 59 | * Create a YearMonthStream, starting at YearMonth.now(). 60 | * 61 | * @return A non-null YearMonthStream. 62 | */ 63 | public static YearMonthStream fromNow() { 64 | return new YearMonthStream(YearMonth.now()); 65 | } 66 | 67 | /** 68 | * Create a YearMonthStream, starting at the given YearMonth. 69 | * 70 | * @param from A non-null YearMonth to begin the stream with. 71 | * @return A non-null YearMonthStream. 72 | */ 73 | public static YearMonthStream from(final YearMonth from) { 74 | return new YearMonthStream(from); 75 | } 76 | 77 | /** 78 | * Set the inclusive end point of the stream, using an absolute YearMonth. 79 | * 80 | * @param to A nullable YearMonth to end the stream with (null means infinite). 81 | * @return A non-null YearMonthStream. 82 | */ 83 | public YearMonthStream to(final YearMonth to) { 84 | setTo(to); 85 | return this; 86 | } 87 | 88 | /** 89 | * Set the inclusive end point of the stream, using a relative duration. 90 | * 91 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 92 | * @param unit The non-null unit the amount is denominated in. May not be null. 93 | * @return A non-null YearMonthStream. 94 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 95 | * @see ChronoUnit 96 | */ 97 | public YearMonthStream to(int amount, 98 | final ChronoUnit unit) { 99 | Objects.requireNonNull(unit); 100 | setTo(getFrom().plus(amount, unit)); 101 | return this; 102 | } 103 | 104 | /** 105 | * Set the exclusive end point of the stream, using an absolute YearMonth. 106 | * 107 | * @param until A nullable YearMonth to end the stream before (null means infinite). 108 | * @return A non-null YearMonthStream. 109 | */ 110 | public YearMonthStream until(final YearMonth until) { 111 | setUntil(until); 112 | return this; 113 | } 114 | 115 | /** 116 | * Set the exclusive end point of the stream, using a relative duration. 117 | * 118 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 119 | * @param unit The non-null unit the amount is denominated in. 120 | * @return A non-null YearMonthStream. 121 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 122 | * @see ChronoUnit 123 | */ 124 | public YearMonthStream until(int amount, 125 | final ChronoUnit unit) { 126 | Objects.requireNonNull(unit); 127 | setUntil(getFrom().plus(amount, unit)); 128 | return this; 129 | } 130 | 131 | /** 132 | * Set the duration between successive elements produced by the stream. The default 133 | * for this builder is 1 Month. 134 | * 135 | * @param amount The number of units to use when calculating the next element of the stream. 136 | * @param unit The non-null unit the amount is denominated in. 137 | * @return A non-null YearMonthStream. 138 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 139 | * @see ChronoUnit 140 | */ 141 | public YearMonthStream every(int amount, 142 | final ChronoUnit unit) { 143 | Objects.requireNonNull(unit); 144 | this.amount = Math.abs(amount); 145 | this.unit = unit; 146 | YearMonth.now().plus(0, unit); // Fail fast test 147 | if (this.amount == 0) { 148 | throw new IllegalArgumentException("Amount must be non-zero"); 149 | } 150 | return this; 151 | } 152 | 153 | /** 154 | * Set the duration between successive elements produced by the stream. The default 155 | * for this builder is 1 Month. 156 | * 157 | * @return A non-null YearMonthStream. 158 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 159 | * @see ChronoUnit 160 | */ 161 | public YearMonthStream every(final Period period) { 162 | Objects.requireNonNull(unit); 163 | this.unit = ChronoUnit.MONTHS; 164 | this.amount = Math.abs(period.get(this.unit)); 165 | YearMonth.now().plus(0, unit); // Fail fast test 166 | if (this.amount == 0) { 167 | throw new IllegalArgumentException("Effective amount must be non-zero (Period resolves to zero duration)"); 168 | } 169 | return this; 170 | } 171 | 172 | @Override 173 | UnaryOperator next() { 174 | return date -> date.plus(isForward() ? amount : 0 - amount, unit); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/ginsberg/timestream/ZonedDateTimeStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import java.time.Duration; 28 | import java.time.ZonedDateTime; 29 | import java.time.temporal.ChronoUnit; 30 | import java.util.Objects; 31 | import java.util.function.UnaryOperator; 32 | 33 | /** 34 | * A builder that creates a stream of ZonedDateTime objects. 35 | *

36 | *

 37 |  * {@code
 38 |  * // Print all of the ZonedDateTimes between now and a day from now, every other minute.
 39 |  * ZonedDateTimeStream
 40 |  *     .fromNow()
 41 |  *     .to(1, ChronoUnit.DAYS)
 42 |  *     .every(2, ChronoUnit.MINUTES)
 43 |  *     .stream()
 44 |  *     .forEach(System.out::println);
 45 |  * }
 46 |  * 
47 | * 48 | * @author Todd Ginsberg (todd@ginsberg.com) 49 | */ 50 | public class ZonedDateTimeStream extends AbstractComparableStream { 51 | private long amount = 1; 52 | private ChronoUnit unit = ChronoUnit.SECONDS; 53 | 54 | private ZonedDateTimeStream(final ZonedDateTime from) { 55 | super(from); 56 | } 57 | 58 | /** 59 | * Create a ZonedDateTimeStream, starting at ZonedDateTime.now(). 60 | * 61 | * @return A non-null ZonedDateTimeStream. 62 | */ 63 | public static ZonedDateTimeStream fromNow() { 64 | return new ZonedDateTimeStream(ZonedDateTime.now()); 65 | } 66 | 67 | /** 68 | * Create a ZonedDateTimeStream, starting at the given ZonedDateTime. 69 | * 70 | * @param from A non-null ZonedDateTime to begin the stream with. 71 | * @return A non-null ZonedDateTimeStream. 72 | */ 73 | public static ZonedDateTimeStream from(final ZonedDateTime from) { 74 | return new ZonedDateTimeStream(from); 75 | } 76 | 77 | /** 78 | * Set the inclusive end point of the stream, using an absolute ZonedDateTime. 79 | * 80 | * @param to A nullable ZonedDateTime to end the stream with (null means infinite). 81 | * @return A non-null ZonedDateTimeStream. 82 | */ 83 | public ZonedDateTimeStream to(final ZonedDateTime to) { 84 | setTo(to); 85 | return this; 86 | } 87 | 88 | /** 89 | * Set the inclusive end point of the stream, using a relative duration. 90 | * 91 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 92 | * @param unit The non-null unit the amount is denominated in. May not be null. 93 | * @return A non-null ZonedDateTimeStream. 94 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 95 | * @see ChronoUnit 96 | */ 97 | public ZonedDateTimeStream to(int amount, 98 | final ChronoUnit unit) { 99 | Objects.requireNonNull(unit); 100 | setTo(getFrom().plus(amount, unit)); 101 | return this; 102 | } 103 | 104 | /** 105 | * Set the exclusive end point of the stream, using an absolute ZonedDateTime. 106 | * 107 | * @param until A nullable ZonedDateTime to end the stream before (null means infinite). 108 | * @return A non-null ZonedDateTimeStream. 109 | */ 110 | public ZonedDateTimeStream until(final ZonedDateTime until) { 111 | setUntil(until); 112 | return this; 113 | } 114 | 115 | /** 116 | * Set the exclusive end point of the stream, using a relative duration. 117 | * 118 | * @param amount The number of units to use when calculating the duration of the stream. May be negative. 119 | * @param unit The non-null unit the amount is denominated in. 120 | * @return A non-null ZonedDateTimeStream. 121 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 122 | * @see ChronoUnit 123 | */ 124 | public ZonedDateTimeStream until(int amount, 125 | final ChronoUnit unit) { 126 | Objects.requireNonNull(unit); 127 | setUntil(getFrom().plus(amount, unit)); 128 | return this; 129 | } 130 | 131 | /** 132 | * Set the duration between successive elements produced by the stream. The default 133 | * for this builder is 1 Second. 134 | * 135 | * @param amount The number of units to use when calculating the next element of the stream. 136 | * @param unit The non-null unit the amount is denominated in. 137 | * @return A non-null ZonedDateTimeStream. 138 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 139 | * @see ChronoUnit 140 | */ 141 | public ZonedDateTimeStream every(int amount, 142 | final ChronoUnit unit) { 143 | Objects.requireNonNull(unit); 144 | this.amount = Math.abs(amount); 145 | this.unit = unit; 146 | if (this.amount == 0) { 147 | throw new IllegalArgumentException("Amount must be non-zero"); 148 | } 149 | return this; 150 | } 151 | 152 | /** 153 | * Set the duration between successive elements produced by the stream. The default 154 | * for this builder is 1 Second. 155 | * 156 | * @return A non-null ZonedDateTimeStream. 157 | * @throws java.time.temporal.UnsupportedTemporalTypeException if the unit is not supported. 158 | * @see ChronoUnit 159 | */ 160 | public ZonedDateTimeStream every(final Duration duration) { 161 | Objects.requireNonNull(unit); 162 | this.unit = ChronoUnit.SECONDS; 163 | this.amount = Math.abs(duration.get(this.unit)); 164 | if (this.amount == 0) { 165 | throw new IllegalArgumentException("Effective amount must be non-zero (Duration resolves to zero duration)"); 166 | } 167 | return this; 168 | } 169 | 170 | @Override 171 | UnaryOperator next() { 172 | return date -> date.plus(isForward() ? amount : 0 - amount, unit); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/test/java/com/ginsberg/timestream/LocalDateStreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import org.assertj.core.util.Sets; 28 | import org.junit.Test; 29 | 30 | import java.time.LocalDate; 31 | import java.time.Period; 32 | import java.time.temporal.ChronoUnit; 33 | import java.util.Set; 34 | import java.util.stream.Stream; 35 | 36 | import static com.ginsberg.timestream.util.Assertions.expectingChronoUnitException; 37 | import static com.ginsberg.timestream.util.Assertions.notExpectingChronoUnitException; 38 | import static org.assertj.core.api.Assertions.assertThat; 39 | 40 | public class LocalDateStreamTest { 41 | 42 | private final LocalDate now = LocalDate.now(); 43 | private final Set validChronoUnits = Sets.newLinkedHashSet(ChronoUnit.DAYS, ChronoUnit.WEEKS, 44 | ChronoUnit.MONTHS, ChronoUnit.YEARS, ChronoUnit.DECADES, ChronoUnit.CENTURIES, 45 | ChronoUnit.ERAS, ChronoUnit.MILLENNIA); 46 | 47 | @Test 48 | public void stopsBeforeUntilDateGivenByChronoUnits() { 49 | final Stream stream = LocalDateStream 50 | .from(now) 51 | .until(2, ChronoUnit.DAYS) 52 | .stream(); 53 | assertThat(stream) 54 | .isNotNull() 55 | .containsExactly(now, now.plusDays(1)); 56 | } 57 | 58 | @Test 59 | public void stopsBeforeUntilDateGivenByLocalDate() { 60 | final Stream stream = LocalDateStream 61 | .from(now) 62 | .until(now.plusDays(2)) 63 | .stream(); 64 | assertThat(stream) 65 | .isNotNull() 66 | .containsExactly(now, now.plusDays(1)); 67 | } 68 | 69 | @Test 70 | public void stopsOnToDateGivenByChronoUnits() { 71 | final Stream stream = LocalDateStream 72 | .from(now) 73 | .to(2, ChronoUnit.DAYS) 74 | .stream(); 75 | assertThat(stream) 76 | .isNotNull() 77 | .containsExactly(now, now.plusDays(1), now.plusDays(2)); 78 | } 79 | 80 | @Test 81 | public void stopsOnToDateGivenByLocalDate() { 82 | final Stream stream = LocalDateStream 83 | .from(now) 84 | .to(now.plusDays(2)) 85 | .stream(); 86 | assertThat(stream) 87 | .isNotNull() 88 | .containsExactly(now, now.plusDays(1), now.plusDays(2)); 89 | } 90 | 91 | @Test 92 | public void stopsBeforeToWhenEveryIsAfterEndDate() { 93 | final Stream stream = LocalDateStream 94 | .from(now) 95 | .to(3, ChronoUnit.DAYS) 96 | .every(2, ChronoUnit.DAYS) 97 | .stream(); 98 | assertThat(stream) 99 | .isNotNull() 100 | .containsExactly(now, now.plusDays(2)); 101 | } 102 | 103 | @Test 104 | public void negativeEveryUnitStillGoesForward() { 105 | final Stream stream = LocalDateStream 106 | .from(now) 107 | .to(3, ChronoUnit.DAYS) 108 | .every(-2, ChronoUnit.DAYS) 109 | .stream(); 110 | assertThat(stream) 111 | .isNotNull() 112 | .containsExactly(now, now.plusDays(2)); 113 | } 114 | 115 | @Test 116 | public void negativeEveryPeriodStillGoesForward() { 117 | final Stream stream = LocalDateStream 118 | .from(now) 119 | .to(3, ChronoUnit.DAYS) 120 | .every(Period.parse("-P2D")) 121 | .stream(); 122 | assertThat(stream) 123 | .isNotNull() 124 | .containsExactly(now, now.plusDays(2)); 125 | } 126 | 127 | @Test 128 | public void positiveEveryUnitStillGoesBackward() { 129 | final Stream stream = LocalDateStream 130 | .from(now) 131 | .to(-3, ChronoUnit.DAYS) 132 | .every(2, ChronoUnit.DAYS) 133 | .stream(); 134 | assertThat(stream) 135 | .isNotNull() 136 | .containsExactly(now, now.minusDays(2)); 137 | } 138 | 139 | @Test 140 | public void positiveEveryPeriodStillGoesBackward() { 141 | final Stream stream = LocalDateStream 142 | .from(now) 143 | .to(-3, ChronoUnit.DAYS) 144 | .every(Period.parse("P2D")) 145 | .stream(); 146 | assertThat(stream) 147 | .isNotNull() 148 | .containsExactly(now, now.minusDays(2)); 149 | } 150 | 151 | @Test 152 | public void identicalFromAndToCreateOnePointStream() { 153 | final Stream stream = LocalDateStream 154 | .from(now) 155 | .to(now) 156 | .stream(); 157 | assertThat(stream) 158 | .isNotNull() 159 | .containsExactly(now); 160 | } 161 | 162 | @Test 163 | public void noToDateRunsForever() { 164 | // No real way to test that a stream never ends so we will just make sure that this generates a lot of iterations. 165 | final int iterations = 1_000_000; 166 | final Stream stream = LocalDateStream 167 | .from(now) 168 | .stream() 169 | .limit(iterations); 170 | assertThat(stream) 171 | .isNotNull() 172 | .endsWith(now.plus(iterations - 1, ChronoUnit.DAYS)) 173 | .hasSize(iterations); 174 | } 175 | 176 | @Test 177 | public void toBeforeFromRunsBackThroughTime() { 178 | final Stream stream = LocalDateStream 179 | .from(now) 180 | .to(-2, ChronoUnit.DAYS) 181 | .stream(); 182 | assertThat(stream) 183 | .isNotNull() 184 | .containsExactly(now, now.minusDays(1), now.minusDays(2)); 185 | } 186 | 187 | @Test 188 | public void stopsBeforeToWhenEveryPeriodIsAfterEndDate() { 189 | final Period period = Period.parse("P15D"); 190 | final Stream stream = LocalDateStream 191 | .from(now) 192 | .until(now.plusDays(30)) 193 | .every(period) 194 | .stream(); 195 | assertThat(stream) 196 | .isNotNull() 197 | .containsExactly(now, now.plusDays(15)); 198 | } 199 | 200 | @Test(expected = NullPointerException.class) 201 | public void mustHaveFromDate() { 202 | LocalDateStream.from(null); 203 | } 204 | 205 | @Test(expected = NullPointerException.class) 206 | public void toByUnitsMustHaveUnit() { 207 | LocalDateStream.fromNow().to(1, null); 208 | } 209 | 210 | @Test(expected = NullPointerException.class) 211 | public void untilByUnitsMustHaveUnit() { 212 | LocalDateStream.fromNow().until(1, null); 213 | } 214 | 215 | @Test(expected = NullPointerException.class) 216 | public void everyMustHavePeriod() { 217 | LocalDateStream.fromNow().every(null); 218 | } 219 | 220 | @Test(expected = IllegalArgumentException.class) 221 | public void everyMustHaveNonZeroAmount() { 222 | LocalDateStream.fromNow().every(0, ChronoUnit.DAYS); 223 | } 224 | 225 | @Test(expected = IllegalArgumentException.class) 226 | public void everyMustHaveNonZeroAmountFromPeriod() { 227 | LocalDateStream.fromNow().every(Period.parse("P0D")); 228 | } 229 | 230 | @Test 231 | public void everyWithInvalidChronoUnitFailsFast() { 232 | expectingChronoUnitException(u -> LocalDateStream.fromNow().every(1, u), validChronoUnits); 233 | } 234 | 235 | @Test 236 | public void everyWithValidChronoUnit() { 237 | notExpectingChronoUnitException(u -> LocalDateStream.fromNow().every(1, u), validChronoUnits); 238 | } 239 | 240 | @Test 241 | public void toWithInvalidChronoUnitFailsFast() { 242 | expectingChronoUnitException(u -> LocalDateStream.fromNow().to(0, u), validChronoUnits); 243 | } 244 | 245 | @Test 246 | public void toWithValidChronoUnit() { 247 | notExpectingChronoUnitException(u -> LocalDateStream.fromNow().to(0, u), validChronoUnits); 248 | } 249 | 250 | @Test 251 | public void untilWithInvalidChronoUnitFailsFast() { 252 | expectingChronoUnitException(u -> LocalDateStream.fromNow().until(0, u), validChronoUnits); 253 | } 254 | 255 | @Test 256 | public void untilWithValidChronoUnit() { 257 | notExpectingChronoUnitException(u -> LocalDateStream.fromNow().until(0, u), validChronoUnits); 258 | } 259 | } -------------------------------------------------------------------------------- /src/test/java/com/ginsberg/timestream/LocalDateTimeStreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import org.junit.Test; 28 | 29 | import java.time.Duration; 30 | import java.time.LocalDateTime; 31 | import java.time.temporal.ChronoUnit; 32 | import java.util.stream.Stream; 33 | 34 | import static org.assertj.core.api.Assertions.assertThat; 35 | 36 | public class LocalDateTimeStreamTest { 37 | 38 | final LocalDateTime now = LocalDateTime.now(); 39 | 40 | @Test 41 | public void stopsBeforeUntilDateGivenByChronoUnits() { 42 | final Stream stream = LocalDateTimeStream 43 | .from(now) 44 | .until(2, ChronoUnit.SECONDS) 45 | .stream(); 46 | assertThat(stream) 47 | .isNotNull() 48 | .containsExactly(now, now.plusSeconds(1)); 49 | } 50 | 51 | @Test 52 | public void stopsBeforeUntilDateGivenByLocalDateTime() { 53 | final Stream stream = LocalDateTimeStream 54 | .from(now) 55 | .until(now.plusSeconds(2)) 56 | .stream(); 57 | assertThat(stream) 58 | .isNotNull() 59 | .containsExactly(now, now.plusSeconds(1)); 60 | } 61 | 62 | @Test 63 | public void stopsOnToDateGivenByChronoUnits() { 64 | final Stream stream = LocalDateTimeStream 65 | .from(now) 66 | .to(2, ChronoUnit.SECONDS) 67 | .stream(); 68 | assertThat(stream) 69 | .isNotNull() 70 | .containsExactly(now, now.plusSeconds(1), now.plusSeconds(2)); 71 | } 72 | 73 | @Test 74 | public void stopsOnToDateGivenByLocalDateTime() { 75 | final Stream stream = LocalDateTimeStream 76 | .from(now) 77 | .to(now.plusSeconds(2)) 78 | .stream(); 79 | assertThat(stream) 80 | .isNotNull() 81 | .containsExactly(now, now.plusSeconds(1), now.plusSeconds(2)); 82 | } 83 | 84 | @Test 85 | public void stopsBeforeToWhenEveryIsAfterEndDate() { 86 | final Stream stream = LocalDateTimeStream 87 | .from(now) 88 | .to(3, ChronoUnit.SECONDS) 89 | .every(2, ChronoUnit.SECONDS) 90 | .stream(); 91 | assertThat(stream) 92 | .isNotNull() 93 | .containsExactly(now, now.plusSeconds(2)); 94 | } 95 | 96 | @Test 97 | public void identicalFromAndToCreateOnePointStream() { 98 | final Stream stream = LocalDateTimeStream 99 | .from(now) 100 | .to(now) 101 | .stream(); 102 | assertThat(stream) 103 | .isNotNull() 104 | .containsExactly(now); 105 | } 106 | 107 | @Test 108 | public void noToDateRunsForever() { 109 | // No real way to test that a stream never ends so we will just make sure that this generates a lot of iterations. 110 | final int iterations = 1_000_000; 111 | final Stream stream = LocalDateTimeStream 112 | .from(now) 113 | .stream() 114 | .limit(iterations); 115 | assertThat(stream) 116 | .isNotNull() 117 | .endsWith(now.plus(iterations - 1, ChronoUnit.SECONDS)) 118 | .hasSize(iterations); 119 | } 120 | 121 | @Test 122 | public void toBeforeFromRunsBackThroughTime() { 123 | final Stream stream = LocalDateTimeStream 124 | .from(now) 125 | .to(-2, ChronoUnit.SECONDS) 126 | .stream(); 127 | assertThat(stream) 128 | .isNotNull() 129 | .containsExactly(now, now.minusSeconds(1), now.minusSeconds(2)); 130 | } 131 | 132 | @Test 133 | public void stopsBeforeToWhenEveryDurationIsAfterEndDate() { 134 | final Duration duration = Duration.parse("PT2S"); 135 | final Stream stream = LocalDateTimeStream 136 | .from(now) 137 | .to(3, ChronoUnit.SECONDS) 138 | .every(duration) 139 | .stream(); 140 | assertThat(stream) 141 | .isNotNull() 142 | .containsExactly(now, now.plusSeconds(2)); 143 | } 144 | 145 | @Test 146 | public void positiveEveryUnitStillGoesBackward() { 147 | final Stream stream = LocalDateTimeStream 148 | .from(now) 149 | .to(-3, ChronoUnit.SECONDS) 150 | .every(2, ChronoUnit.SECONDS) 151 | .stream(); 152 | assertThat(stream) 153 | .isNotNull() 154 | .containsExactly(now, now.minusSeconds(2)); 155 | } 156 | 157 | @Test 158 | public void positiveEveryDurationStillGoesBackward() { 159 | final Stream stream = LocalDateTimeStream 160 | .from(now) 161 | .to(-3, ChronoUnit.SECONDS) 162 | .every(Duration.parse("PT2S")) 163 | .stream(); 164 | assertThat(stream) 165 | .isNotNull() 166 | .containsExactly(now, now.minusSeconds(2)); 167 | } 168 | 169 | @Test 170 | public void negativeEveryUnitStillGoesForward() { 171 | final Stream stream = LocalDateTimeStream 172 | .from(now) 173 | .to(3, ChronoUnit.SECONDS) 174 | .every(-2, ChronoUnit.SECONDS) 175 | .stream(); 176 | assertThat(stream) 177 | .isNotNull() 178 | .containsExactly(now, now.plusSeconds(2)); 179 | } 180 | 181 | @Test 182 | public void negativeEveryDurationStillGoesForward() { 183 | final Stream stream = LocalDateTimeStream 184 | .from(now) 185 | .to(3, ChronoUnit.SECONDS) 186 | .every(Duration.parse("-PT2S")) 187 | .stream(); 188 | assertThat(stream) 189 | .isNotNull() 190 | .containsExactly(now, now.plusSeconds(2)); 191 | } 192 | 193 | @Test(expected = NullPointerException.class) 194 | public void mustHaveFromDate() { 195 | LocalDateTimeStream.from(null); 196 | } 197 | 198 | @Test(expected = NullPointerException.class) 199 | public void toByUnitsMustHaveUnit() { 200 | LocalDateTimeStream.fromNow().to(1, null); 201 | } 202 | 203 | @Test(expected = NullPointerException.class) 204 | public void untilByUnitsMustHaveUnit() { 205 | LocalDateTimeStream.fromNow().until(1, null); 206 | } 207 | 208 | @Test(expected = NullPointerException.class) 209 | public void everyMustHaveDuration() { 210 | LocalDateTimeStream.fromNow().every(null); 211 | } 212 | 213 | @Test(expected = IllegalArgumentException.class) 214 | public void everyMustHaveNonZeroAmount() { 215 | LocalDateTimeStream.fromNow().every(0, ChronoUnit.SECONDS); 216 | } 217 | 218 | @Test(expected = IllegalArgumentException.class) 219 | public void everyMustHaveNonZeroAmountFromPeriod() { 220 | LocalDateTimeStream.fromNow().every(Duration.parse("PT0S")); 221 | } 222 | } -------------------------------------------------------------------------------- /src/test/java/com/ginsberg/timestream/TakeWhileTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import org.junit.Test; 28 | 29 | import java.util.Arrays; 30 | import java.util.Collections; 31 | import java.util.stream.Stream; 32 | import java.util.stream.StreamSupport; 33 | 34 | import static org.assertj.core.api.Assertions.assertThat; 35 | 36 | public class TakeWhileTest { 37 | 38 | @Test 39 | public void stopsWhenPredicateReturnsFirstFalse() { 40 | final Stream realStream = Arrays.asList("A", "B", "A", "B").stream(); 41 | final TakeWhile takeWhile = TakeWhile.of(realStream.spliterator(), s -> s.equalsIgnoreCase("A")); 42 | final Stream limitedStream = StreamSupport.stream(takeWhile, false); 43 | assertThat(limitedStream).containsExactly("A"); 44 | } 45 | 46 | @Test 47 | public void doesNotFailWithEmptyStream() { 48 | final Stream realStream = Collections.emptyList().stream(); 49 | final TakeWhile takeWhile = TakeWhile.of(realStream.spliterator(), s -> true); 50 | final Stream limitedStream = StreamSupport.stream(takeWhile, false); 51 | assertThat(limitedStream).isEmpty(); 52 | } 53 | 54 | @Test 55 | public void streamRunsOutBeforePredicateReturnsFalse() { 56 | final Stream realStream = Arrays.asList("A", "B", "C", "D").stream(); 57 | final TakeWhile takeWhile = TakeWhile.of(realStream.spliterator(), s -> !s.equalsIgnoreCase("E")); 58 | final Stream limitedStream = StreamSupport.stream(takeWhile, false); 59 | assertThat(limitedStream).containsExactly("A", "B", "C", "D"); 60 | } 61 | } -------------------------------------------------------------------------------- /src/test/java/com/ginsberg/timestream/YearMonthStreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import org.assertj.core.util.Sets; 28 | import org.junit.Test; 29 | 30 | import java.time.Period; 31 | import java.time.YearMonth; 32 | import java.time.temporal.ChronoUnit; 33 | import java.util.Set; 34 | import java.util.stream.Stream; 35 | 36 | import static com.ginsberg.timestream.util.Assertions.expectingChronoUnitException; 37 | import static com.ginsberg.timestream.util.Assertions.notExpectingChronoUnitException; 38 | import static org.assertj.core.api.Assertions.assertThat; 39 | 40 | public class YearMonthStreamTest { 41 | 42 | final YearMonth now = YearMonth.now(); 43 | private final Set validChronoUnits = Sets.newLinkedHashSet(ChronoUnit.MONTHS, ChronoUnit.YEARS, 44 | ChronoUnit.DECADES, ChronoUnit.CENTURIES, ChronoUnit.ERAS, ChronoUnit.MILLENNIA); 45 | 46 | @Test 47 | public void stopsBeforeUntilDateGivenByChronoUnits() { 48 | final Stream stream = YearMonthStream 49 | .from(now) 50 | .until(2, ChronoUnit.MONTHS) 51 | .stream(); 52 | assertThat(stream) 53 | .isNotNull() 54 | .containsExactly(now, now.plusMonths(1)); 55 | } 56 | 57 | @Test 58 | public void stopsBeforeUntilDateGivenByYearMonth() { 59 | final Stream stream = YearMonthStream 60 | .from(now) 61 | .until(now.plusMonths(2)) 62 | .stream(); 63 | assertThat(stream) 64 | .isNotNull() 65 | .containsExactly(now, now.plusMonths(1)); 66 | } 67 | 68 | @Test 69 | public void stopsOnToDateGivenByChronoUnits() { 70 | final Stream stream = YearMonthStream 71 | .from(now) 72 | .to(2, ChronoUnit.MONTHS) 73 | .stream(); 74 | assertThat(stream) 75 | .isNotNull() 76 | .containsExactly(now, now.plusMonths(1), now.plusMonths(2)); 77 | } 78 | 79 | @Test 80 | public void stopsOnToDateGivenByYearMonth() { 81 | final Stream stream = YearMonthStream 82 | .from(now) 83 | .to(now.plusMonths(2)) 84 | .stream(); 85 | assertThat(stream) 86 | .isNotNull() 87 | .containsExactly(now, now.plusMonths(1), now.plusMonths(2)); 88 | } 89 | 90 | @Test 91 | public void stopsBeforeToWhenEveryIsAfterEndDate() { 92 | final Stream stream = YearMonthStream 93 | .from(now) 94 | .to(3, ChronoUnit.MONTHS) 95 | .every(2, ChronoUnit.MONTHS) 96 | .stream(); 97 | assertThat(stream) 98 | .isNotNull() 99 | .containsExactly(now, now.plusMonths(2)); 100 | } 101 | 102 | @Test 103 | public void negativeEveryUnitStillGoesForward() { 104 | final Stream stream = YearMonthStream 105 | .from(now) 106 | .to(3, ChronoUnit.MONTHS) 107 | .every(-2, ChronoUnit.MONTHS) 108 | .stream(); 109 | assertThat(stream) 110 | .isNotNull() 111 | .containsExactly(now, now.plusMonths(2)); 112 | } 113 | 114 | 115 | @Test 116 | public void negativeEveryPeriodStillGoesForward() { 117 | final Stream stream = YearMonthStream 118 | .from(now) 119 | .to(3, ChronoUnit.MONTHS) 120 | .every(Period.parse("-P2M")) 121 | .stream(); 122 | assertThat(stream) 123 | .isNotNull() 124 | .containsExactly(now, now.plusMonths(2)); 125 | } 126 | 127 | @Test 128 | public void positiveEveryUnitStillGoesBackward() { 129 | final Stream stream = YearMonthStream 130 | .from(now) 131 | .to(-3, ChronoUnit.MONTHS) 132 | .every(2, ChronoUnit.MONTHS) 133 | .stream(); 134 | assertThat(stream) 135 | .isNotNull() 136 | .containsExactly(now, now.minusMonths(2)); 137 | } 138 | 139 | @Test 140 | public void positiveEveryPeriodStillGoesBackward() { 141 | final Stream stream = YearMonthStream 142 | .from(now) 143 | .to(-3, ChronoUnit.MONTHS) 144 | .every(Period.parse("P2M")) 145 | .stream(); 146 | assertThat(stream) 147 | .isNotNull() 148 | .containsExactly(now, now.minusMonths(2)); 149 | } 150 | 151 | @Test 152 | public void identicalFromAndToCreateOnePointStream() { 153 | final Stream stream = YearMonthStream 154 | .from(now) 155 | .to(now) 156 | .stream(); 157 | assertThat(stream) 158 | .isNotNull() 159 | .containsExactly(now); 160 | } 161 | 162 | @Test 163 | public void noToDateRunsForever() { 164 | // No real way to test that a stream never ends so we will just make sure that this generates a lot of iterations. 165 | final int iterations = 1_000_000; 166 | final Stream stream = YearMonthStream 167 | .from(now) 168 | .stream() 169 | .limit(iterations); 170 | assertThat(stream) 171 | .isNotNull() 172 | .endsWith(now.plus(iterations - 1, ChronoUnit.MONTHS)) 173 | .hasSize(iterations); 174 | } 175 | 176 | @Test 177 | public void toBeforeFromRunsBackThroughTime() { 178 | final Stream stream = YearMonthStream 179 | .from(now) 180 | .to(-2, ChronoUnit.MONTHS) 181 | .stream(); 182 | assertThat(stream) 183 | .isNotNull() 184 | .containsExactly(now, now.minusMonths(1), now.minusMonths(2)); 185 | } 186 | 187 | @Test 188 | public void stopsBeforeToWhenEveryDurationIsAfterEndDate() { 189 | final Period period = Period.parse("P2M"); 190 | final Stream stream = YearMonthStream 191 | .from(now) 192 | .to(3, ChronoUnit.MONTHS) 193 | .every(period) 194 | .stream(); 195 | assertThat(stream) 196 | .isNotNull() 197 | .containsExactly(now, now.plusMonths(2)); 198 | } 199 | 200 | @Test(expected = NullPointerException.class) 201 | public void mustHaveFromDate() { 202 | YearMonthStream.from(null); 203 | } 204 | 205 | @Test(expected = NullPointerException.class) 206 | public void toByUnitsMustHaveUnit() { 207 | YearMonthStream.fromNow().to(1, null); 208 | } 209 | 210 | @Test(expected = NullPointerException.class) 211 | public void untilByUnitsMustHaveUnit() { 212 | YearMonthStream.fromNow().until(1, null); 213 | } 214 | 215 | @Test(expected = NullPointerException.class) 216 | public void everyMustHavePeriod() { 217 | YearMonthStream.fromNow().every(null); 218 | } 219 | 220 | @Test(expected = IllegalArgumentException.class) 221 | public void everyMustHaveNonZeroAmount() { 222 | YearMonthStream.fromNow().every(0, ChronoUnit.MONTHS); 223 | } 224 | 225 | @Test(expected = IllegalArgumentException.class) 226 | public void everyMustHaveNonZeroAmountFromPeriod() { 227 | YearMonthStream.fromNow().every(Period.parse("P1W")); 228 | } 229 | 230 | @Test 231 | public void everyWithInvalidChronoUnitFailsFast() { 232 | expectingChronoUnitException(u -> YearMonthStream.fromNow().every(1, u), validChronoUnits); 233 | } 234 | 235 | @Test 236 | public void everyWithValidChronoUnit() { 237 | notExpectingChronoUnitException(u -> YearMonthStream.fromNow().every(1, u), validChronoUnits); 238 | } 239 | 240 | @Test 241 | public void toWithInvalidChronoUnitFailsFast() { 242 | expectingChronoUnitException(u -> YearMonthStream.fromNow().to(0, u), validChronoUnits); 243 | } 244 | 245 | @Test 246 | public void toWithValidChronoUnit() { 247 | notExpectingChronoUnitException(u -> YearMonthStream.fromNow().to(0, u), validChronoUnits); 248 | } 249 | 250 | @Test 251 | public void untilWithInvalidChronoUnitFailsFast() { 252 | expectingChronoUnitException(u -> YearMonthStream.fromNow().until(0, u), validChronoUnits); 253 | } 254 | 255 | @Test 256 | public void untilWithValidChronoUnit() { 257 | notExpectingChronoUnitException(u -> YearMonthStream.fromNow().until(0, u), validChronoUnits); 258 | } 259 | 260 | } -------------------------------------------------------------------------------- /src/test/java/com/ginsberg/timestream/ZonedDateTimeStreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream; 26 | 27 | import org.junit.Test; 28 | 29 | import java.time.Duration; 30 | import java.time.ZonedDateTime; 31 | import java.time.temporal.ChronoUnit; 32 | import java.util.stream.Stream; 33 | 34 | import static org.assertj.core.api.Assertions.assertThat; 35 | 36 | public class ZonedDateTimeStreamTest { 37 | 38 | final ZonedDateTime now = ZonedDateTime.now(); 39 | 40 | @Test 41 | public void stopsBeforeUntilDateGivenByChronoUnits() { 42 | final Stream stream = ZonedDateTimeStream 43 | .from(now) 44 | .until(2, ChronoUnit.SECONDS) 45 | .stream(); 46 | assertThat(stream) 47 | .isNotNull() 48 | .containsExactly(now, now.plusSeconds(1)); 49 | } 50 | 51 | @Test 52 | public void stopsBeforeUntilDateGivenByZonedDateTime() { 53 | final Stream stream = ZonedDateTimeStream 54 | .from(now) 55 | .until(now.plusSeconds(2)) 56 | .stream(); 57 | assertThat(stream) 58 | .isNotNull() 59 | .containsExactly(now, now.plusSeconds(1)); 60 | } 61 | 62 | @Test 63 | public void stopsOnToDateGivenByChronoUnits() { 64 | final Stream stream = ZonedDateTimeStream 65 | .from(now) 66 | .to(2, ChronoUnit.SECONDS) 67 | .stream(); 68 | assertThat(stream) 69 | .isNotNull() 70 | .containsExactly(now, now.plusSeconds(1), now.plusSeconds(2)); 71 | } 72 | 73 | @Test 74 | public void stopsOnToDateGivenByZonedDateTime() { 75 | final Stream stream = ZonedDateTimeStream 76 | .from(now) 77 | .to(now.plusSeconds(2)) 78 | .stream(); 79 | assertThat(stream) 80 | .isNotNull() 81 | .containsExactly(now, now.plusSeconds(1), now.plusSeconds(2)); 82 | } 83 | 84 | @Test 85 | public void stopsBeforeToWhenEveryIsAfterEndDate() { 86 | final Stream stream = ZonedDateTimeStream 87 | .from(now) 88 | .to(3, ChronoUnit.SECONDS) 89 | .every(2, ChronoUnit.SECONDS) 90 | .stream(); 91 | assertThat(stream) 92 | .isNotNull() 93 | .containsExactly(now, now.plusSeconds(2)); 94 | } 95 | 96 | @Test 97 | public void identicalFromAndToCreateOnePointStream() { 98 | final Stream stream = ZonedDateTimeStream 99 | .from(now) 100 | .to(now) 101 | .stream(); 102 | assertThat(stream) 103 | .isNotNull() 104 | .containsExactly(now); 105 | } 106 | 107 | @Test 108 | public void noToDateRunsForever() { 109 | // No real way to test that a stream never ends so we will just make sure that this generates a lot of iterations. 110 | final int iterations = 1_000_000; 111 | final Stream stream = ZonedDateTimeStream 112 | .from(now) 113 | .stream() 114 | .limit(iterations); 115 | assertThat(stream) 116 | .isNotNull() 117 | .endsWith(now.plus(iterations - 1, ChronoUnit.SECONDS)) 118 | .hasSize(iterations); 119 | } 120 | 121 | @Test 122 | public void toBeforeFromRunsBackThroughTime() { 123 | final Stream stream = ZonedDateTimeStream 124 | .from(now) 125 | .to(-2, ChronoUnit.SECONDS) 126 | .stream(); 127 | assertThat(stream) 128 | .isNotNull() 129 | .containsExactly(now, now.minusSeconds(1), now.minusSeconds(2)); 130 | } 131 | 132 | @Test 133 | public void stopsBeforeToWhenEveryDurationIsAfterEndDate() { 134 | final Duration duration = Duration.parse("PT2S"); 135 | final Stream stream = ZonedDateTimeStream 136 | .from(now) 137 | .to(3, ChronoUnit.SECONDS) 138 | .every(duration) 139 | .stream(); 140 | assertThat(stream) 141 | .isNotNull() 142 | .containsExactly(now, now.plusSeconds(2)); 143 | } 144 | 145 | @Test 146 | public void positiveEveryUnitStillGoesBackward() { 147 | final Stream stream = ZonedDateTimeStream 148 | .from(now) 149 | .to(-3, ChronoUnit.SECONDS) 150 | .every(2, ChronoUnit.SECONDS) 151 | .stream(); 152 | assertThat(stream) 153 | .isNotNull() 154 | .containsExactly(now, now.minusSeconds(2)); 155 | } 156 | 157 | @Test 158 | public void positiveEveryDurationStillGoesBackward() { 159 | final Stream stream = ZonedDateTimeStream 160 | .from(now) 161 | .to(-3, ChronoUnit.SECONDS) 162 | .every(Duration.parse("PT2S")) 163 | .stream(); 164 | assertThat(stream) 165 | .isNotNull() 166 | .containsExactly(now, now.minusSeconds(2)); 167 | } 168 | 169 | @Test 170 | public void negativeEveryUnitStillGoesForward() { 171 | final Stream stream = ZonedDateTimeStream 172 | .from(now) 173 | .to(3, ChronoUnit.SECONDS) 174 | .every(-2, ChronoUnit.SECONDS) 175 | .stream(); 176 | assertThat(stream) 177 | .isNotNull() 178 | .containsExactly(now, now.plusSeconds(2)); 179 | } 180 | 181 | @Test 182 | public void negativeEveryDurationStillGoesForward() { 183 | final Stream stream = ZonedDateTimeStream 184 | .from(now) 185 | .to(3, ChronoUnit.SECONDS) 186 | .every(Duration.parse("-PT2S")) 187 | .stream(); 188 | assertThat(stream) 189 | .isNotNull() 190 | .containsExactly(now, now.plusSeconds(2)); 191 | } 192 | 193 | @Test(expected = NullPointerException.class) 194 | public void mustHaveFromDate() { 195 | ZonedDateTimeStream.from(null); 196 | } 197 | 198 | @Test(expected = NullPointerException.class) 199 | public void toByUnitsMustHaveUnit() { 200 | ZonedDateTimeStream.fromNow().to(1, null); 201 | } 202 | 203 | @Test(expected = NullPointerException.class) 204 | public void untilByUnitsMustHaveUnit() { 205 | ZonedDateTimeStream.fromNow().until(1, null); 206 | } 207 | 208 | @Test(expected = NullPointerException.class) 209 | public void everyMustHavePeriod() { 210 | ZonedDateTimeStream.fromNow().every(null); 211 | } 212 | 213 | @Test(expected = IllegalArgumentException.class) 214 | public void everyMustHaveNonZeroAmount() { 215 | ZonedDateTimeStream.fromNow().every(0, ChronoUnit.SECONDS); 216 | } 217 | 218 | @Test(expected = IllegalArgumentException.class) 219 | public void everyMustHaveNonZeroAmountFromPeriod() { 220 | ZonedDateTimeStream.fromNow().every(Duration.parse("PT0S")); 221 | } 222 | } -------------------------------------------------------------------------------- /src/test/java/com/ginsberg/timestream/util/Assertions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Todd Ginsberg 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.ginsberg.timestream.util; 26 | 27 | import java.time.temporal.ChronoUnit; 28 | import java.time.temporal.UnsupportedTemporalTypeException; 29 | import java.util.Arrays; 30 | import java.util.Collection; 31 | import java.util.function.Consumer; 32 | 33 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 34 | 35 | public abstract class Assertions { 36 | 37 | public static void expectingChronoUnitException(final Consumer consumer, final Collection validUnits) { 38 | Arrays.stream(ChronoUnit.values()) 39 | .filter(u -> !validUnits.contains(u)) 40 | .forEach(u -> assertThatExceptionOfType(UnsupportedTemporalTypeException.class).isThrownBy( 41 | () -> consumer.accept(u) 42 | )); 43 | } 44 | 45 | public static void notExpectingChronoUnitException(final Consumer consumer, final Collection validUnits) { 46 | validUnits.stream().forEach(consumer); 47 | } 48 | } 49 | --------------------------------------------------------------------------------