jodd.datetime.JDateTime Maven / Gradle / Ivy
Show all versions of jodd-all Show documentation
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
package jodd.datetime;
import jodd.datetime.format.JdtFormat;
import jodd.datetime.format.JdtFormatter;
import jodd.util.HashCode;
import java.io.Serializable;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import static jodd.util.HashCode.hash;
/**
* Universal all-in-one date and time class that uses Astronomical Julian
* Dates for time calculations. Guaranteed precision for all manipulations
* and calculations is up to 1 ms (0.001 sec).
*
*
* The Julian day or Julian day number (JDN) is the (integer) number of days that
* have elapsed since Monday, January 1, 4713 BC in the proleptic Julian calendar 1.
* That day is counted as Julian day zero. Thus the multiples of 7 are Mondays.
* Negative values can also be used.
*
*
* The Julian Date (JD) is the number of days (with decimal fraction of the day) that
* have elapsed since 12 noon Greenwich Mean Time (UT or TT) of that day.
* Rounding to the nearest integer gives the Julian day number.
*
*
* JDateTime
contains date/time information for current day. By
* default, behaviour and formats are set to ISO standard, although this may
* be changed.
*
*
* JDateTime
can be set in many different ways by using setXxx()
* methods or equivalent constructors. Moreover, date time information may be loaded from an instance
* of any available java date-time class. This functionality can be easily
* enhanced for any custom date/time class. Furthermore, JDateTime
* can be converted to any such date/time class.
*
*
* Rolling dates with JDateTime
is easy. For this
* JDateTime
contains many addXxx()
methods. Time can be added
* or subtracted with any value or more values at once. All combinations are
* valid. Calculations also performs month fixes by default.
*
*
* JDateTime
behaviour is set by several attributes (or
* parameters). Each one contains 2 values: one is the default value, used by
* all instances of JDateTime
and the other one is just for a
* specific instance of JDateTime
. This means that it is
* possible to set behaviour of all instances at once or of one particular
* instance.
*
*
* Bellow is the list of behaviour attributes:
*
*
*
* - monthFix - since months do not have the same number of days, adding
* months and years may be calculated in two different ways: with or
* without month fix. when month fix is on,
JDateTime
will
* additionally fix all time adding and fix the date. For example, adding
* one month to 2003-01-31 will give 2003-02-28 and not 2003-03-03.
* By default, monthFix is turned on and set to true
.
*
*
* - locale - current locale, used for getting names during formatting the
* output string.
*
*
* - timezone - current timezone
*
* - format - is String that describes how time is converted and parsed to
* and from a String. Default format matches ISO standard. An instance of
*
JdtFormatter
parses and uses this template.
*
* - week definition - is used for specifying the definition of the week.
* Week is defined with first day of the week and with the must have day. A
* must have day is a day that must exist in the 1st week of the year. For
* example, default value is Thursday (4) as specified by ISO standard.
* Alternatively, instead of must have day, minimal days of week may be used,
* since this two numbers are in relation.
*
*
*
*
* Optimization: although based on heavy calculations, JDateTime
* works significantly faster then java's Calendar
s. Since
* JDateTime
doesn't use lazy initialization, setXxx() method is
* slower. However, this doesn't have much effect to the global performances:
* settings are usually not used without gettings:) As soon as any other method is
* used (getXxx() or addXxx()) performances of JDateTime
becomes
* significantly better.
*
*
* Year 1582 is (almost:) working: after 1582-10-04 (Thursday) is 1582-10-15 (Friday).
* Users must be aware of this when doing time rolling before across this period.
*
*
* More info: Julian Date on Wikipedia
*/
public class JDateTime implements Comparable, Cloneable, Serializable {
public static final String DEFAULT_FORMAT = "YYYY-MM-DD hh:mm:ss.mss";
// day of week names
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
public static final int THURSDAY = 4;
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;
public static final int SUNDAY = 7;
// month names
public static final int JANUARY = 1;
public static final int FEBRUARY = 2;
public static final int MARCH = 3;
public static final int APRIL = 4;
public static final int MAY = 5;
public static final int JUNE = 6;
public static final int JULY = 7;
public static final int AUGUST = 8;
public static final int SEPTEMBER = 9;
public static final int OCTOBER = 10;
public static final int NOVEMBER = 11;
public static final int DECEMBER = 12;
/**
* {@link DateTimeStamp} for current date.
*/
protected DateTimeStamp time = new DateTimeStamp();
/**
* Day of week, range: [1-7] == [Monday - Sunday]
*/
protected int dayofweek;
/**
* Day of year, range: [1-365] or [1-366]
*/
protected int dayofyear;
/**
* Leap year flag.
*/
protected boolean leap;
/**
* Week of year, range: [1-52] or [1-53]
*/
protected int weekofyear;
/**
* Week of month.
*/
protected int weekofmonth;
/**
* Current Julian Date.
*/
protected JulianDateStamp jdate;
// ---------------------------------------------------------------- some precalculated times
/**
* Julian Date for 1970-01-01T00:00:00 (Thursday).
*/
public static final JulianDateStamp JD_1970 = new JulianDateStamp(2440587, 0.5);
/**
* Julian Date for 2001-01-01T00:00:00 (Monday).
*/
public static final JulianDateStamp JD_2001 = new JulianDateStamp(2451910, 0.5);
// ---------------------------------------------------------------- julian date (CORE)
/**
* Returns current {@link DateTimeStamp}.
* Returned instance is still used internally (i.e. it is not cloned before returning).
*/
public DateTimeStamp getDateTimeStamp() {
return time;
}
/**
* Loads current date time information.
*/
public JDateTime setDateTimeStamp(DateTimeStamp dts) {
return set(dts.year, dts.month, dts.day, dts.hour, dts.minute, dts.second, dts.millisecond);
}
/**
* Sets current Julian Date. This is the core of the JDateTime class and it
* is used by all other classes. This method performs all calculations
* required for whole class.
*
* @param jds current julian date
*/
public JDateTime setJulianDate(JulianDateStamp jds) {
setJdOnly(jds.clone());
calculateAdditionalData();
return this;
}
/**
* Returns {@link JulianDateStamp}. It is the same instance used internally.
*/
public JulianDateStamp getJulianDate() {
return jdate;
}
/**
* Returns JDN. Note that JDN is not equal to integer part of julian date. It is calculated by
* rounding to the nearest integer.
*/
public int getJulianDayNumber() {
return jdate.getJulianDayNumber();
}
/**
* Internal method for calculating various data other then date/time.
*/
private void calculateAdditionalData() {
this.leap = TimeUtil.isLeapYear(time.year);
this.dayofweek = calcDayOfWeek();
this.dayofyear = calcDayOfYear();
this.weekofyear = calcWeekOfYear(firstDayOfWeek, mustHaveDayOfFirstWeek);
this.weekofmonth = calcWeekNumber(time.day, this.dayofweek);
}
/**
* Internal method that just sets the time stamp and not all other additional
* parameters. Used for faster calculations only and only by main core
* set/add methods.
*
* @param jds julian date
*/
private void setJdOnly(JulianDateStamp jds) {
jdate = jds;
time = TimeUtil.fromJulianDate(jds);
}
/**
* Core method that sets date and time. All others set() methods use this
* one. Milliseconds are truncated after 3rd digit.
*
* @param year year to set
* @param month month to set
* @param day day to set
* @param hour hour to set
* @param minute minute to set
* @param second second to set
*/
public JDateTime set(int year, int month, int day, int hour, int minute, int second, int millisecond) {
// fix seconds fractions because of float point arithmetic
//second = ((int) second) + ((int) ((second - (int)second) * 1000 + 1e-9) / 1000.0);
/*
double ms = (second - (int)second) * 1000;
if (ms > 999) {
ms = 999;
} else {
ms += 1.0e-9;
}
second = ((int) second) + ((int) ms / 1000.0);
*/
jdate = TimeUtil.toJulianDate(year, month, day, hour, minute, second, millisecond);
// if given time is valid it means that there is no need to calculate it
// again from already calculated Julian date. however, it is still
// necessary to fix milliseconds to match the value that would be
// calculated as setJulianDate() is used. This fix only deals with the
// time, not doing the complete and more extensive date calculation.
// this means that method works faster when regular date is specified.
/*
if (TimeUtil.isValidDateTime(year, month, day, hour, minute, second, millisecond)) {
int ka = (int)(jdate.fraction + 0.5);
double frac = jdate.fraction + 0.5 - ka + 1.0e-10;
// hour with minute and second included as fraction
double d_hour = frac * 24.0;
// minute with second included as a fraction
double d_minute = (d_hour - (int)d_hour) * 60.0;
second = (d_minute - (int)d_minute) * 60.0;
// fix calculation errors
second = ((int) (second * 10000) + 0.5) / 10000.0;
time.year = year; time.month = month; time.day = day;
time.hour = hour; time.minute = minute; time.second = second;
setParams();
} else {
setJulianDate(jdate);
}
*/
if (TimeUtil.isValidDateTime(year, month, day, hour, minute, second, millisecond)) {
time.year = year; time.month = month; time.day = day;
time.hour = hour; time.minute = minute; time.second = second;
time.millisecond = millisecond;
calculateAdditionalData();
} else {
setJulianDate(jdate);
}
return this;
}
/**
* Sets just Julian Date and no other parameter such as day of week etc.
* Used internally for speed.
*
* @param year year to set
* @param month month to set
* @param day day to set
* @param hour hour to set
* @param minute minute to set
* @param second second to set
*/
private void setJdOnly(int year, int month, int day, int hour, int minute, int second, int millisecond) {
setJdOnly(TimeUtil.toJulianDate(year, month, day, hour, minute, second, millisecond));
}
// ---------------------------------------------------------------- core calculations
/**
* Calculates day of week.
*/
private int calcDayOfWeek() {
int jd = (int)(jdate.doubleValue() + 0.5);
return (jd % 7) + 1;
//return (jd + 1) % 7; // return 0 (Sunday), 1 (Monday),...
}
private static final int[] NUM_DAYS = {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; // 1-based
private static final int[] LEAP_NUM_DAYS = {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; // 1-based
/**
* Calculates day of year.
*/
private int calcDayOfYear() {
if (leap) {
return LEAP_NUM_DAYS[time.month] + time.day;
}
return NUM_DAYS[time.month] + time.day;
}
/**
* Calculates week of year. Based on:
* "Algorithm for Converting Gregorian Dates to ISO 8601 Week Date"
* by Rick McCarty, 1999
*
* @param start first day of week
* @param must must have day of week
*
* @return week of year number
*/
private int calcWeekOfYear(int start, int must) {
// is modification required?
// modification is a fix for the days of year because of the different
// starting day of week. when modification is required, one week is added
// or subtracted to the current day, so calculation of the week of year
// would be correct.
int delta = 0;
if (start <= this.dayofweek) {
if (must < start) {
delta = 7;
}
} else {
if (must >= start) {
delta = -7;
}
}
int jd = (int)(jdate.doubleValue() + 0.5) + delta;
int WeekDay = (jd % 7) + 1;
int time_year = time.year;
int DayOfYearNumber = this.dayofyear + delta;
if (DayOfYearNumber < 1) {
time_year--;
DayOfYearNumber = TimeUtil.isLeapYear(time_year) ? 366 + DayOfYearNumber: 365 + DayOfYearNumber;
} else if (DayOfYearNumber > (this.leap ? 366 : 365)) {
DayOfYearNumber = this.leap ? DayOfYearNumber - 366: DayOfYearNumber - 365;
time_year++;
}
// modification, if required, is finished. proceed to the calculation.
int firstDay = jd - DayOfYearNumber + 1;
int Jan1WeekDay = (firstDay % 7) + 1;
// find if the date falls in YearNumber Y - 1 set WeekNumber to 52 or 53
int YearNumber = time_year;
int WeekNumber = 52;
if ((DayOfYearNumber <= (8 - Jan1WeekDay)) && (Jan1WeekDay > must)) {
YearNumber--;
if ((Jan1WeekDay == must + 1) || ( (Jan1WeekDay == must + 2) && (TimeUtil.isLeapYear(YearNumber)) ) ) {
WeekNumber = 53;
}
}
// set WeekNumber to 1 to 53 if date falls in YearNumber
int m = 365;
if (YearNumber == time_year) {
if (TimeUtil.isLeapYear(time_year)) {
m = 366;
}
if ((m - DayOfYearNumber) < (must - WeekDay)) {
YearNumber = time_year + 1;
WeekNumber = 1;
}
}
if (YearNumber == time_year) {
int n = DayOfYearNumber + (7 - WeekDay) + (Jan1WeekDay - 1);
WeekNumber = n / 7;
if (Jan1WeekDay > must) {
WeekNumber -= 1;
}
}
return WeekNumber;
}
/**
* Return the week number of a day, within a period. This may be the week number in
* a year, or the week number in a month. Usually this will be a value >= 1, but if
* some initial days of the period are excluded from week 1, because
* minimalDaysInFirstWeek is > 1, then the week number will be zero for those
* initial days. Requires the day of week for the given date in order to determine
* the day of week of the first day of the period.
*
* @param dayOfPeriod
* Day-of-year or day-of-month. Should be 1 for first day of period.
* @param dayOfWeek day of week
*
* @return Week number, one-based, or zero if the day falls in part of the
* month before the first week, when there are days before the first
* week because the minimum days in the first week is more than one.
*/
private int calcWeekNumber(int dayOfPeriod, int dayOfWeek) {
// Determine the day of the week of the first day of the period
// in question (either a year or a month). Zero represents the
// first day of the week on this calendar.
int periodStartDayOfWeek = (dayOfWeek - firstDayOfWeek - dayOfPeriod + 1) % 7;
if (periodStartDayOfWeek < 0) {
periodStartDayOfWeek += 7;
}
// Compute the week number. Initially, ignore the first week, which
// may be fractional (or may not be). We add periodStartDayOfWeek in
// order to fill out the first week, if it is fractional.
int weekNo = (dayOfPeriod + periodStartDayOfWeek - 1) / 7;
// If the first week is long enough, then count it. If
// the minimal days in the first week is one, or if the period start
// is zero, we always increment weekNo.
if ((7 - periodStartDayOfWeek) >= minDaysInFirstWeek) {
++weekNo;
}
return weekNo;
}
// ---------------------------------------------------------------- add/sub time
/**
* Adds time to current time. The main add method - all other add() methods
* must use this one.
*
* There are 2 different kinds of addings, when months are added:
*
* - when months are not specially concern. All remaining days will be
* added to the next month. Example: 2003-01-31 + 0-01-0 = 2003-03-03
* - when months addition is fixed, and month is not approximated.
* example: 2003-01-31 + 0-01-0 = 2003-02-28
*
*
* @param year delta year
* @param month delta month
* @param day delta days
* @param hour delta hours
* @param minute delta minutes
* @param second delta seconds
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime add(int year, int month, int day, int hour, int minute, int second, int millisecond, boolean monthFix) {
int difference = 0;
if (trackDST) {
difference = TimeZoneUtil.getOffset(this, timezone);
}
addNoDST(year, month, day, hour, minute, second, millisecond, monthFix);
if (trackDST) {
difference = TimeZoneUtil.getOffset(this, timezone) - difference;
if (difference != 0) {
addNoDST(0, 0, 0, 0, 0, 0, difference, false);
}
}
return this;
}
protected void addNoDST(int year, int month, int day, int hour, int minute, int second, int millisecond, boolean monthFix) {
millisecond += time.millisecond;
second += time.second;
minute += time.minute;
hour += time.hour;
day += time.day;
if (!monthFix) {
month += time.month;
year += time.year;
set(year, month, day, hour, minute, second, millisecond);
} else {
// month fix:
// 1. add all except month and year
// 2. store day value
// 3. add just months
// 4. if new date is not equal to stored, return to last day of previous month
setJdOnly(time.year, time.month, day, hour, minute, second, millisecond);
int from = time.day;
month += time.month + (year * 12); // delta years to add are converted to delta months
setJdOnly(time.year, month, time.day, time.hour, time.minute, time.second, time.millisecond);
if (time.day < from) {
set(time.year, time.month, 0, time.hour, time.minute, time.second, time.millisecond);
} else {
calculateAdditionalData();
}
/*
// 5. store month value
// 6. add just year
// 7. if new month is not equal to stored, return to last day of previous month
from = time.month;
year += time.year;
setJdOnly(year, time.month, time.day, time.hour, time.minute, time.second);
if (time.month > from) {
set(time.year, time.month, 0, time.hour, time.minute, time.second);
}*/
}
}
public JDateTime sub(int year, int month, int day, int hour, int minute, int second, int millisecond, boolean monthFix) {
return add(-year, -month, -day, -hour, -minute, -second, -millisecond, monthFix);
}
/**
* Performs time adding with preset value of monthFix attribute.
*
* @param year delta year
* @param month delta month
* @param day delta days
* @param hour delta hours
* @param minute delta minutes
* @param second delta seconds
*
* @see #add(int, int, int, int, int, int, int, boolean)
*/
public JDateTime add(int year, int month, int day, int hour, int minute, int second, int millisecond) {
return add(year, month, day, hour, minute, second, millisecond, monthFix);
}
public JDateTime sub(int year, int month, int day, int hour, int minute, int second, int millisecond) {
return add(-year, -month, -day, -hour, -minute, -second, millisecond, monthFix);
}
/**
* Adds date, leaving time unchanged.
*
* @param year years to add
* @param month months to add
* @param day days to add
* @param monthFix true
for month fixing, false
otherwise
*
* @see #add(int, int, int, int, int, int, int, boolean)
*/
public JDateTime add(int year, int month, int day, boolean monthFix) {
return add(year, month, day, 0, 0, 0, 0, monthFix);
}
public JDateTime sub(int year, int month, int day, boolean monthFix) {
return add(-year, -month, -day, 0, 0, 0, 0, monthFix);
}
/**
* Adds date, leaving time unchanged, with preset value of monthFix.
* attribute.
*
* @param year years to add
* @param month months to add
* @param day days to add
*
* @see #add(int, int, int, boolean)
*/
public JDateTime add(int year, int month, int day) {
return add(year, month, day, monthFix);
}
public JDateTime sub(int year, int month, int day) {
return add(-year, -month, -day, monthFix);
}
/**
* Adds time.
*
* @param hour hours to add
* @param minute minutes to add
* @param second seconds to add
* @param monthFix true
for month fixing, false
otherwise
*
* @see #add(int, int, int, int, int, int, int)
*/
public JDateTime addTime(int hour, int minute, int second, int millisecond, boolean monthFix) {
return add(0, 0, 0, hour, minute, second, millisecond, monthFix);
}
public JDateTime subTime(int hour, int minute, int second, int millisecond, boolean monthFix) {
return add(0, 0, 0, -hour, -minute, -second, -millisecond, monthFix);
}
public JDateTime addTime(int hour, int minute, int second, boolean monthFix) {
return add(0, 0, 0, hour, minute, second, 0, monthFix);
}
public JDateTime subTime(int hour, int minute, int second, boolean monthFix) {
return add(0, 0, 0, -hour, -minute, -second, 0, monthFix);
}
/**
* Adds time, with preset value of monthFix.
*
* @param hour hours to add
* @param minute minutes to add
* @param second seconds to add
*
* @see #addTime(int, int, int, int, boolean)
*/
public JDateTime addTime(int hour, int minute, int second, int millisecond) {
return addTime(hour, minute, second, millisecond, monthFix);
}
public JDateTime subTime(int hour, int minute, int second, int millisecond) {
return addTime(-hour, -minute, -second, -millisecond, monthFix);
}
public JDateTime addTime(int hour, int minute, int second) {
return addTime(hour, minute, second, 0, monthFix);
}
public JDateTime subTime(int hour, int minute, int second) {
return addTime(-hour, -minute, -second, 0, monthFix);
}
/**
* Adds year.
*
* @param y year to add
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime addYear(int y, boolean monthFix) {
return add(y, 0, 0, monthFix);
}
public JDateTime subYear(int y, boolean monthFix) {
return add(-y, 0, 0, monthFix);
}
/**
* Adds year, with preset value of monthFix.
*
* @param y year to add
*/
public JDateTime addYear(int y) {
return addYear(y, monthFix);
}
public JDateTime subYear(int y) {
return addYear(-y, monthFix);
}
/**
* Adds month.
*
* @param m month to add
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime addMonth(int m, boolean monthFix) {
return add(0, m, 0, monthFix);
}
public JDateTime subMonth(int m, boolean monthFix) {
return add(0, -m, 0, monthFix);
}
/**
* Adds month, with preset value of monthFix.
*
* @param m month to add
*/
public JDateTime addMonth(int m) {
return addMonth(m, monthFix);
}
public JDateTime subMonth(int m) {
return addMonth(-m, monthFix);
}
/**
* Adds days.
*
* @param d days to add
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime addDay(int d, boolean monthFix) {
return add(0, 0, d, monthFix);
}
public JDateTime subDay(int d, boolean monthFix) {
return add(0, 0, -d, monthFix);
}
/**
* Adds days, with preset value of monthFix.
*
* @param d days to add
*/
public JDateTime addDay(int d) {
return addDay(d, monthFix);
}
public JDateTime subDay(int d) {
return addDay(-d, monthFix);
}
/**
* Adds hours.
*
* @param h hours to add
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime addHour(int h, boolean monthFix) {
return addTime(h, 0, 0, 0, monthFix);
}
public JDateTime subHour(int h, boolean monthFix) {
return addTime(-h, 0, 0, 0, monthFix);
}
/**
* Adds hours, with preset value of monthFix.
*
* @param h hours to add
*/
public JDateTime addHour(int h) {
return addHour(h, monthFix);
}
public JDateTime subHour(int h) {
return addHour(-h, monthFix);
}
/**
* Adds minutes.
*
* @param m minutes to add.
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime addMinute(int m, boolean monthFix) {
return addTime(0, m, 0, 0, monthFix);
}
public JDateTime subMinute(int m, boolean monthFix) {
return addTime(0, -m, 0, 0, monthFix);
}
/**
* Adds minutes, with preset value of monthFix.
*
* @param m minutes to add.
*/
public JDateTime addMinute(int m) {
return addMinute(m, monthFix);
}
public JDateTime subMinute(int m) {
return addMinute(-m, monthFix);
}
/**
* Adds seconds.
*
* @param s seconds to add
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime addSecond(int s, boolean monthFix) {
return addTime(0, 0, s, 0, monthFix);
}
public JDateTime subSecond(int s, boolean monthFix) {
return addTime(0, 0, -s, 0, monthFix);
}
/**
* Adds seconds, with preset value of monthFix.
*
* @param s seconds to add
*/
public JDateTime addSecond(int s) {
return addSecond(s, monthFix);
}
public JDateTime subSecond(int s) {
return addSecond(-s, monthFix);
}
/**
* Adds milliseconds.
*
* @param ms milliseconds to add
* @param monthFix true
for month fixing, false
otherwise
*/
public JDateTime addMillisecond(int ms, boolean monthFix) {
return addTime(0, 0, 0, ms, monthFix);
}
public JDateTime subMillisecond(int ms, boolean monthFix) {
return addTime(0, 0, 0, -ms, monthFix);
}
/**
* Adds milliseconds, with preset value of monthFix.
*
* @param ms milliseconds to add
*/
public JDateTime addMillisecond(int ms) {
return addMillisecond(ms, monthFix);
}
public JDateTime subMillisecond(int ms) {
return addMillisecond(-ms, monthFix);
}
// ---------------------------------------------------------------- ctors & sets
/**
* Constructor that set date and time.
*
* @param year year to set
* @param month month to set
* @param day day to set
* @param hour hours to set
* @param minute minutes to set
* @param second seconds to set
* @param millisecond milliseconds to set
*
* @see #set(int, int, int, int, int, int, int)
*/
public JDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) {
this.set(year, month, day, hour, minute, second, millisecond);
}
/**
* Sets date, time is set to midnight (00:00:00.000).
*
* @param year year to set
* @param month month to set
* @param day day to set
*/
public JDateTime set(int year, int month, int day) {
return set(year, month, day, 0, 0, 0, 0);
}
/**
* Constructor that sets just date. Time is set to 00:00:00.
*
* @param year year to set
* @param month month to set
* @param day day to set
*
* @see #set(int, int, int)
*/
public JDateTime(int year, int month, int day) {
this.set(year, month, day);
}
/**
* Sets time, date is unchanged.
*
* @param hour hours to set
* @param minute minutes to set
* @param second seconds to set
*/
public JDateTime setTime(int hour, int minute, int second, int millisecond) {
return set(time.year, time.month, time.day, hour, minute, second, millisecond);
}
/**
* Sets date, time remains unchanged.
*
* @param year year
* @param month month
* @param day day
*/
public JDateTime setDate(int year, int month, int day) {
return set(year, month, day, time.hour, time.minute, time.second, time.millisecond);
}
// ---------------------------------------------------------------- milliseconds
/**
* Constructor that sets current time specified as time in milliseconds, from
* the midnight, January 1, 1970 UTC.
*
* @param millis time in milliseconds, from the midnight, January 1, 1970 UTC
*
* @see #setTimeInMillis(long )
*/
public JDateTime(long millis) {
setTimeInMillis(millis);
}
/**
* Sets the time based on current time in milliseconds. Current time is
* calculated from the midnight, January 1, 1970 UTC.
*
* Calculation is divided in two steps, due to precision issues.
*
* @param millis time in milliseconds, from the midnight, January 1, 1970 UTC
*/
public JDateTime setTimeInMillis(long millis) {
millis += timezone.getOffset(millis);
int integer = (int) (millis / TimeUtil.MILLIS_IN_DAY);
double fraction = (double)(millis % TimeUtil.MILLIS_IN_DAY) / TimeUtil.MILLIS_IN_DAY;
integer += JD_1970.integer;
fraction += JD_1970.fraction;
return setJulianDate(new JulianDateStamp(integer, fraction));
}
/**
* Returns time based on current time in milliseconds. Current time is
* calculated from the midnight, January 1, 1970 UTC.
*
* Due to possible huge values calculation is divided in two steps:
* first for fractional difference, and then for integer parts.
*
* @return time in milliseconds, from the midnight, January 1, 1970 UTC
*/
public long getTimeInMillis() {
double then = (jdate.fraction - JD_1970.fraction) * TimeUtil.MILLIS_IN_DAY;
then += (jdate.integer - JD_1970.integer) * TimeUtil.MILLIS_IN_DAY;
then -= timezone.getOffset((long) then);
then += then > 0 ? 1.0e-6 : -1.0e-6;
return (long) then;
}
// ---------------------------------------------------------------- date/time sets
/**
* Sets current year.
*
* @param y year to set
*/
public JDateTime setYear(int y) {
return setDate(y, time.month, time.day);
}
/**
* Sets current month.
*
* @param m month to set
*/
public JDateTime setMonth(int m) {
return setDate(time.year, m, time.day);
}
/**
* Sets current day of month.
*
* @param d day to set
*/
public JDateTime setDay(int d) {
return setDate(time.year, time.month, d);
}
/**
* Set current hour.
*
* @param h hour to set
*/
public JDateTime setHour(int h) {
return setTime(h, time.minute, time.second, time.millisecond);
}
/**
* Set current minute.
*
* @param m minutes to set
*/
public JDateTime setMinute(int m) {
return setTime(time.hour, m, time.second, time.millisecond);
}
/**
* Sets current second.
*
* @param s seconds and milliseconds to set
*/
public JDateTime setSecond(int s) {
return setTime(time.hour, time.minute, s, time.millisecond);
}
public JDateTime setSecond(int s, int m) {
return setTime(time.hour, time.minute, s, m);
}
/**
* Sets current millisecond.
*
* @param m milliseconds to set
*/
public JDateTime setMillisecond(int m) {
return setTime(time.hour, time.minute, time.second, m);
}
// ---------------------------------------------------------------- date/time gets
/**
* Returns current year.
*/
public int getYear() {
return time.year;
}
/**
* Returns current month.
*/
public int getMonth() {
return time.month;
}
/**
* Returns current day of month.
* @see #getDayOfMonth
*/
public int getDay() {
return time.day;
}
/**
* Returns current day of month.
* @see #getDay
*/
public int getDayOfMonth() {
return time.day;
}
/**
* Returns current hour.
*/
public int getHour() {
return time.hour;
}
/**
* Returns current minutes.
*/
public int getMinute() {
return time.minute;
}
/**
* Return current seconds. For an integer value, just cast the returned
* value.
*/
public int getSecond() {
return time.second;
}
/**
* Returns current milliseconds.
*/
public int getMillisecond() {
return time.millisecond;
}
// ---------------------------------------------------------------- other gets
/**
* Returns current day of week.
*/
public int getDayOfWeek() {
return dayofweek;
}
/**
* Returns current day of year.
*/
public int getDayOfYear() {
return dayofyear;
}
/**
* Returns true
if current year is leap year.
*/
public boolean isLeapYear() {
return leap;
}
/**
* Returns current week of year.
*/
public int getWeekOfYear() {
return weekofyear;
}
/**
* Returns current week of month.
*/
public int getWeekOfMonth() {
return weekofmonth;
}
/**
* Returns the length of the specified month in days.
*/
public int getMonthLength(int month) {
return TimeUtil.getMonthLength(time.year, month, this.leap);
}
/**
* Returns the length of the current month in days.
*/
public int getMonthLength() {
return getMonthLength(time.month);
}
/**
* Returns era: AD(1) or BC(0).
*/
public int getEra() {
return time.year > 0 ? 1 : 0;
}
/**
* Calculates the number of milliseconds of a day.
*/
public int getMillisOfDay() {
return ((((time.hour * 60) + time.minute) * 60) + time.second) * 1000 + time.millisecond;
}
// ---------------------------------------------------------------- current date time
/**
* Sets current local date and time.
*/
public JDateTime setCurrentTime() {
return setTimeInMillis(System.currentTimeMillis());
}
/**
* Constructor that sets current local date and time.
*/
public JDateTime() {
this.setCurrentTime();
}
// ---------------------------------------------------------------- conversion
/**
* Creates JDateTime
from Calendar
.
*/
public JDateTime(Calendar calendar) {
setDateTime(calendar);
}
/**
* Sets current date and time from Calendar
.
*/
public JDateTime setDateTime(Calendar calendar) {
setTimeInMillis(calendar.getTimeInMillis());
changeTimeZone(calendar.getTimeZone());
return this;
}
/**
* Creates JDateTime
from Date
.
*/
public JDateTime(Date date) {
setDateTime(date);
}
/**
* Sets current date and time from Date
.
*/
public JDateTime setDateTime(Date date) {
return setTimeInMillis(date.getTime());
}
/**
* Converts to Date
instance.
*/
public Date convertToDate() {
return new Date(getTimeInMillis());
}
/**
* Converts to Calendar
instance.
*/
public Calendar convertToCalendar() {
Calendar calendar = Calendar.getInstance(getTimeZone());
calendar.setTimeInMillis(getTimeInMillis());
return calendar;
}
/**
* Converts to java.sql.Date
instance.
*/
public java.sql.Date convertToSqlDate() {
return new java.sql.Date(getTimeInMillis());
}
/**
* Converts to Time
instance.
*/
public Time convertToSqlTime() {
return new Time(getTimeInMillis());
}
/**
* Converts to Timestamp
instance.
*/
public Timestamp convertToSqlTimestamp() {
return new Timestamp(getTimeInMillis());
}
// ---------------------------------------------------------------- ctors from conversions
/**
* Creates JDateTime
from DateTimeStamp
.
*/
public JDateTime(DateTimeStamp dts) {
setDateTimeStamp(dts);
}
/**
* Creates JDateTime
from JulianDateStamp
.
*/
public JDateTime(JulianDateStamp jds) {
setJulianDate(jds);
}
/**
* Creates JDateTime
from double
that represents JD.
*/
public JDateTime(double jd) {
setJulianDate(new JulianDateStamp(jd));
}
/**
* Creates JDateTime
from a string.
*/
public JDateTime(String src) {
parse(src);
}
/**
* Creates JDateTime
from a string, using specified template.
*/
public JDateTime(String src, String template) {
parse(src, template);
}
public JDateTime(String src, JdtFormat jdtFormat) {
parse(src, jdtFormat);
}
// ---------------------------------------------------------------- dst
protected boolean trackDST = JDateTimeDefault.trackDST;
public boolean isTrackDST() {
return trackDST;
}
public JDateTime setTrackDST(boolean trackDST) {
this.trackDST = trackDST;
return this;
}
// ---------------------------------------------------------------- monthFix
protected boolean monthFix = JDateTimeDefault.monthFix;
/**
* Returns true
if month fix is active.
*/
public boolean isMonthFix() {
return monthFix;
}
/**
* Sets custom month fix value.
*/
public JDateTime setMonthFix(boolean monthFix) {
this.monthFix = monthFix;
return this;
}
// ---------------------------------------------------------------- timezone
protected TimeZone timezone = JDateTimeDefault.timeZone == null ? TimeZone.getDefault() : JDateTimeDefault.timeZone;
/**
* Changes current timezone. Current time is changed if time zone has been changed.
*/
public JDateTime changeTimeZone(TimeZone timezone) {
long now = getTimeInMillis();
int difference = TimeZoneUtil.getOffsetDifference(now, this.timezone, timezone);
this.timezone = timezone;
if (difference != 0) {
addMillisecond(difference);
}
return this;
}
/**
* Changes time zone. Equivalent to:
* setTimeZone(from); changeTimeZone(to);
*/
public JDateTime changeTimeZone(TimeZone from, TimeZone to) {
this.timezone = from;
changeTimeZone(to);
return this;
}
/**
* Sets time zone without changing the time.
*/
public JDateTime setTimeZone(TimeZone timezone) {
this.timezone = timezone;
return this;
}
/**
* Return currently active time zone.
*/
public TimeZone getTimeZone() {
return timezone;
}
/**
* Returns true
if current date is in
* daylight savings time in the time zone.
*/
public boolean isInDaylightTime() {
long now = getTimeInMillis();
int offset = timezone.getOffset(now);
int rawOffset = timezone.getRawOffset();
return (offset != rawOffset);
}
// ---------------------------------------------------------------- locale
protected Locale locale = JDateTimeDefault.locale == null ? Locale.getDefault() : JDateTimeDefault.locale;
/**
* Sets custom locale.
*/
public JDateTime setLocale(Locale locale) {
this.locale = locale;
return this;
}
/**
* Return currently active locale.
*/
public Locale getLocale() {
return locale;
}
// ---------------------------------------------------------------- format
protected String format = JDateTimeDefault.format;
/**
* Defines default format.
*/
public JDateTime setFormat(String format) {
this.format = format;
return this;
}
/**
* Returns format.
*/
public String getFormat() {
return format;
}
protected JdtFormatter jdtFormatter = JDateTimeDefault.formatter;
/**
* Defines custom formatter.
*/
public JDateTime setJdtFormatter(JdtFormatter jdtFormatter) {
this.jdtFormatter = jdtFormatter;
return this;
}
/**
* Returns actual {@link JdtFormatter}.
*/
public JdtFormatter getJdtFormatter() {
return jdtFormatter;
}
/**
* Sets both format and formatter from provided {@link JdtFormat}.
*/
public JDateTime setJdtFormat(JdtFormat jdtFormat) {
this.format = jdtFormat.getFormat();
this.jdtFormatter = jdtFormat.getFormatter();
return this;
}
// ---------------------------------------------------------------- toString and parse
/**
* Returns string representation of date/time in specified format.
*/
public String toString(String format) {
return jdtFormatter.convert(this, format);
}
/**
* Returns spring representation of current date/time in currently active format.
* @see #getFormat()
*/
@Override
public String toString() {
return jdtFormatter.convert(this, format);
}
public String toString(JdtFormat jdtFormat) {
return jdtFormat.convert(this);
}
/**
* Sets date/time from a string given in provided template.
* @param src string containing date time information
* @param format format template
*/
public void parse(String src, String format) {
setDateTimeStamp(jdtFormatter.parse(src, format));
}
/**
* Sets date/time from a string and currently active template.
* @param src string containing date time information
* @see #getFormat()
*/
public void parse(String src) {
setDateTimeStamp(jdtFormatter.parse(src, format));
}
public void parse(String src, JdtFormat jdtFormat) {
setDateTimeStamp(jdtFormat.parse(src));
}
/**
* Checks if some string represents a valid date using actual template.
*
* @return true
if date is valid, otherwise false
*/
public boolean isValid(String s) {
return isValid(s, format);
}
/**
* Checks if some string represents a valid date using provided template.
*
* @return true
if date is valid, otherwise false
*/
public boolean isValid(String s, String template) {
DateTimeStamp dtsOriginal;
try {
dtsOriginal = jdtFormatter.parse(s, template);
} catch (Exception ignore) {
return false;
}
if (dtsOriginal == null) {
return false;
}
return TimeUtil.isValidDateTime(dtsOriginal);
}
// ---------------------------------------------------------------- week definitions
protected int firstDayOfWeek = JDateTimeDefault.firstDayOfWeek;
protected int mustHaveDayOfFirstWeek = JDateTimeDefault.mustHaveDayOfFirstWeek;
/**
* Defines week definitions.
*
* @param start first day in week, [1-7],
* @param must must have day of the 1st week, [1-7]
*/
public JDateTime setWeekDefinition(int start, int must) {
if ((start >= 1) && (start <= 7)) {
firstDayOfWeek = start;
}
if ((must >= 1) && (must <= 7)) {
mustHaveDayOfFirstWeek = must;
minDaysInFirstWeek = convertMin2Must(firstDayOfWeek, must);
}
return this;
}
/**
* Returns actual the first day of the week.
*/
public int getFirstDayOfWeek() {
return firstDayOfWeek;
}
/**
* Returns actual must have day of the 1st week.
*/
public int getMustHaveDayOfFirstWeek() {
return mustHaveDayOfFirstWeek;
}
// ---------------------------------------------------------------- week definitions (alt)
protected int minDaysInFirstWeek = JDateTimeDefault.minDaysInFirstWeek;
/**
* Returns actual minimal number of days of the first week. It is
* calculated from must have day of the first week.
*
* @return minimal number of days of the first week
*/
public int getMinDaysInFirstWeek() {
return minDaysInFirstWeek;
}
/**
* Defines week alternatively.
*
* @param start first day in week
* @param min minimal days of week
*/
public JDateTime setWeekDefinitionAlt(int start, int min) {
if ((start >= 1) && (start <= 7)) {
firstDayOfWeek = start;
}
if ((min >= 1) && (min <= 7)) {
mustHaveDayOfFirstWeek = convertMin2Must(firstDayOfWeek, min);
minDaysInFirstWeek = min;
}
return this;
}
/**
* Converts minimal day of week to must have day of week.
* Method is symmetrical.
*
* @param start first day of week
* @param min minimal day of week
*
* @return must have day of week
*/
private static int convertMin2Must(int start, int min) {
int must = 8 - min + (start - 1);
if (must > 7) {
must -= 7;
}
return must;
}
// ---------------------------------------------------------------- equals & hashCode
/**
* Compares if two JDateTime instances are equal.
* Comparison precision is 1e-3 seconds.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (this.getClass() != obj.getClass()) {
return false;
}
JDateTime jdt = (JDateTime) obj;
return (this.monthFix == jdt.monthFix) &&
(this.firstDayOfWeek == jdt.firstDayOfWeek) &&
(this.mustHaveDayOfFirstWeek == jdt.mustHaveDayOfFirstWeek) &&
(this.time.equals(jdt.time)) &&
(this.timezone.equals(jdt.timezone));
}
@Override
public int hashCode() {
int result = HashCode.SEED;
result = hash(result, time);
result = hash(result, timezone);
result = hash(result, monthFix);
result = hash(result, firstDayOfWeek);
result = hash(result, mustHaveDayOfFirstWeek);
return result;
}
// ---------------------------------------------------------------- clone
@Override
public JDateTime clone() {
JDateTime jdt = new JDateTime(this.jdate);
jdt.monthFix = this.monthFix;
jdt.timezone = this.timezone;
jdt.locale = this.locale;
jdt.format = this.format;
jdt.jdtFormatter = this.jdtFormatter;
jdt.firstDayOfWeek = this.firstDayOfWeek;
jdt.mustHaveDayOfFirstWeek = this.mustHaveDayOfFirstWeek;
jdt.trackDST = this.trackDST;
return jdt;
}
// ---------------------------------------------------------------- compare
/**
* Compares current JDateTime object with another one, up to 1 millisecond.
*
* @param o JDateTime to compare
*
* @return -1 if the current object is less than the argument, 0 if the argument is
* equal, and 1 if the current object is greater than the argument
*/
public int compareTo(Object o) {
return time.compareTo(((JDateTime) o).getDateTimeStamp());
}
public int compareTo(JDateTime jd) {
return time.compareTo(jd.getDateTimeStamp());
}
/**
* Compares current JDateTime date with another date. Time component is ignored.
*/
public int compareDateTo(JDateTime jd) {
return time.compareDateTo(jd.getDateTimeStamp());
}
/**
* Returns true
if current time is after then provided time.
*/
public boolean isAfter(JDateTime then) {
return time.compareTo((then).getDateTimeStamp()) > 0;
}
/**
* Returns true
if current time is before then provided time.
*/
public boolean isBefore(JDateTime then) {
return time.compareTo((then).getDateTimeStamp()) < 0;
}
/**
* Returns true
if current date is after then provided date.
* Time component is ignored.
*/
public boolean isAfterDate(JDateTime then) {
return time.compareDateTo((then).getDateTimeStamp()) > 0;
}
/**
* Returns true
if current date is before then provided date.
* Time component is ignored.
*/
public boolean isBeforeDate(JDateTime then) {
return time.compareDateTo((then).getDateTimeStamp()) < 0;
}
// ---------------------------------------------------------------- difference
/**
* Returns number of full days between two dates.
*/
public int daysBetween(JDateTime then) {
return this.jdate.daysBetween(then.jdate);
}
/**
* Returns number of full days between two dates.
*/
public int daysBetween(JulianDateStamp then) {
return this.jdate.daysBetween(then);
}
// ---------------------------------------------------------------- alternative representation
/**
* Returns JD as double value.
*/
public double getJulianDateDouble() {
return jdate.doubleValue();
}
/**
* Sets JD.
*/
public JDateTime setJulianDate(double jd) {
return setJulianDate(new JulianDateStamp(jd));
}
// ---------------------------------------------------------------- custom equals
/**
* Returns true
if provided date is equal to current one.
* May be used for date validation test.
*/
public boolean equalsDate(int year, int month, int day) {
return (time.year == year) && (time.month == month) && (time.day == day);
}
/**
* Returns true
if two dates are equal.
* Time component is ignored.
*/
public boolean equalsDate(JDateTime date) {
return time.isEqualDate(date.time);
}
/**
* Returns true
if two times are equal.
* Date component is ignored.
*/
public boolean equalsTime(JDateTime date) {
return time.isEqualTime(date.time);
}
}