📄 jdatetime.java
字号:
package jodd.datetime;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.HashMap;
import java.util.TimeZone;
import jodd.datetime.converters.CalendarConverter;
import jodd.datetime.converters.DateConverter;
import jodd.datetime.converters.GregorianCalendarConverter;
import jodd.datetime.converters.SqlDateConverter;
import jodd.datetime.converters.SqlTimestampConverter;
import jodd.datetime.formatters.DefaultFormatter;
import jodd.datetime.names.EnglishNames;
/**
* Universal all-in-one date and time class that uses Astronomical Julian
* Dates for time calculations. Guarantied precision is up to 0.001 sec. <p>
*
* <code>JDateTime</code> contains date/time information for current day. By
* default, behaviour and formats are set to ISO standard, although this may
* be changed.<p>
*
* <code>JDateTime</code> can be set in many different ways by using setXxx()
* methods or equivalent constructors. Moreover, it may be set from an instance
* of any available java date-time class. This functionality can be easily
* enhanced for any custom date/time class. Furthermore, <code>JDateTime</code>
* can be used for generation of any such date/time class.<p>
*
* Rolling dates with <code>JDateTime</code> is easy. For this
* <code>JDateTime</code> 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.<p>
*
* <code>JDateTime</code> behaviour is set by several attributes (or
* parameters). Each one contains 2 values: one is the default value, used by
* all instances of <code>JDateTime</code> and the other one is just for a
* specific instance of <code>JDateTime</code>. This means that it is
* possible to set behavior of all instances at once or of one particular
* instance.<p>
*
* Bellow is the list of behavior attributes:
*
* <ul>
*
* <li>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, <code>JDateTime</code> 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 <code>true</code>.
* </li>
*
* <li>names - are objects that holds various date's names and text for a
* specific languages. Names are used during formating the output string.
* </li>
*
* <li>format template - is String that describes how time is converted to
* and from a String. Default format matches ISO standard. An instance of
* <code>JdtFormatter</code> parses and uses this template.</li>
*
* <li>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.
* </li>
*
* </ul>
*
* Optimization: although based on heavy calculations, <code>JDateTime</code>
* works significantly faster then java's <code>Calendar</code>s. Since
* <code>JDateTime</code> doesn't use lazy initialization, setXxx() method is
* slower. However, this doesn't have much effect to the global performances:
* settings are not used without gettings. As soon as any other method is
* used (getXxx() or addXxx()) performances of <code>JDateTime</code> becomes
* significantly better.
*
*/
public class JDateTime implements Comparable {
/**
* DateTime stamp for current date.
*
* @see DateTimeStamp
*/
private DateTimeStamp time = new DateTimeStamp();
/**
* Day of week, range: [1-7] == [Monday - Sunday]
*/
private int dayofweek;
/**
* Day of year, range: [1-365] or [1-366]
*/
private int dayofyear;
/**
* Leap year flag.
*/
private boolean leap;
/**
* Week of year, range: [1-52] or [1-53]
*/
private int weekofyear;
private int weekofmonth;
/**
* Julian Date for 1970-01-01.
*/
public static final JulianDateStamp JD_1970 = new JulianDateStamp(2440587.5);
/**
* Current Julian Date.
*/
private JulianDateStamp jdate;
// ---------------------------------------------------------------- julian date (CORE)
/**
* 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 void setJulianDate(JulianDateStamp jds) {
setJdOnly(jds);
setParams();
}
/**
* Internal method for setting various parameters other then date/time.
*/
private void setParams() {
this.leap = TimeUtil.isLeapYear(time.year);
this.dayofweek = calcDayOfWeek();
this.dayofyear = calcDayOfYear();
MONTH_LENGTH[2] = this.leap ? 29 : 28;
this.weekofyear = calcWeekOfYear(getFirstDayOfWeek() ,getMustHaveDayOfFirstWeek());
this.weekofmonth = weekNumber(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 void set(int year, int month, int day, int hour, int minute, double second) {
// fix seconds fractions because of float point arithmetics
//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 += 1e-9;
}
second = ((int) second) + ((int) ms / 1000.0);
jdate = TimeUtil.toJulianDate(year, month, day, hour, minute, second);
// 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)) {
int ka = (int)(jdate.fraction + 0.5);
double frac = jdate.fraction + 0.5 - ka + 1e-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);
}
}
/**
* Sets just Julian Date and no other parameter such as day of week etc. It
* is 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, double second) {
setJdOnly(TimeUtil.toJulianDate(year, month, day, hour, minute, second));
}
/**
* Returns Julian Date stamp.
*
* @return Julian Date stamp
*/
public JulianDateStamp getJulianDate() {
return jdate;
}
// ---------------------------------------------------------------- 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 == true) {
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, n;
if (YearNumber == time_year) {
if (TimeUtil.isLeapYear(time_year) == true) {
m = 366;
}
if ((m - DayOfYearNumber) < (must - WeekDay)) {
YearNumber = time_year + 1;
WeekNumber = 1;
}
}
if (YearNumber == time_year) {
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
*
* @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 final int weekNumber(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 - getFirstDayOfWeek() - 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) >= getMinimalDaysInFirstWeek()) {
++weekNo;
}
return weekNo;
}
// ---------------------------------------------------------------- compare & clone
/**
* Compares current JDateTime object with another one, up to 1 millisecond.
*
* @param gt 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 gt) {
return time.compareTo(((JDateTime)gt).getDateTimeStamp());
}
// ---------------------------------------------------------------- add time
/**
* Adds time to current time. The main add method. All other add() methods
* must use this one. <p>
*
* There are 2 different kind of addings, when months are added:
* <ul>
* <li>when months are not specially concerne, and month is aproximated as 31
* days. example: 2003-01-31 + 0-01-0 = 2003-03-03</li>
* <li>when months addition is fixed, and month is not approximated.
* example: 2003-01-31 + 0-01-0 = 2003-28-02</li>
* </ul>
* <p>
*
* @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 <code>true</code> for month fixing, <code>false</code> otherwise
*/
public void add(int year, int month, int day, int hour, int minute, double second, boolean monthFix) {
second += time.second;
minute += time.minute;
hour += time.hour;
day += time.day;
if (monthFix == false) {
month += time.month;
year += time.year;
set(year, month, day, hour, minute, second);
} 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);
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);
if (time.day < from) {
set(time.year, time.month, 0, time.hour, time.minute, time.second);
} else {
setParams();
}
/*// 5. store month value
// 6. add just year
// 7. if new month is not equal to stored, rturn 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);
}*/
}
}
/**
* 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, double, boolean)
*/
public void add(int year, int month, int day, int hour, int minute, double second) {
add(year, month, day, hour, minute, second, getMonthFix());
}
/**
* Adds date, leaving time unchanged.
*
* @param year years to add
* @param month months to add
* @param day days to add
* @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
*
* @see #add(int, int, int, int, int, double, boolean)
*/
public void add(int year, int month, int day, boolean monthFix) {
add(year, month, day, 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 void add(int year, int month, int day) {
add(year, month, day, getMonthFix());
}
/**
* Adds time.
*
* @param hour hours to add
* @param minute minutes to add
* @param second seconds to add
* @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
*
* @see #add(int, int, int, int, int, double)
*/
public void addTime(int hour, int minute, double second, boolean monthFix) {
add(0, 0, 0, hour, minute, second, 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, double, boolean)
*/
public void addTime(int hour, int minute, double second) {
addTime(hour, minute, second, getMonthFix());
}
/**
* Adds year.
*
* @param y year to add
* @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
*/
public void addYear(int y, boolean monthFix) {
add(y, 0, 0, monthFix);
}
/**
* Adds year, with preset value of monthFix.
*
* @param y year to add
*/
public void addYear(int y) {
addYear(y, getMonthFix());
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -