DateRepeatImpl.java
/*
* Copyright 2015 gregorygraham.
*
* 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.
*/
package nz.co.gregs.dbvolution.internal.datatypes;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Optional;
import nz.co.gregs.regexi.Regex;
import nz.co.gregs.regexi.RegexReplacement;
import nz.co.gregs.regexi.RegexSplitter;
import nz.co.gregs.regexi.RegexValueFinder;
import org.joda.time.Period;
/**
*
* @author gregorygraham
*/
public class DateRepeatImpl {
private static final String ZERO_DATEREPEAT_STRING = "P0Y0M0D0h0n0.0s";
/**
* Default constructor
*
*/
public DateRepeatImpl() {
}
/**
*
*
* @return the DateRepeat version of Zero
*/
public static String getZeroDateRepeatString() {
return ZERO_DATEREPEAT_STRING;
}
/**
*
* @param original the first date
* @param compareTo the second date
*
* @return the DateRepeat the represents the difference between these 2 dates
*/
@SuppressWarnings("deprecation")
public static String repeatFromTwoDates(Date original, Date compareTo) {
if (original == null || compareTo == null) {
return null;
}
Calendar origCal = GregorianCalendar.getInstance();
origCal.setTime(original);
Calendar compCal = GregorianCalendar.getInstance();
compCal.setTime(compareTo);
int years = origCal.get(Calendar.YEAR) - compCal.get(Calendar.YEAR);
int months = origCal.get(Calendar.MONTH) - compCal.get(Calendar.MONTH);
int days = origCal.get(Calendar.DAY_OF_MONTH) - compCal.get(Calendar.DAY_OF_MONTH);
int hours = origCal.get(Calendar.HOUR_OF_DAY) - compCal.get(Calendar.HOUR_OF_DAY);
int minutes = origCal.get(Calendar.MINUTE) - compCal.get(Calendar.MINUTE);
int seconds = origCal.get(Calendar.SECOND) - compCal.get(Calendar.SECOND);
int millis = origCal.get(Calendar.MILLISECOND) - compCal.get(Calendar.MILLISECOND);
String intervalString = "P" + years + "Y" + months + "M" + days + "D" + hours + "h" + minutes + "n" + seconds + "." + millis + "s";
return intervalString;
}
/**
* Convenience method to convert Period objects into the string representation of a DateRepeat.
*
* @param interval the Period object to convert to DateRepeat format
* @return the DateRepeat equivalent of the Period value
*/
public static String getDateRepeatString(Period interval) {
if (interval == null) {
return null;
}
int years = interval.getYears();
int months = interval.getMonths();
int days = interval.getDays() + interval.getWeeks() * 7;
int hours = interval.getHours();
int minutes = interval.getMinutes();
int millis = interval.getMillis();
double seconds = interval.getSeconds() + (millis / 1000.0);
String intervalString = "P" + years + "Y" + months + "M" + days + "D" + hours + "h" + minutes + "n" + seconds + "s";
return intervalString;
}
/**
*
* @param original the first date
* @param compareTo the second date
*
* @return TRUE if the DateRepeats are the same, otherwise FALSE
*/
public static boolean isEqualTo(String original, String compareTo) {
return compareDateRepeatStrings(original, compareTo) == 0;
}
/**
*
* @param original the first date
* @param compareTo the second date
*
* @return TRUE if the first DateRepeat value is greater than the second,
* otherwise FALSE
*/
public static boolean isGreaterThan(String original, String compareTo) {
return compareDateRepeatStrings(original, compareTo) == 1;
}
/**
*
* @param original the first date
* @param compareTo the second date
*
* @return TRUE if the first DateRepeat value is less than the second,
* otherwise FALSE
*/
public static boolean isLessThan(String original, String compareTo) {
return compareDateRepeatStrings(original, compareTo) == -1;
}
static final private RegexSplitter DATE_REPEAT_SPLITTER = Regex.empty().beginSetIncluding().includeLetters().endSet().toSplitter();
/**
*
* @param original the first date
* @param compareTo the second date
*
* @return -1 if the first DateRepeat is the smallest, 0 if they are equal,
* and 1 if the first is the largest.
*/
public static Integer compareDateRepeatStrings(String original, String compareTo) {
if (original == null || compareTo == null) {
return null;
}
String[] splitOriginal = DATE_REPEAT_SPLITTER.split(original);
String[] splitCompareTo = DATE_REPEAT_SPLITTER.split(compareTo);
for (int i = 1; i < splitCompareTo.length; i++) { // Start at 1 because the first split is empty
double intOriginal = Double.parseDouble(splitOriginal[i]);
double intCompareTo = Double.parseDouble(splitCompareTo[i]);
if (intOriginal > intCompareTo) {
return 1;
}
if (intOriginal < intCompareTo) {
return -1;
}
}
return 0;
}
/**
*
* @param original the first date.
* @param intervalStr the DateRepeat to offset the date.
*
* @return the Date value offset by the DateRepeat value.
*/
public static Date addDateAndDateRepeatString(Date original, String intervalStr) {
if (original == null || intervalStr == null || intervalStr.length() == 0 || original.toString().length() == 0) {
return null;
}
Calendar cal = new GregorianCalendar();
cal.setTime(original);
int years = getYearPart(intervalStr);
int months = getMonthPart(intervalStr);
int days = getDayPart(intervalStr);
int hours = getHourPart(intervalStr);
int minutes = getMinutePart(intervalStr);
int seconds = getSecondPart(intervalStr);
int millis = getMillisecondPart(intervalStr);
cal.add(Calendar.YEAR, years);
cal.add(Calendar.MONTH, months);
cal.add(Calendar.DAY_OF_MONTH, days);
cal.add(Calendar.HOUR, hours);
cal.add(Calendar.MINUTE, minutes);
cal.add(Calendar.SECOND, seconds);
cal.add(Calendar.MILLISECOND, millis);
return cal.getTime();
}
private static final RegexReplacement NORMALISE_DATEREPEAT = Regex.empty().beginSetExcluding().excludeLiterals(".PYMDhns").excludeRange('0', '9').excludeMinus().endSet().oneOrMore().remove();
/**
*
* @param original the first date.
* @param intervalInput the DateRepeat to offset the date.
*
* @return the Date shift backwards (towards the past) by the DateRepeat
* value.
*/
public static Date subtractDateAndDateRepeatString(Date original, String intervalInput) {
if (original == null || intervalInput == null || intervalInput.length() == 0) {
return null;
}
String intervalStr = NORMALISE_DATEREPEAT.replaceAll(intervalInput);
Calendar cal = new GregorianCalendar();
cal.setTime(original);
int years = getYearPart(intervalStr);
int months = getMonthPart(intervalStr);
int days = getDayPart(intervalStr);
int hours = getHourPart(intervalStr);
int minutes = getMinutePart(intervalStr);
int seconds = getSecondPart(intervalStr);
int millis = getMillisecondPart(intervalStr);
cal.add(Calendar.YEAR, -1 * years);
cal.add(Calendar.MONTH, -1 * months);
cal.add(Calendar.DAY_OF_MONTH, -1 * days);
cal.add(Calendar.HOUR, -1 * hours);
cal.add(Calendar.MINUTE, -1 * minutes);
cal.add(Calendar.SECOND, -1 * seconds);
cal.add(Calendar.MILLISECOND, -1 * millis);
return cal.getTime();
}
/**
*
* @param intervalStr the DateRepeat to parse
*
* @return the DateRepeat value represented by the String value
*/
public static Period parseDateRepeatFromGetString(String intervalStr) {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
Period interval = new Period();
interval = interval.withYears(getYearPart(intervalStr));
interval = interval.withMonths(getMonthPart(intervalStr));
interval = interval.withDays(getDayPart(intervalStr));
interval = interval.withHours(getHourPart(intervalStr));
interval = interval.withMinutes(getMinutePart(intervalStr));
interval = interval.withSeconds(getSecondPart(intervalStr));
interval = interval.withMillis(getMillisecondPart(intervalStr));
return interval;
}
/**
*
* @param intervalStr the DateRepeat
*
* @return get the fractional seconds to millisecond precision
* @throws NumberFormatException parsing is used to interpret the seconds so a
* NumberFormatException maybe thrown if the intervalStr is malformed
*/
public static Integer getMillisecondPart(String intervalStr) throws NumberFormatException {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
Double secondsDouble = parseValueDouble(FIND_SECOND_VALUE, intervalStr);
final int secondsInt = secondsDouble.intValue();
final Double millisDouble = secondsDouble * 1000.0 - secondsInt * 1000;
final int millis = millisDouble.intValue();
return millis;
}
/**
*
* @param intervalStr the DateRepeat
*
* @return get the integer and fractional seconds part of the DateRepeat
* @throws NumberFormatException parsing is used to interpret the seconds so a
* NumberFormatException maybe thrown if the intervalStr is malformed
*/
public static Integer getSecondPart(String intervalStr) throws NumberFormatException {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
Double valueOf = parseValueDouble(FIND_SECOND_VALUE, intervalStr);
return valueOf.intValue();
}
/**
*
* @param intervalStr the DateRepeat
*
* @return get the minutes part of the DateRepeat
* @throws NumberFormatException parsing is used to interpret the minutes so a
* NumberFormatException maybe thrown if the intervalStr is malformed
*/
public static Integer getMinutePart(String intervalStr) throws NumberFormatException {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
return parseValue(FIND_MINUTE_VALUE, intervalStr);
}
/**
*
* @param intervalStr the DateRepeat
*
* @return get the hour part of the DateRepeat value
* @throws NumberFormatException parsing is used to interpret the numbers so a
* NumberFormatException maybe thrown if the intervalStr is malformed
*/
public static Integer getHourPart(String intervalStr) throws NumberFormatException {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
return parseValue(FIND_HOUR_VALUE, intervalStr);
}
/**
*
* @param intervalStr the DateRepeat
*
* @return get the day part of the DateRepeat value
* @throws NumberFormatException parsing is used to interpret the numbers so a
* NumberFormatException maybe thrown if the intervalStr is malformed
*/
public static Integer getDayPart(String intervalStr) throws NumberFormatException {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
return parseValue(FIND_DAY_VALUE, intervalStr);
}
/**
*
* @param intervalStr the DateRepeat
*
* @return get the month part of the DateRepeat value
* @throws NumberFormatException parsing is used to interpret the numbers so a
* NumberFormatException maybe thrown if the intervalStr is malformed
*/
public static Integer getMonthPart(String intervalStr) throws NumberFormatException {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
return parseValue(FIND_MONTH_VALUE, intervalStr);
}
/**
*
* @param intervalStr the DateRepeat
*
* @return get the year part of the DateRepeat value
* @throws NumberFormatException parsing is used to interpret the numbers so a
* NumberFormatException maybe thrown if the intervalStr is malformed
*/
public static Integer getYearPart(String intervalStr) throws NumberFormatException {
if (intervalStr == null || intervalStr.length() == 0) {
return null;
}
return parseValue(FIND_YEAR_VALUE, intervalStr);
}
private static Integer parseValue(RegexValueFinder finder, String intervalStr) throws NumberFormatException {
Optional<String> value = finder.getValueFrom(intervalStr);
return value.isPresent() ? Integer.parseInt(value.get()) : null;
}
private static Double parseValueDouble(RegexValueFinder finder, String intervalStr) throws NumberFormatException {
Optional<String> value = finder.getValueFrom(intervalStr);
return value.isPresent() ? Double.parseDouble(value.get()) : null;
}
private static final RegexValueFinder FIND_YEAR_VALUE = Regex.startingAnywhere()
.literal("P")
.beginNamedCapture("value")
.numberLike()
.oneOrMore()
.endNamedCapture()
.literal("Y").returnValueFor("value");
private static final RegexValueFinder FIND_MONTH_VALUE = Regex.startingAnywhere()
.literal("Y")
.beginNamedCapture("value")
.numberLike()
.oneOrMore()
.endNamedCapture()
.literal("M").returnValueFor("value");
private static final RegexValueFinder FIND_DAY_VALUE = Regex.startingAnywhere()
.literal("M")
.beginNamedCapture("value")
.numberLike()
.oneOrMore()
.endNamedCapture()
.literal("D").returnValueFor("value");
private static final RegexValueFinder FIND_HOUR_VALUE = Regex.startingAnywhere()
.literal("D")
.beginNamedCapture("value")
.numberLike()
.oneOrMore()
.endNamedCapture()
.literal("h").returnValueFor("value");
private static final RegexValueFinder FIND_MINUTE_VALUE = Regex.startingAnywhere()
.literal("h")
.beginNamedCapture("value")
.numberLike()
.oneOrMore()
.endNamedCapture()
.literal("n").returnValueFor("value");
private static final RegexValueFinder FIND_SECOND_VALUE = Regex.startingAnywhere()
.literal("n")
.beginNamedCapture("value")
.numberLike()
.oneOrMore()
.endNamedCapture()
.literal("s").returnValueFor("value");
}