jdatetime.java
来自「Struts2 + Spring JPA Hibernate demo.」· Java 代码 · 共 1,806 行 · 第 1/4 页
JAVA
1,806 行
/*
* $Id: JDateTime.java 30 2006-06-08 13:26:40Z wjx $
*/
package com.vegeta.utils.datetime.common;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.HashMap;
import java.util.TimeZone;
import com.vegeta.utils.datetime.converter.CalendarConverter;
import com.vegeta.utils.datetime.converter.DateConverter;
import com.vegeta.utils.datetime.converter.GregorianCalendarConverter;
import com.vegeta.utils.datetime.converter.JDateTimeConverter;
import com.vegeta.utils.datetime.converter.SqlDateConverter;
import com.vegeta.utils.datetime.converter.SqlTimestampConverter;
import com.vegeta.utils.datetime.formatter.DefaultFormatter;
import com.vegeta.utils.datetime.name.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.
*
* <p>
* <a href="JDateTime.java.html"><i>View Source</i></a>
* </p>
*
* @version $Revision: 30 $ $Date: 2006-06-08 21:26:40 +0800 (星期四, 08
* 六月 2006) $
*/
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
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?