Commit 0c0f4813 authored by Daniel Eggert's avatar Daniel Eggert
Browse files

extended timestamp parser

parent ddda876c
......@@ -24,1163 +24,1167 @@ import java.util.Date;
/**
* @author Daniel Eggert (daniel.eggert@gfz-potsdam.de)
*
*/
public class Timestamp implements Comparable<Timestamp>, Serializable {
private static final long serialVersionUID = 4399474003723453591L;
public static abstract class TimeUnits {
public static final int UNDEFINED = -1;
public static final int SECONDS = 0;
public static final int MINUTES = 1;
public static final int HOURS = 2;
public static final int DAYS = 3;
public static final int WEEKS = 4;
public static final int MONTHS = 5;
public static final int YEARS = 6;
public static final int DECADES = 7;
public static final int NUM_UNITS = DECADES + 1;
public static final int MINUTE_IN_SECS = 60;
public static final int HOUR_IN_MINUTES = 60;
public static final int HOUR_IN_SECS = MINUTE_IN_SECS * HOUR_IN_MINUTES;
public static final int DAY_IN_HOURS = 24;
public static final int DAY_IN_MINUTES = DAY_IN_HOURS * HOUR_IN_MINUTES;
public static final int DAY_IN_SECS = DAY_IN_HOURS * HOUR_IN_SECS;
public static final int WEEK_IN_DAYS = 7;
public static final int MONTH_IN_DAYS = 30;
public static final int YEAR_IN_DAYS = 365;
public static final int YEAR_IN_MONTHS = 12;
public static final int DECADES_IN_YEARS = 10;
public static String getUnitName(int unit) {
switch (unit) {
case UNDEFINED:
return "Undefined";
case SECONDS:
return "Seconds";
case MINUTES:
return "Minutes";
case HOURS:
return "Hours";
case DAYS:
return "Days";
case WEEKS:
return "Weeks";
case MONTHS:
return "Months";
case YEARS:
return "Years";
case DECADES:
return "Decades";
default:
return "N/A";
}
}
public static long getUnitInsSeconds(int unit) {
switch (unit) {
case SECONDS:
return 1;
case MINUTES:
return MINUTE_IN_SECS;
case HOURS:
return HOUR_IN_SECS;
case DAYS:
return DAY_IN_SECS;
case WEEKS:
return WEEK_IN_DAYS * DAY_IN_SECS;
case MONTHS:
return MONTH_IN_DAYS * DAY_IN_SECS;
case YEARS:
return YEAR_IN_DAYS * DAY_IN_SECS;
case DECADES:
return DECADES_IN_YEARS * YEAR_IN_DAYS * DAY_IN_SECS;
}
return 0;
}
/**
* Returns the center of the given TimeUnit within the next bigger TimeUnit,<br>
* e.g. getMidValue(TimeUnits.MONTHS) returns 6
*
* @param unit
* @return
*/
public static int getMidValue(int unit) {
switch (unit) {
case SECONDS:
return MINUTE_IN_SECS / 2;
case MINUTES:
return HOUR_IN_MINUTES / 2;
case HOURS:
return DAY_IN_HOURS / 2;
case DAYS:
return MONTH_IN_DAYS / 2;
case WEEKS:
return 2;
case MONTHS:
return YEAR_IN_MONTHS / 2;
default:
return 0;
}
}
}
/**
* Parses the given string an returns the corresponding timestamp.<br>
* The only supported formats (for now) are:<br>
* [time only] "HH:mm:ss"<br>
* [date only] "dd.MM.yyyy"<br>
* [full] "HH:mm:ss dd.MM.yyyy"<br>
* [iso8601] "yyyy.MM.ddTHH:mm:ssZ"
*
* @param timeString
* @return
*/
public static Timestamp parse(String dateTimeString) {
final int len = dateTimeString.length();
if (len < 5) {
// parse year only
return parseYearOnly(dateTimeString);
} else if (len == 8) {
if (dateTimeString.contains("|")) {
// parse date with month literal
return parseDateWithMonthLiteral(dateTimeString);
} else {
// parse time only
return parseTime(dateTimeString, new Timestamp());
}
} else if (len == 10) {
// parse date only
return parseDate(dateTimeString);
} else if (len == 19) {
// parse full timestamp
return parseTime(dateTimeString.substring(0, 8), parseDate(dateTimeString.substring(9)));
} else if (len == 20 || len == 24) {
return parseISO8601(dateTimeString);
}
throw new IllegalArgumentException("invalid text string given: " + dateTimeString);
}
/**
* @param dateTimeString
* @return
*/
private static Timestamp parseISO8601(String dateTimeString) {
Timestamp t = new Timestamp();
// parse and set year
t.setYear(Integer.parseInt(dateTimeString.substring(0, 4)));
// parse and set month
t.setMonth(Integer.parseInt(dateTimeString.substring(5, 7)));
// parse and set day
t.setDay(Integer.parseInt(dateTimeString.substring(8, 10)));
// parse and set hour
t.setHour(Integer.parseInt(dateTimeString.substring(11, 13)));
// parse and set minute
t.setMinute(Integer.parseInt(dateTimeString.substring(14, 16)));
// parse and set second
t.setSecond(Integer.parseInt(dateTimeString.substring(17, 19)));
return t;
}
private static Timestamp parseYearOnly(String dateString) {
Timestamp t = new Timestamp();
t.setYear(Integer.parseInt(dateString));
return t;
}
/**
* @param dateTimeString
* @return
*/
private static Timestamp parseDateWithMonthLiteral(String dateTimeString) {
String[] split = dateTimeString.split("\\|");
return new Timestamp(Integer.parseInt(split[1]), getMonthFromLiteral(split[0]), 1);
}
private static Timestamp parseDate(String dateString) {
String[] split = dateString.split("\\D");
if (dateString.contains(".")) {
// day will be first and year will be last
String day = split[0];
split[0] = split[2];
split[2] = day;
}
return new Timestamp(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
}
private static Timestamp parseTime(String timeString, Timestamp timestamp) {
String[] split = timeString.split(":");
timestamp.setHour(Integer.parseInt(split[0]));
timestamp.setMinute(Integer.parseInt(split[1]));
timestamp.setSecond(Integer.parseInt(split[2]));
return timestamp;
}
public static final int JAN = 1;
public static final int FEB = 2;
public static final int MAR = 3;
public static final int APR = 4;
public static final int MAY = 5;
public static final int JUN = 6;
public static final int JUL = 7;
public static final int AUG = 8;
public static final int SEP = 9;
public static final int OCT = 10;
public static final int NOV = 11;
public static final int DEC = 12;
public static final Timestamp NULL = new Timestamp();
public static final Timestamp MIN = new Timestamp(1, 1, 1);
public static final Timestamp MAX = new Timestamp(Short.MAX_VALUE / 2, 12, 31);
private int date = 0;
private int time = 0;
/**
*
*/
public Timestamp(int year, int month, int day, int hour, int minute, int second) {
setYear(year);
setMonth(month);
setDay(day);
setHour(hour);
setMinute(minute);
setSecond(second);
}
public int getTime() {
return time;
}
public int getDate() {
return date;
}
public Timestamp(int... fields) {
int year = 0;
int month = 0, day = 0, hour = 0, minute = 0, second = 0;
switch (fields.length) {
case 6:
second = fields[5];
case 5:
minute = fields[4];
case 4:
hour = fields[3];
case 3:
day = fields[2];
case 2:
month = fields[1];
case 1:
year = fields[0];
break;
}
set(year, month, day, hour, minute, second);
}
public Timestamp() {
}
/**
* Don't use with {@link java.util.Date#getTime()}
*
* @param dateTime
* @see {@link Timestamp#setFromDateTimeLong(long)}, {@link Timestamp#getDateTimeAsLong()}
*/
public Timestamp(long dateTime) {
setFromDateTimeLong(dateTime);
}
public Timestamp(Timestamp toCopy) {
if (toCopy == null) {
return;
}
this.date = toCopy.date;
this.time = toCopy.time;
}
@Override
public int hashCode() {
return (31 + date) * 31 + time;
}
public boolean hasValidDate() {
return date != 0;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Timestamp)) {
return false;
}
if (obj instanceof Timestamp) {
Timestamp ft = (Timestamp) obj;
return this.date == ft.date && this.time == ft.time;
} else {
return false;
}
// return this.compareTo((Timestamp) obj) == 0;
}
public void setYear(int year) {
date = ((year << 16) & (0xffff0000)) | (date & (0xffff));
}
public int getYear() {
return date >> 16;
}
public void setMonth(int month) {
date = ((month << 8) & 0xff00) | (date & (0xffff00ff));
}
public int getMonth() {
return (date >> 8) & (0xff);
}
public void setDay(int day) {
date = (day & 0xff) | (date & (0xffffff00));
}
public int getDay() {
return date & (0xff);
}
public void setHour(int hour) {
time = (hour << 24) | (time & (0x00ffffff));
}
public int getHour() {
return time >> 24;
}
public void setMinute(int minute) {
time = ((minute << 16) & 0xff0000) | (time & (0xff00ffff));
}
public int getMinute() {
return (time >> 16) & (0xff);
}
public void setSecond(int second) {
time = ((second << 8) & 0xff00) | (time & (0xffff00ff));
}
public int getSecond() {
return (time >> 8) & (0xff);
}
public int getDecade() {
return (getYear() / 10) * 10;
}
public Timestamp truncate(int unit) {
if (unit == TimeUnits.DECADES) {
Timestamp dec = new Timestamp();
dec.setYear(getDecade());
return dec;
}
int year = getYear();
int month = 0;
int day = 0;
int hour = 0;
int minute = 0;
int second = 0;
if (unit < TimeUnits.YEARS) {
month = getMonth();
}
if (unit < TimeUnits.MONTHS) {
day = getDay();
if (unit == TimeUnits.WEEKS) {
// truncate week - bind to Mondays
int weekDay = weekDay(year, month, day);
day -= weekDay;
// handle underflow
if (day < 1) {
// we moved to the previous month
if (--month < 1) {
// we moved to the previous year
--year;
month = 12;
}
day += getNumDaysInMonth(year, month);
}
}
}
if (unit < TimeUnits.DAYS) {
hour = getHour();
}
if (unit < TimeUnits.HOURS) {
minute = getMinute();
}
if (unit < TimeUnits.MINUTES) {
second = getSecond();
}
return new Timestamp(year, month, day, hour, minute, second);
}
/**
* Returns a new timestamp instance plus amount units from this date apart.
*
* @param amount
* @param unit
* @return
*/
public Timestamp plus(int amount, int unit) {
Timestamp ts = null;
int year = 0;
int month = 0;
int day = 0;
switch (unit) {
case TimeUnits.SECONDS:
// create jd with time
double jdWithTime = getJulianDayWithTime();
// now add the seconds as day fraction
double secsInDays = amount;
secsInDays /= TimeUnits.DAY_IN_SECS;
jdWithTime += secsInDays;
return new Timestamp().setFromJulianDayWithTime(jdWithTime);
case TimeUnits.MINUTES:
// make seconds from minutes
amount *= TimeUnits.MINUTE_IN_SECS;
return plus(amount, TimeUnits.SECONDS);
case TimeUnits.HOURS:
// make seconds from hours
amount *= TimeUnits.HOUR_IN_SECS;
return plus(amount, TimeUnits.SECONDS);
case TimeUnits.DAYS:
int jd = getJulianDay();
ts = fromJulianDay(jd + amount);
// copy time
ts.time = this.time;
return ts;
case TimeUnits.WEEKS:
// make days from week
amount *= TimeUnits.WEEK_IN_DAYS;
return plus(amount, TimeUnits.DAYS);
case TimeUnits.MONTHS:
year = getYear();
month = getMonth();
day = getDay();
if (amount > 12) {
year += amount / 12;
amount = amount % 12;
}
month += amount;
// handle overflow
if (month > 12) {
++year;
month -= 12;
}
// handle num days per month
if (day > 28) {
day = Math.min(day, getNumDaysInMonth(year, month));
}
ts = new Timestamp(year, month, day);
ts.time = this.time;
return ts;
case TimeUnits.YEARS:
year = getYear() + amount;
month = getMonth();
day = getDay();
// handle leap years
if (month == FEB && day == 29) {
day = Math.min(day, getNumDaysInMonth(year, month));
}
ts = new Timestamp(year, month, day);
ts.time = this.time;
return ts;
case TimeUnits.DECADES:
// make decades years
amount *= TimeUnits.DECADES_IN_YEARS;
return plus(amount, TimeUnits.YEARS);
}
return ts;
}
/**
* Set this timestamp to the same values as the other
*
* @param other
*/
public void set(Timestamp other) {
this.date = other.date;
this.time = other.time;
}
public double getJulianDayWithTime() {
return appendTimeToJulianDay(julianDay(getYear(), getMonth(), getDay()), getHour(), getMinute(), getSecond());
}
public int getJulianDay() {
return julianDay(getYear(), getMonth(), getDay());
}
public Timestamp setFromJulianDayWithTime(double jdWithTime) {
int jd = (int) jdWithTime;
setFromJulianDay(jd);
// remove date part which leaves the time fraction
setTimeFromDayFraction(jdWithTime - jd);
return this;
}
public void set(int year, int month, int day, int hour, int minute, int second) {
setYear(year);
setMonth(month);
setDay(day);
setHour(hour);
setMinute(minute);
setSecond(second);
}
public int getCalendarWeek() {
return calendarWeek(getYear(), getMonth(), getDay());
}
public static Timestamp now() {
return fromDate(new Date());
}
/**
* Calendar week 1..54
*
* @return
*/
public static int calendarWeek(int year, int month, int day) {
// what weekday is the first day of the year
int firstWeekDay = weekDay(year, JAN, 1);
int dayOfYear = dayOfYear(year, month, day);
return 1 + (dayOfYear + firstWeekDay - 1) / 7;
}
public int getDayOfYear() {
return dayOfYear(getYear(), getMonth(), getDay());
}