001/*
002 * The MIT License
003 *
004 * Copyright (c) 2013-2016, Ninja Squad
005 *
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be included in
014 * all copies or substantial portions of the Software.
015 *
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
022 * THE SOFTWARE.
023 */
024
025package com.ninja_squad.dbsetup.generator;
026
027import javax.annotation.Nonnull;
028import java.sql.Timestamp;
029import java.time.LocalDate;
030import java.time.LocalDateTime;
031import java.time.ZoneId;
032import java.time.ZonedDateTime;
033import java.time.temporal.ChronoUnit;
034import java.time.temporal.TemporalUnit;
035import java.util.Calendar;
036import java.util.Date;
037import java.util.TimeZone;
038
039import com.ninja_squad.dbsetup.util.Preconditions;
040
041/**
042 * A {@link ValueGenerator} that returns a sequence of dates, starting at a given zoned date time and incremented by a
043 * given time, specified as an increment and a temporal unit.
044 * @author JB
045 */
046public final class DateSequenceValueGenerator implements ValueGenerator<ZonedDateTime> {
047
048    // the number of chars in yyyy-mm-dd hh:mm:ss
049    private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;
050
051    /**
052     * The available units for the increment of this sequence
053     * @deprecated use ChronoField instead. This enum is only kept to maintain backward compatibility
054     */
055    @Deprecated
056    public enum CalendarField {
057        YEAR(ChronoUnit.YEARS),
058        MONTH(ChronoUnit.MONTHS),
059        DAY(ChronoUnit.DAYS),
060        HOUR(ChronoUnit.HOURS),
061        MINUTE(ChronoUnit.MINUTES),
062        SECOND(ChronoUnit.SECONDS),
063        MILLISECOND(ChronoUnit.MILLIS);
064
065        private TemporalUnit unit;
066
067        CalendarField(TemporalUnit unit) {
068            this.unit = unit;
069        }
070
071        private TemporalUnit toTemporalUnit() {
072            return unit;
073        }
074    }
075
076    private ZonedDateTime next;
077    private int increment;
078    private TemporalUnit unit;
079
080    DateSequenceValueGenerator() {
081        this(LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault()), 1, ChronoUnit.DAYS);
082    }
083
084    private DateSequenceValueGenerator(ZonedDateTime next, int increment, TemporalUnit unit) {
085        this.next = next;
086        this.increment = increment;
087        this.unit = unit;
088    }
089
090    /**
091     * Restarts the sequence at the given date, in the given time zone
092     * @return this instance, for chaining
093     * @deprecated use one of the other <code>startingAt()</code> methods taking java.time types as argument
094     */
095    @Deprecated
096    public DateSequenceValueGenerator startingAt(@Nonnull Date startDate, @Nonnull TimeZone timeZone) {
097        Preconditions.checkNotNull(startDate, "startDate may not be null");
098        Preconditions.checkNotNull(timeZone, "timeZone may not be null");
099        next = startDate.toInstant().atZone(timeZone.toZoneId());
100        return this;
101    }
102
103    /**
104     * Restarts the sequence at the given date, in the default time zone
105     * @return this instance, for chaining
106     * @deprecated use one of the other <code>startingAt()</code> methods taking java.time types as argument
107     */
108    @Deprecated
109    public DateSequenceValueGenerator startingAt(@Nonnull Date startDate) {
110        return startingAt(startDate, TimeZone.getDefault());
111    }
112
113    /**
114     * Restarts the sequence at the given date
115     * @return this instance, for chaining
116     * @deprecated use one of the other <code>startingAt()</code> methods taking java.time types as argument
117     */
118    @Deprecated
119    public DateSequenceValueGenerator startingAt(@Nonnull Calendar startDate) {
120        Preconditions.checkNotNull(startDate, "startDate may not be null");
121        next = startDate.toInstant().atZone(startDate.getTimeZone().toZoneId());
122        return this;
123    }
124
125    /**
126     * Restarts the sequence at the given date, in the default time zone
127     * @param startDate the starting date, as a String. The supported formats are the same as the ones supported by
128     * {@link com.ninja_squad.dbsetup.bind.Binders#timestampBinder()}, i.e. the formats supported by
129     * <code>java.sql.Timestamp.valueOf()</code> and <code>java.sql.Date.valueOf()</code>
130     * @return this instance, for chaining
131     */
132    public DateSequenceValueGenerator startingAt(@Nonnull String startDate) {
133        Preconditions.checkNotNull(startDate, "startDate may not be null");
134        if (startDate.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {
135            return startingAt(new Date(Timestamp.valueOf(startDate).getTime()));
136        }
137        else {
138            return startingAt(new Date(java.sql.Date.valueOf(startDate).getTime()));
139        }
140    }
141
142    /**
143     * Restarts the sequence at the given local date, in the default time zone
144     * @return this instance, for chaining
145     */
146    public DateSequenceValueGenerator startingAt(@Nonnull LocalDate startDate) {
147        return startingAt(startDate.atStartOfDay());
148    }
149
150    /**
151     * Restarts the sequence at the given local date time, in the default time zone
152     * @return this instance, for chaining
153     */
154    public DateSequenceValueGenerator startingAt(@Nonnull LocalDateTime startDate) {
155        return startingAt(startDate.atZone(ZoneId.systemDefault()));
156    }
157
158    /**
159     * Restarts the sequence at the given zoned date time
160     * @return this instance, for chaining
161     */
162    public DateSequenceValueGenerator startingAt(@Nonnull ZonedDateTime startDate) {
163        next = startDate;
164        return this;
165    }
166
167    /**
168     * Increments the date by the given increment of the given unit.
169     * @return this instance, for chaining
170     * @deprecated use the other {@link #incrementingBy(int, TemporalUnit)} method
171     */
172    @Deprecated
173    public DateSequenceValueGenerator incrementingBy(int increment, @Nonnull CalendarField unit) {
174        Preconditions.checkNotNull(unit, "unit may not be null");
175        return incrementingBy(increment, unit.toTemporalUnit());
176    }
177
178    /**
179     * Increments the date by the given increment of the given unit. One of the constants of ChronoField is typically
180     * used for the unit.
181     * @return this instance, for chaining
182     */
183    public DateSequenceValueGenerator incrementingBy(int increment, @Nonnull TemporalUnit unit) {
184        Preconditions.checkNotNull(unit, "unit may not be null");
185        this.increment = increment;
186        this.unit = unit;
187        return this;
188    }
189
190    @Override
191    public ZonedDateTime nextValue() {
192        ZonedDateTime result = next;
193        next = next.plus(increment, unit);
194        return result;
195    }
196
197    @Override
198    public String toString() {
199        return "DateSequenceValueGenerator["
200               + "next=" + next
201               + ", increment=" + increment
202               + ", unit=" + unit
203               + "]";
204    }
205}