All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cloudhopper.commons.util.time.DateTimePeriod Maven / Gradle / Ivy

package com.cloudhopper.commons.util.time;

/*
 * #%L
 * ch-commons-util
 * %%
 * Copyright (C) 2012 Cloudhopper by Twitter
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.util.ArrayList;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

/**
 * Represents two DateTime values that represent the "start" and "end" of a
 * period of time.  Useful for selecting data from a database for records
 * between two DateTime values.  The "start" DateTime is inclusive for the
 * period, however the "end" DateTime is exclusive for the period.
 *
 * For example, a period of a month such as "January 2009" would have a start
 * DateTime of "2009-01-01 00:00:00.000" and an end DateTime of
 * "2009-02-01 00:00:00.000".  Since the start date is inclusive and the end
 * date is exclusive, the resulting SELECT statement would consist of:
 *
 *   "DateCol" >= period.getStart() AND "DateCol" < period.getEnd()  .
 *
 * NOTE: This cannot be used with a BETWEEN statement since the end date should
 * not actually be included.  This is more precise since databases may have
 * different levels of precision such as no milliseconds, or perhaps nano
 * precision.  Also, this representation makes the internal calculations much
 * easier since the end date would always be start.plusMonths(1).
 * 
 * @author joelauer (twitter: @jjlauer or http://twitter.com/jjlauer)
 */
public abstract class DateTimePeriod {

    private final DateTime start;
    private final DateTime end;
    private final DateTimeDuration duration;
    // unique key representing this period such as 2009-01 or 2009-06-24 or 2009-06-24-01
    private final String key;
    // long name of period such as "January 2009" or "January 1, 2009" or "January 1, 2009 13:00"
    private final String longName;
    // short name of period such as "2009" or "2009-01" or "2009-06-24" or "13:00" or "13:15"
    private final String shortName;
    // index name of period representing the most granular duration this period
    // supports such as "2009" for a year or "01" for a month or "24" for a day or "13" or "15"
    private final String indexName;

    protected DateTimePeriod(DateTime start, DateTime end, DateTimeDuration duration, String keyPattern,
            String longNamePattern, String shortNamePattern, String indexNamePattern) {
        this.start = start;
        this.end = end;
        this.duration = duration;
        this.key = DateTimeFormat.forPattern(keyPattern).print(start);
        this.longName = DateTimeFormat.forPattern(longNamePattern).print(start);
        this.shortName = DateTimeFormat.forPattern(shortNamePattern).print(start);
        this.indexName = DateTimeFormat.forPattern(indexNamePattern).print(start);
    }

    /**
     * Gets the inclusive starting DateTime for this period.
     * @return The inclusive starting DateTime for this period.
     */
    public DateTime getStart() {
        return this.start;
    }

    /**
     * Gets the exclusive ending DateTime for this period.
     * @return The exclusive ending DateTime for this period.
     */
    public DateTime getEnd() {
        return this.end;
    }

    /**
     * Gets the duration this period represents such as a Month, Year, etc.
     * @return The duration for this period
     */
    public DateTimeDuration getDuration() {
        return this.duration;
    }

    /**
     * Gets a key that is unique to this period.  This key is unique and could
     * be used in a HashTable, "select" box on a webpage, etc.  A period can
     * be recreated back from a key.  For example:
     * 
    *
  • The year 2009 would have a key of "2009" *
  • The month January 2009 would have key of "2009-01" *
  • The day January 24, 2009 would have a key of "2009-01-24" *
  • The hour January 24, 2009 at 7 AM would have a key of "2009-01-24-07" *
  • The hour January 24, 2009 at 2 PM would have a key of "2009-01-24-14" *
  • The five minutes at January 24, 2009 at 2:15 PM would have a key of "2009-01-24-14-15" *
* @return A key to this period such as "2009-01" to represent a period for * January 2009. */ public String getKey() { return this.key; } /** * Gets a long name for this period appropriate for display as the title * for a report such as "January 2009". For example: *
    *
  • The year 2009 would have a long name of "2009" *
  • The month January 2009 would have a long name of "January 2009" *
  • The day January 24, 2009 would have a long name of "January 24, 2009" *
  • The hour January 24, 2009 at 7 AM would have a long name of "January 24, 2009 07:00" *
  • The hour January 24, 2009 at 2 PM would have a long name of "January 24, 2009 14:00" *
  • The five minutes at January 24, 2009 at 2:15 PM would have a long name of "January 24, 2009 14:15" *
* @return The long name of the period such as "January 2009" * @see #getShortName() * @see #getIndexName() */ public String getLongName() { return this.longName; } /** * Gets a short name for this period appropriate for display within a table * of data to identify a row. A short name will be no longer than 10 characters. * For example: *
    *
  • The year 2009 would have a short name of "2009" *
  • The month January 2009 would have a short name of "2009-01" *
  • The day January 24, 2009 would have a short name of "2009-01-24" *
  • The hour January 24, 2009 at 7 AM would have a short name of "07:00" *
  • The hour January 24, 2009 at 2 PM would have a short name of "14:00" *
  • The five minutes at January 24, 2009 at 2:15 PM would have a short name of "14:15" *
* @return The short name of the period such as "2009-01-24" * @see #getLongName() * @see #getIndexName() */ public String getShortName() { return this.shortName; } /** * Gets an index name for this period appropriate for display to only identify * the particular row by the smallest possible identifer. An index name will * be no longer than 4 characters. * For example: *
    *
  • The year 2009 would have an index name of "2009" *
  • The month January 2009 would have an index name of "01" *
  • The day January 24, 2009 would have an index name of "24" *
  • The hour January 24, 2009 at 7 AM would have an index name of "07" *
  • The hour January 24, 2009 at 2 PM would have an index name of "14" *
  • The five minutes at January 24, 2009 at 2:15 PM would have an index name of "15" *
* @return The index name of the period such as "01" for January * @see #getLongName() * @see #getShortName() */ public String getIndexName() { return this.indexName; } /** * Gets the next period based on the duration. For example, if this period * represents a Month, this will return a period for the next Month. * @return The next period */ public abstract DateTimePeriod getNext(); /** * Gets the previous period based on the duration. For example, if this period * represents a Month, this will return a period for the previous Month. * @return The previous period */ public abstract DateTimePeriod getPrevious(); /** * Gets the default "Group By" enumeration based on the duration for this * period. The default "Group By" is usually the period "slice" one level * below this one. For example, if this period is a month, this would * return a grouping by year-month-day. If this period is a year, * this would return a grouping by year-quarter. * @return A default group by determined by this period's duration */ public abstract DateTimeDuration getDefaultSubDuration(); /** * Converts this period to a list of periods for the given duration. This is * an alternative way to create a list of periods instead of calling each * specific duration method such as toHours() or toDays(). Partial periods will not be * included. * @return A list of periods contained within this period for the specified duration * @see #toFiveMinutes() * @see #toHours() * @see #toDays() * @see #toMonths() * @see #toYears() */ public List toPeriods(DateTimeDuration duration) { if (duration == DateTimeDuration.YEAR) { return toYears(); } else if (duration == DateTimeDuration.MONTH) { return toMonths(); } else if (duration == DateTimeDuration.DAY) { return toDays(); } else if (duration == DateTimeDuration.HOUR) { return toHours(); } else if (duration == DateTimeDuration.FIVE_MINUTES) { return toFiveMinutes(); } else { throw new UnsupportedOperationException("Duration " + duration + " not supported yet"); } } /** * Converts this period to a list of five minute periods. Partial five minute periods will not be * included. For example, a period of "January 20, 2009 7 to 8 AM" will return a list * of 12 five minute periods - one for each 5 minute block of time 0, 5, 10, 15, etc. * On the other hand, a period of "January 20, 2009 at 13:04" would return an empty list since partial * five minute periods are not included. * @return A list of five minute periods contained within this period */ public List toFiveMinutes() { ArrayList list = new ArrayList(); // default "current" five minutes to start datetime DateTime currentStart = getStart(); // calculate "next" five minutes DateTime nextStart = currentStart.plusMinutes(5); // continue adding until we've reached the end while (nextStart.isBefore(getEnd()) || nextStart.isEqual(getEnd())) { // its okay to add the current list.add(new DateTimeFiveMinutes(currentStart, nextStart)); // increment both currentStart = nextStart; nextStart = currentStart.plusMinutes(5); } return list; } /** * Converts this period to a list of hour periods. Partial hours will not be * included. For example, a period of "January 20, 2009" will return a list * of 24 hours - one for each hour on January 20, 2009. On the other hand, * a period of "January 20, 2009 13:10" would return an empty list since partial * hours are not included. * @return A list of hour periods contained within this period */ public List toHours() { ArrayList list = new ArrayList(); // default "current" hour to start datetime DateTime currentStart = getStart(); // calculate "next" hour DateTime nextStart = currentStart.plusHours(1); // continue adding until we've reached the end while (nextStart.isBefore(getEnd()) || nextStart.isEqual(getEnd())) { // its okay to add the current list.add(new DateTimeHour(currentStart, nextStart)); // increment both currentStart = nextStart; nextStart = currentStart.plusHours(1); } return list; } /** * Converts this period to a list of day periods. Partial days will not be * included. For example, a period of "January 2009" will return a list * of 31 days - one for each day in January. On the other hand, a period * of "January 20, 2009 13:00-59" would return an empty list since partial * days are not included. * @return A list of day periods contained within this period */ public List toDays() { ArrayList list = new ArrayList(); // default "current" day to start datetime DateTime currentStart = getStart(); // calculate "next" day DateTime nextStart = currentStart.plusDays(1); // continue adding until we've reached the end while (nextStart.isBefore(getEnd()) || nextStart.isEqual(getEnd())) { // its okay to add the current list.add(new DateTimeDay(currentStart, nextStart)); // increment both currentStart = nextStart; nextStart = currentStart.plusDays(1); } return list; } /** * Converts this period to a list of month periods. Partial months will not be * included. For example, a period of "2009" will return a list * of 12 months - one for each month in 2009. On the other hand, a period * of "January 20, 2009" would return an empty list since partial * months are not included. * @return A list of month periods contained within this period */ public List toMonths() { ArrayList list = new ArrayList(); // default "current" month to start datetime DateTime currentStart = getStart(); // calculate "next" month DateTime nextStart = currentStart.plusMonths(1); // continue adding until we've reached the end while (nextStart.isBefore(getEnd()) || nextStart.isEqual(getEnd())) { // its okay to add the current list.add(new DateTimeMonth(currentStart, nextStart)); // increment both currentStart = nextStart; nextStart = currentStart.plusMonths(1); } return list; } /** * Converts this period to a list of year periods. Partial years will not be * included. For example, a period of "2009-2010" will return a list * of 2 years - one for 2009 and one for 2010. On the other hand, a period * of "January 2009" would return an empty list since partial * years are not included. * @return A list of year periods contained within this period */ public List toYears() { ArrayList list = new ArrayList(); // default "current" year to start datetime DateTime currentStart = getStart(); // calculate "next" year DateTime nextStart = currentStart.plusYears(1); // continue adding until we've reached the end while (nextStart.isBefore(getEnd()) || nextStart.isEqual(getEnd())) { // its okay to add the current list.add(new DateTimeYear(currentStart, nextStart)); // increment both currentStart = nextStart; nextStart = currentStart.plusYears(1); } return list; } /** * Compares another DateTimePeriod for equality if the internal start * and end DateTimes equal this period. * @param object The other DateTimePeriod * @return True if the internal start and end DateTimes equal this period. */ @Override public boolean equals(Object object) { if (!(object instanceof DateTimePeriod)) { throw new ClassCastException("Can only compare with other DateTimePeriod instances"); } DateTimePeriod otherPeriod = (DateTimePeriod)object; if (this.start == null && otherPeriod.start != null) { return false; } if (this.end == null && otherPeriod.end != null) { return false; } if (this.start.equals(otherPeriod.start) && this.end.equals(otherPeriod.end)) { return true; } return true; } /** * Hashcode computed based on the internal start and end DateTime hashCode * values. * @return A hashCode based on the hashCode of the internal start and end * DateTime values. */ @Override public int hashCode() { int hash = 5; hash = 59 * hash + (this.start != null ? this.start.hashCode() : 0); hash = 59 * hash + (this.end != null ? this.end.hashCode() : 0); return hash; } /** * Creates a new period for the specified duration starting from the * provided DateTime. * @param duration The duration for this period such as Month, Hour, etc. * @param start The DateTime to start this period from * @return A new period */ static public DateTimePeriod create(DateTimeDuration duration, DateTime start) { if (duration == DateTimeDuration.YEAR) { return createYear(start); } else if (duration == DateTimeDuration.MONTH) { return createMonth(start); } else if (duration == DateTimeDuration.DAY) { return createDay(start); } else if (duration == DateTimeDuration.HOUR) { return createHour(start); } else if (duration == DateTimeDuration.FIVE_MINUTES) { return createFiveMinutes(start); } else { throw new UnsupportedOperationException("Duration " + duration + " not yet supported"); } } static public DateTimePeriod createYear(int year, DateTimeZone zone) { // year-01-01 00:00:00.000 DateTime start = new DateTime(year, 1, 1, 0, 0, 0, 0, zone); return createYear(start); } static public DateTimePeriod createYear(DateTime start) { DateTime end = start.plusYears(1); return new DateTimeYear(start, end); } static public DateTimePeriod createMonth(int year, int month, DateTimeZone zone) { // year-month-01 00:00:00.000 DateTime start = new DateTime(year, month, 1, 0, 0, 0, 0, zone); return createMonth(start); } static public DateTimePeriod createMonth(DateTime start) { DateTime end = start.plusMonths(1); return new DateTimeMonth(start, end); } static public DateTimePeriod createDay(int year, int month, int day, DateTimeZone zone) { // year-month-day 00:00:00.000 DateTime start = new DateTime(year, month, day, 0, 0, 0, 0, zone); return createDay(start); } static public DateTimePeriod createDay(DateTime start) { DateTime end = start.plusDays(1); return new DateTimeDay(start, end); } static public DateTimePeriod createHour(int year, int month, int day, int hour, DateTimeZone zone) { // year-month-day hour:00:00.000 DateTime start = new DateTime(year, month, day, hour, 0, 0, 0, zone); return createHour(start); } static public DateTimePeriod createHour(DateTime start) { DateTime end = start.plusHours(1); return new DateTimeHour(start, end); } static public DateTimePeriod createFiveMinutes(int year, int month, int day, int hour, int minute, DateTimeZone zone) { // year-month-day hour:00:00.000 DateTime start = new DateTime(year, month, day, hour, minute, 0, 0, zone); return createFiveMinutes(start); } static public DateTimePeriod createFiveMinutes(DateTime start) { DateTime end = start.plusMinutes(5); return new DateTimeFiveMinutes(start, end); } /** * Create a list of DateTimePeriods that represent the last year of * YearMonth periods. For example, if its currently January 2009, this * would return periods representing "January 2009, December 2008, ... February 2008" * @param zone * @return */ static public List createLastYearMonths(DateTimeZone zone) { ArrayList periods = new ArrayList(); // get today's date DateTime now = new DateTime(zone); // start with today's current month and 11 others (last 12 months) for (int i = 0; i < 12; i++) { // create a new period DateTimePeriod period = createMonth(now.getYear(), now.getMonthOfYear(), zone); periods.add(period); // subtract 1 month now = now.minusMonths(1); } return periods; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy