0001: // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
0002:
0003: package jodd.datetime;
0004:
0005: import java.util.Calendar;
0006: import java.util.TimeZone;
0007: import java.util.Locale;
0008:
0009: import jodd.datetime.converter.JdtConverter;
0010: import jodd.datetime.formatter.DefaultFormatter;
0011: import jodd.datetime.formatter.JdtFormatter;
0012: import jodd.util.ObjectUtil;
0013: import jodd.util.HashCodeUtil;
0014:
0015: /**
0016: * Universal all-in-one date and time class that uses Astronomical Julian
0017: * Dates for time calculations. Guaranteed precision for all manipulations
0018: * and calculations is up to 1 ms (0.001 sec).
0019: *
0020: * <p>
0021: * The Julian day or Julian day number (JDN) is the (integer) number of days that
0022: * have elapsed since Monday, January 1, 4713 BC in the proleptic Julian calendar 1.
0023: * That day is counted as Julian day zero. Thus the multiples of 7 are Mondays.
0024: * Negative values can also be used.
0025: *
0026: * <p>
0027: * The Julian Date (JD) is the number of days (with decimal fraction of the day) that
0028: * have elapsed since 12 noon Greenwich Mean Time (UT or TT) of that day.
0029: * Rounding to the nearest integer gives the Julian day number.
0030: *
0031: * <p>
0032: * <code>JDateTime</code> contains date/time information for current day. By
0033: * default, behavior and formats are set to ISO standard, although this may
0034: * be changed.
0035: *
0036: * <p>
0037: * <code>JDateTime</code> can be set in many different ways by using <code>setXxx()</code>
0038: * methods or equivalent constructors. Moreover, date time information may be loaded from an instance
0039: * of any available java date-time class. This functionality can be easily
0040: * enhanced for any custom date/time class. Furthermore, <code>JDateTime</code>
0041: * can be converted to any such date/time class.
0042: *
0043: * <p>
0044: * Rolling dates with <code>JDateTime</code> is easy. For this
0045: * <code>JDateTime</code> contains many <code>addXxx()</code> methods. Time can be added
0046: * or subtracted with any value or more values at once. All combinations are
0047: * valid. Calculations also performs month fixes by default.
0048: *
0049: * <p>
0050: * <code>JDateTime</code> behavior is set by several attributes (or
0051: * parameters). Each one contains 2 values: one is the default value, used by
0052: * all instances of <code>JDateTime</code> and the other one is just for a
0053: * specific instance of <code>JDateTime</code>. This means that it is
0054: * possible to set behavior of all instances at once or of one particular
0055: * instance.
0056: *
0057: * <p>
0058: * Bellow is the list of behavior attributes:
0059: *
0060: * <ul>
0061: *
0062: * <li>monthFix - since months do not have the same number of days, adding
0063: * months and years may be calculated in two different ways: with or
0064: * without month fix. when month fix is on, <code>JDateTime</code> will
0065: * additionally fix all time adding and fix the date. For example, adding
0066: * one month to 2003-01-31 will give 2003-02-28 and not 2003-03-03.
0067: * By default, monthFix is turned on and set to <code>true</code>.
0068: * </li>
0069: *
0070: * <li>locale - current locale, used for getting names during formating the
0071: * output string.
0072: * </li>
0073: *
0074: * <li>timezone - current timezone</li>
0075: *
0076: * <li>format - is String that describes how time is converted and parsed to
0077: * and from a String. Default format matches ISO standard. An instance of
0078: * <code>JdtFormatter</code> parses and uses this template.</li>
0079: *
0080: * <li>week definition - is used for specifying the definition of the week.
0081: * Week is defined with first day of the week and with the must have day. A
0082: * must have day is a day that must exist in the 1st week of the year. For
0083: * example, default value is Thursday (4) as specified by ISO standard.
0084: * Alternatively, instead of must have day, minimal days of week may be used,
0085: * since this two numbers are in relation.
0086: * </li>
0087: *
0088: * </ul>
0089: *
0090: * Optimization: although based on heavy calculations, <code>JDateTime</code>
0091: * works significantly faster then java's <code>Calendar</code>s. Since
0092: * <code>JDateTime</code> doesn't use lazy initialization, setXxx() method is
0093: * slower. However, this doesn't have much effect to the global performances:
0094: * settings are usually not used without gettings:) As soon as any other method is
0095: * used (getXxx() or addXxx()) performances of <code>JDateTime</code> becomes
0096: * significantly better.
0097: *
0098: * <p>
0099: * Year 1582 is (almost:) working: after 1582-10-04 (Thursday) is 1582-10-15 (Friday).
0100: * Users must be aware of this when doing time rolling before across this period.
0101: *
0102: * <p>
0103: * More info: <a href="http://en.wikipedia.org/wiki/Julian_Date">Julian Date on Wikipedia</a>
0104: */
0105: public class JDateTime implements Comparable, Cloneable {
0106:
0107: /**
0108: * {@link DateTimeStamp} for current date.
0109: */
0110: protected DateTimeStamp time = new DateTimeStamp();
0111:
0112: /**
0113: * Day of week, range: [1-7] == [Monday - Sunday]
0114: */
0115: protected int dayofweek;
0116:
0117: /**
0118: * Day of year, range: [1-365] or [1-366]
0119: */
0120: protected int dayofyear;
0121:
0122: /**
0123: * Leap year flag.
0124: */
0125: protected boolean leap;
0126:
0127: /**
0128: * Week of year, range: [1-52] or [1-53]
0129: */
0130: protected int weekofyear;
0131:
0132: /**
0133: * Week of month.
0134: */
0135: protected int weekofmonth;
0136:
0137: /**
0138: * Current Julian Date.
0139: */
0140: protected JulianDateStamp jdate;
0141:
0142: // ---------------------------------------------------------------- misc precalculated times
0143:
0144: /**
0145: * Julian Date for 1970-01-01T00:00:00 (Thursday).
0146: */
0147: public static final JulianDateStamp JD_1970 = new JulianDateStamp(
0148: 2440587.5);
0149:
0150: /**
0151: * Julian Date for 2001-01-01T00:00:00 (Monday).
0152: */
0153: public static final JulianDateStamp JD_2001 = new JulianDateStamp(
0154: 2451910.5);
0155:
0156: /**
0157: * The Modified Julian Day (MJD) is the number of days (with decimal fraction of the day)
0158: * that have elapsed since midnight at the beginning of Wednesday November 17, 1858.
0159: */
0160: public static final JulianDateStamp JD_MJD = new JulianDateStamp(
0161: 2400000.5);
0162:
0163: /**
0164: * The Reduced Julian Day (RJD) is also used by astronomers and counts days
0165: * from the same day as MJD, but from noon.
0166: */
0167: public static final JulianDateStamp JD_RJD = new JulianDateStamp(
0168: 2400000);
0169:
0170: /**
0171: * The Truncated Julian Day (TJD) was introduced by NASA for the space program.
0172: * TJD began at midnight at the beginning of May 24, 1968 (Friday).
0173: */
0174: public static final JulianDateStamp JD_TJD = new JulianDateStamp(
0175: 2440000.5);
0176:
0177: // ---------------------------------------------------------------- julian date (CORE)
0178:
0179: /**
0180: * Returns current {@link DateTimeStamp}.
0181: * Returned instance is still used internally (i.e. it is not clonned before returning).
0182: */
0183: public DateTimeStamp getDateTimeStamp() {
0184: return time;
0185: }
0186:
0187: /**
0188: * Loads current date time information.
0189: */
0190: public void setDateTimeStamp(DateTimeStamp dts) {
0191: set(dts.year, dts.month, dts.day, dts.hour, dts.minute,
0192: dts.second);
0193: }
0194:
0195: /**
0196: * Sets current Julian Date. This is the core of the JDateTime class and it
0197: * is used by all other classes. This method performs all calculations
0198: * required for whole class.
0199: *
0200: * @param jds current julian date
0201: */
0202: public void setJulianDate(JulianDateStamp jds) {
0203: setJdOnly((JulianDateStamp) jds.clone());
0204: setParams();
0205: }
0206:
0207: /**
0208: * Returns {@link JulianDateStamp}. It is the same instance used internally.
0209: */
0210: public JulianDateStamp getJulianDate() {
0211: return jdate;
0212: }
0213:
0214: /**
0215: * Internal method for setting various parameters other then date/time.
0216: */
0217: private void setParams() {
0218: this .leap = TimeUtil.isLeapYear(time.year);
0219: this .dayofweek = calcDayOfWeek();
0220: this .dayofyear = calcDayOfYear();
0221: this .weekofyear = calcWeekOfYear(getFirstDayOfWeek(),
0222: getMustHaveDayOfFirstWeek());
0223: this .weekofmonth = weekNumber(time.day, this .dayofweek);
0224: }
0225:
0226: /**
0227: * Internal method that just sets the time stamp and not all other additional
0228: * parameters. Used for faster calculations only and only by main core
0229: * set/add methods.
0230: *
0231: * @param jds julian date
0232: */
0233: private void setJdOnly(JulianDateStamp jds) {
0234: jdate = jds;
0235: time = TimeUtil.fromJulianDate(jds);
0236: }
0237:
0238: /**
0239: * Core method that sets date and time. All others set() methods use this
0240: * one. Milliseconds are truncated after 3rd digit.
0241: *
0242: * @param year year to set
0243: * @param month month to set
0244: * @param day day to set
0245: * @param hour hour to set
0246: * @param minute minute to set
0247: * @param second second to set
0248: */
0249: public void set(int year, int month, int day, int hour, int minute,
0250: double second) {
0251:
0252: // fix seconds fractions because of float point arithmetics
0253: //second = ((int) second) + ((int) ((second - (int)second) * 1000 + 1e-9) / 1000.0);
0254: double ms = (second - (int) second) * 1000;
0255: if (ms > 999) {
0256: ms = 999;
0257: } else {
0258: ms += 1e-9;
0259: }
0260: second = ((int) second) + ((int) ms / 1000.0);
0261: jdate = TimeUtil.toJulianDate(year, month, day, hour, minute,
0262: second);
0263:
0264: // if given time is valid it means that there is no need to calculate it
0265: // again from already calculated julian date. however, it is still
0266: // necessary to fix milliseconds to match the value that would be
0267: // calculated as setJulianDate() is used. This fix only deals with the
0268: // time, not doing the complete and more extensive date calculation.
0269: // this means that method works faster when regular date is specified.
0270: if (TimeUtil.isValidDateTime(year, month, day, hour, minute,
0271: second)) {
0272:
0273: int ka = (int) (jdate.fraction + 0.5);
0274: double frac = jdate.fraction + 0.5 - ka + 1e-10;
0275:
0276: // hour with minute and second included as fraction
0277: double d_hour = frac * 24.0;
0278:
0279: // minute with second included as a fraction
0280: double d_minute = (d_hour - (int) d_hour) * 60.0;
0281:
0282: second = (d_minute - (int) d_minute) * 60.0;
0283:
0284: // fix calculation errors
0285: second = ((int) (second * 10000) + 0.5) / 10000.0;
0286:
0287: time.year = year;
0288: time.month = month;
0289: time.day = day;
0290: time.hour = hour;
0291: time.minute = minute;
0292: time.second = second;
0293: setParams();
0294: } else {
0295: setJulianDate(jdate);
0296: }
0297: }
0298:
0299: /**
0300: * Sets just Julian Date and no other parameter such as day of week etc.
0301: * Used internally for speed.
0302: *
0303: * @param year year to set
0304: * @param month month to set
0305: * @param day day to set
0306: * @param hour hour to set
0307: * @param minute minute to set
0308: * @param second second to set
0309: */
0310: private void setJdOnly(int year, int month, int day, int hour,
0311: int minute, double second) {
0312: setJdOnly(TimeUtil.toJulianDate(year, month, day, hour, minute,
0313: second));
0314: }
0315:
0316: // ---------------------------------------------------------------- core calculations
0317:
0318: /**
0319: * Calculates day of week.
0320: */
0321: private int calcDayOfWeek() {
0322: int jd = (int) (jdate.doubleValue() + 0.5);
0323: return (jd % 7) + 1;
0324: //return (jd + 1) % 7; // return 0 (Sunday), 1 (Monday),...
0325: }
0326:
0327: private static final int NUM_DAYS[] = { -1, 0, 31, 59, 90, 120,
0328: 151, 181, 212, 243, 273, 304, 334 }; // 1-based
0329: private static final int LEAP_NUM_DAYS[] = { -1, 0, 31, 60, 91,
0330: 121, 152, 182, 213, 244, 274, 305, 335 }; // 1-based
0331:
0332: /**
0333: * Calculates day of year.
0334: */
0335: private int calcDayOfYear() {
0336: if (leap == true) {
0337: return LEAP_NUM_DAYS[time.month] + time.day;
0338: }
0339: return NUM_DAYS[time.month] + time.day;
0340: }
0341:
0342: /**
0343: * Calculates week of year. Based on:
0344: * "Algorithm for Converting Gregorian Dates to ISO 8601 Week Date"
0345: * by Rick McCarty, 1999
0346: *
0347: * @param start first day of week
0348: * @param must must have day of week
0349: *
0350: * @return week of year number
0351: */
0352: private int calcWeekOfYear(int start, int must) {
0353:
0354: // is modification required?
0355: // modification is a fix for the days of year because of the different
0356: // starting day of week. when modification is required, one week is added
0357: // or subtracted to the current day, so calculation of the week of year
0358: // would be correct.
0359: int delta = 0;
0360: if (start <= this .dayofweek) {
0361: if (must < start) {
0362: delta = 7;
0363: }
0364: } else {
0365: if (must >= start) {
0366: delta = -7;
0367: }
0368: }
0369:
0370: int jd = (int) (jdate.doubleValue() + 0.5) + delta;
0371: int WeekDay = (jd % 7) + 1;
0372:
0373: int time_year = time.year;
0374: int DayOfYearNumber = this .dayofyear + delta;
0375: if (DayOfYearNumber < 1) {
0376: time_year--;
0377: DayOfYearNumber = TimeUtil.isLeapYear(time_year) ? 366 + DayOfYearNumber
0378: : 365 + DayOfYearNumber;
0379: } else if (DayOfYearNumber > (this .leap ? 366 : 365)) {
0380: DayOfYearNumber = this .leap ? DayOfYearNumber - 366
0381: : DayOfYearNumber - 365;
0382: time_year++;
0383: }
0384:
0385: // modification, if required, is finished. proceed to the calculation.
0386:
0387: int firstDay = jd - DayOfYearNumber + 1;
0388: int Jan1WeekDay = (firstDay % 7) + 1;
0389:
0390: // find if the date falls in YearNumber Y - 1 set WeekNumber to 52 or 53
0391: int YearNumber = time_year;
0392: int WeekNumber = 52;
0393: if ((DayOfYearNumber <= (8 - Jan1WeekDay))
0394: && (Jan1WeekDay > must)) {
0395: YearNumber--;
0396: if ((Jan1WeekDay == must + 1)
0397: || ((Jan1WeekDay == must + 2) && (TimeUtil
0398: .isLeapYear(YearNumber)))) {
0399: WeekNumber = 53;
0400: }
0401: }
0402:
0403: // set WeekNumber to 1 to 53 if date falls in YearNumber
0404: int m = 365;
0405: if (YearNumber == time_year) {
0406: if (TimeUtil.isLeapYear(time_year) == true) {
0407: m = 366;
0408: }
0409: if ((m - DayOfYearNumber) < (must - WeekDay)) {
0410: YearNumber = time_year + 1;
0411: WeekNumber = 1;
0412: }
0413: }
0414:
0415: if (YearNumber == time_year) {
0416: int n = DayOfYearNumber + (7 - WeekDay) + (Jan1WeekDay - 1);
0417: WeekNumber = n / 7;
0418: if (Jan1WeekDay > must) {
0419: WeekNumber -= 1;
0420: }
0421: }
0422: return WeekNumber;
0423: }
0424:
0425: /**
0426: * Return the week number of a day, within a period. This may be the week number in
0427: * a year, or the week number in a month. Usually this will be a value >= 1, but if
0428: * some initial days of the period are excluded from week 1, because
0429: * minimalDaysInFirstWeek is > 1, then the week number will be zero for those
0430: * initial days. Requires the day of week for the given date in order to determine
0431: * the day of week of the first day of the period.
0432: *
0433: * @param dayOfPeriod
0434: * Day-of-year or day-of-month. Should be 1 for first day of period.
0435: * @param dayOfWeek
0436: *
0437: * @return Week number, one-based, or zero if the day falls in part of the
0438: * month before the first week, when there are days before the first
0439: * week because the minimum days in the first week is more than one.
0440: */
0441: private int weekNumber(int dayOfPeriod, int dayOfWeek) {
0442: // Determine the day of the week of the first day of the period
0443: // in question (either a year or a month). Zero represents the
0444: // first day of the week on this calendar.
0445: int periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek()
0446: - dayOfPeriod + 1) % 7;
0447: if (periodStartDayOfWeek < 0) {
0448: periodStartDayOfWeek += 7;
0449: }
0450:
0451: // Compute the week number. Initially, ignore the first week, which
0452: // may be fractional (or may not be). We add periodStartDayOfWeek in
0453: // order to fill out the first week, if it is fractional.
0454: int weekNo = (dayOfPeriod + periodStartDayOfWeek - 1) / 7;
0455:
0456: // If the first week is long enough, then count it. If
0457: // the minimal days in the first week is one, or if the period start
0458: // is zero, we always increment weekNo.
0459: if ((7 - periodStartDayOfWeek) >= getMinDaysInFirstWeek()) {
0460: ++weekNo;
0461: }
0462:
0463: return weekNo;
0464: }
0465:
0466: // ---------------------------------------------------------------- add time
0467:
0468: /**
0469: * Adds time to current time. The main add method. All other add() methods
0470: * must use this one. <p>
0471: *
0472: * There are 2 different kind of addings, when months are added:
0473: * <ul>
0474: * <li>when months are not specially concern, and month is aproximated as 31
0475: * days. example: 2003-01-31 + 0-01-0 = 2003-03-03</li>
0476: * <li>when months addition is fixed, and month is not approximated.
0477: * example: 2003-01-31 + 0-01-0 = 2003-28-02</li>
0478: * </ul>
0479: * <p>
0480: *
0481: * @param year delta year
0482: * @param month delta month
0483: * @param day delta days
0484: * @param hour delta hours
0485: * @param minute delta minutes
0486: * @param second delta seconds
0487: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0488: */
0489:
0490: public void add(int year, int month, int day, int hour, int minute,
0491: double second, boolean monthFix) {
0492: second += time.second;
0493: minute += time.minute;
0494: hour += time.hour;
0495: day += time.day;
0496: if (monthFix == false) {
0497: month += time.month;
0498: year += time.year;
0499: set(year, month, day, hour, minute, second);
0500: } else {
0501: // month fix:
0502: // 1. add all except month and year
0503: // 2. store day value
0504: // 3. add just months
0505: // 4. if new date is not equal to stored, return to last day of previous month
0506: setJdOnly(time.year, time.month, day, hour, minute, second);
0507: int from = time.day;
0508: month += time.month + (year * 12); // delta years to add are converted to delta months
0509: setJdOnly(time.year, month, time.day, time.hour,
0510: time.minute, time.second);
0511: if (time.day < from) {
0512: set(time.year, time.month, 0, time.hour, time.minute,
0513: time.second);
0514: } else {
0515: setParams();
0516: }
0517:
0518: /*
0519: // 5. store month value
0520: // 6. add just year
0521: // 7. if new month is not equal to stored, return to last day of previous month
0522: from = time.month;
0523: year += time.year;
0524: setJdOnly(year, time.month, time.day, time.hour, time.minute, time.second);
0525: if (time.month > from) {
0526: set(time.year, time.month, 0, time.hour, time.minute, time.second);
0527: }*/
0528: }
0529: }
0530:
0531: /**
0532: * Performs time adding with preset value of monthFix attribute.
0533: *
0534: * @param year delta year
0535: * @param month delta month
0536: * @param day delta days
0537: * @param hour delta hours
0538: * @param minute delta minutes
0539: * @param second delta seconds
0540: *
0541: * @see #add(int, int, int, int, int, double, boolean)
0542: */
0543: public void add(int year, int month, int day, int hour, int minute,
0544: double second) {
0545: add(year, month, day, hour, minute, second, getMonthFix());
0546: }
0547:
0548: /**
0549: * Adds date, leaving time unchanged.
0550: *
0551: * @param year years to add
0552: * @param month months to add
0553: * @param day days to add
0554: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0555: *
0556: * @see #add(int, int, int, int, int, double, boolean)
0557: */
0558: public void add(int year, int month, int day, boolean monthFix) {
0559: add(year, month, day, 0, 0, 0, monthFix);
0560: }
0561:
0562: /**
0563: * Adds date, leaving time unchanged, with preset value of monthFix.
0564: * attribute.
0565: *
0566: * @param year years to add
0567: * @param month months to add
0568: * @param day days to add
0569: *
0570: * @see #add(int, int, int, boolean)
0571: */
0572: public void add(int year, int month, int day) {
0573: add(year, month, day, getMonthFix());
0574: }
0575:
0576: /**
0577: * Adds time.
0578: *
0579: * @param hour hours to add
0580: * @param minute minutes to add
0581: * @param second seconds to add
0582: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0583: *
0584: * @see #add(int, int, int, int, int, double)
0585: */
0586: public void addTime(int hour, int minute, double second,
0587: boolean monthFix) {
0588: add(0, 0, 0, hour, minute, second, monthFix);
0589: }
0590:
0591: /**
0592: * Adds time, with preset value of monthFix.
0593: *
0594: * @param hour hours to add
0595: * @param minute minutes to add
0596: * @param second seconds to add
0597: *
0598: * @see #addTime(int, int, double, boolean)
0599: */
0600: public void addTime(int hour, int minute, double second) {
0601: addTime(hour, minute, second, getMonthFix());
0602: }
0603:
0604: /**
0605: * Adds year.
0606: *
0607: * @param y year to add
0608: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0609: */
0610: public void addYear(int y, boolean monthFix) {
0611: add(y, 0, 0, monthFix);
0612: }
0613:
0614: /**
0615: * Adds year, with preset value of monthFix.
0616: *
0617: * @param y year to add
0618: */
0619: public void addYear(int y) {
0620: addYear(y, getMonthFix());
0621: }
0622:
0623: /**
0624: * Adds month.
0625: *
0626: * @param m month to add
0627: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0628: */
0629: public void addMonth(int m, boolean monthFix) {
0630: add(0, m, 0, monthFix);
0631: }
0632:
0633: /**
0634: * Adds month, with preset value of monthFix.
0635: *
0636: * @param m month to add
0637: */
0638: public void addMonth(int m) {
0639: addMonth(m, getMonthFix());
0640: }
0641:
0642: /**
0643: * Adds days.
0644: *
0645: * @param d days to add
0646: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0647: */
0648: public void addDay(int d, boolean monthFix) {
0649: add(0, 0, d, monthFix);
0650: }
0651:
0652: /**
0653: * Adds days, with preset value of monthFix.
0654: *
0655: * @param d days to add
0656: */
0657: public void addDay(int d) {
0658: addDay(d, getMonthFix());
0659: }
0660:
0661: /**
0662: * Adds hours.
0663: *
0664: * @param h hours to add
0665: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0666: */
0667: public void addHour(int h, boolean monthFix) {
0668: addTime(h, 0, 0, monthFix);
0669: }
0670:
0671: /**
0672: * Adds hours, with preset value of monthFix.
0673: *
0674: * @param h hours to add
0675: */
0676: public void addHour(int h) {
0677: addHour(h, getMonthFix());
0678: }
0679:
0680: /**
0681: * Adds minutes.
0682: *
0683: * @param m minutes to add.
0684: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0685: */
0686: public void addMinute(int m, boolean monthFix) {
0687: addTime(0, m, 0, monthFix);
0688: }
0689:
0690: /**
0691: * Adds minutes, with preset value of monthFix.
0692: *
0693: * @param m minutes to add.
0694: */
0695: public void addMinute(int m) {
0696: addMinute(m, getMonthFix());
0697: }
0698:
0699: /**
0700: * Adds seconds.
0701: *
0702: * @param s seconds to add
0703: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0704: */
0705: public void addSecond(double s, boolean monthFix) {
0706: addTime(0, 0, s, monthFix);
0707: }
0708:
0709: /**
0710: * Adds seconds, with preset value of monthFix.
0711: *
0712: * @param s seconds to add
0713: */
0714: public void addSecond(double s) {
0715: addSecond(s, getMonthFix());
0716: }
0717:
0718: /**
0719: * Adds milliseconds.
0720: *
0721: * @param ms miliseconds to add
0722: * @param monthFix <code>true</code> for month fixing, <code>false</code> otherwise
0723: */
0724: public void addMillisecond(int ms, boolean monthFix) {
0725: addTime(0, 0, ms / 1000.0, monthFix);
0726: }
0727:
0728: /**
0729: * Adds milliseconds, with preset value of monthFix.
0730: *
0731: * @param ms miliseconds to add
0732: */
0733: public void addMillisecond(int ms) {
0734: addMillisecond(ms, getMonthFix());
0735: }
0736:
0737: // ---------------------------------------------------------------- ctors & sets
0738:
0739: /**
0740: * Constructor that set date and time.
0741: *
0742: * @param year year to set
0743: * @param month month to set
0744: * @param day day to set
0745: * @param hour hours to set
0746: * @param minute minutes to set
0747: * @param second seconds to set
0748: *
0749: * @see #set(int, int, int, int, int, double)
0750: */
0751: public JDateTime(int year, int month, int day, int hour,
0752: int minute, double second) {
0753: this .set(year, month, day, hour, minute, second);
0754: }
0755:
0756: /**
0757: * Sets date, time is set to midnight (00:00:00.000).
0758: *
0759: * @param year year to set
0760: * @param month month to set
0761: * @param day day to set
0762: */
0763: public void set(int year, int month, int day) {
0764: set(year, month, day, 0, 0, 0);
0765: }
0766:
0767: /**
0768: * Constructor that sets just date. Time is set to 00:00:00.
0769: *
0770: * @param year year to set
0771: * @param month month to set
0772: * @param day day to set
0773: *
0774: * @see #set(int, int, int)
0775: */
0776: public JDateTime(int year, int month, int day) {
0777: this .set(year, month, day);
0778: }
0779:
0780: /**
0781: * Sets time, date is unchanged.
0782: *
0783: * @param hour hours to set
0784: * @param minute minutes to set
0785: * @param second secnds to set
0786: */
0787: public void setTime(int hour, int minute, double second) {
0788: set(time.year, time.month, time.day, hour, minute, second);
0789: }
0790:
0791: /**
0792: * Sets date, time remains unchanged.
0793: *
0794: * @param year year
0795: * @param month month
0796: * @param day day
0797: */
0798: public void setDate(int year, int month, int day) {
0799: set(year, month, day, time.hour, time.minute, time.second);
0800: }
0801:
0802: // ---------------------------------------------------------------- milliseconds
0803:
0804: /**
0805: * Constructor that sets current time specified as time in milliseconds, from
0806: * the midnight, January 1, 1970 UTC.
0807: *
0808: * @param milis time in milliseconds, from the midnight, January 1, 1970 UTC
0809: *
0810: * @see #setTimeInMillis(long )
0811: */
0812: public JDateTime(long milis) {
0813: setTimeInMillis(milis);
0814: }
0815:
0816: /**
0817: * Sets the time based on current time in milliseconds. Current time is
0818: * calculated from the midnight, January 1, 1970 UTC.
0819: * <p>
0820: * Calculation is divided in two steps, due to precission issues.
0821: *
0822: * @param millis time in milliseconds, from the midnight, January 1, 1970 UTC
0823: */
0824: public void setTimeInMillis(long millis) {
0825: millis += getTimeZone().getOffset(millis);
0826: JulianDateStamp now = new JulianDateStamp();
0827: now.integer = (int) (millis / TimeUtil.MILLIS_IN_DAY);
0828: now.fraction = (double) (millis % TimeUtil.MILLIS_IN_DAY)
0829: / TimeUtil.MILLIS_IN_DAY;
0830: now.integer += JD_1970.integer;
0831: now.fraction += JD_1970.fraction;
0832: if (now.fraction >= 1.0) {
0833: now.integer++;
0834: now.fraction -= 1.0;
0835: }
0836: setJulianDate(now);
0837: }
0838:
0839: /**
0840: * Returns time based on current time in milliseconds. Current time is
0841: * calculated from the midnight, January 1, 1970 UTC.
0842: * <p>
0843: * Due to popssible huge values calculation is divided in two steps:
0844: * first for fractionals difference, and thene for integer parts.
0845: *
0846: * @return milis time in milliseconds, from the midnight, January 1, 1970 UTC
0847: */
0848: public long getTimeInMillis() {
0849: double then = (jdate.fraction - JD_1970.fraction)
0850: * TimeUtil.MILLIS_IN_DAY;
0851: then += (jdate.integer - JD_1970.integer)
0852: * TimeUtil.MILLIS_IN_DAY;
0853: then -= getTimeZone().getOffset((long) then);
0854: then += then > 0 ? 1.0e-6 : -1.0e-6;
0855: return (long) then;
0856: }
0857:
0858: // ---------------------------------------------------------------- date/time sets
0859:
0860: /**
0861: * Sets current year.
0862: *
0863: * @param y year to set
0864: */
0865: public void setYear(int y) {
0866: setDate(y, time.month, time.day);
0867: }
0868:
0869: /**
0870: * Sets current month.
0871: *
0872: * @param m month to set
0873: */
0874: public void setMonth(int m) {
0875: setDate(time.year, m, time.day);
0876: }
0877:
0878: /**
0879: * Sets current day.
0880: *
0881: * @param d day to set
0882: */
0883: public void setDay(int d) {
0884: setDate(time.year, time.month, d);
0885: }
0886:
0887: /**
0888: * Set current hour.
0889: *
0890: * @param h hour to set
0891: */
0892: public void setHour(int h) {
0893: setTime(h, time.minute, time.second);
0894: }
0895:
0896: /**
0897: * Set current minute.
0898: *
0899: * @param m minutes to set
0900: */
0901: public void setMinute(int m) {
0902: setTime(time.hour, m, time.second);
0903:
0904: }
0905:
0906: /**
0907: * Sets current second and millisecond.
0908: *
0909: * @param s seconds and milliseconds to set
0910: */
0911: public void setSecond(double s) {
0912: setTime(time.hour, time.minute, s);
0913: }
0914:
0915: /**
0916: * Sets current second.
0917: *
0918: * @param s seconds to set
0919: */
0920: public void setSecond(int s) {
0921: double milis = s + (time.second - (int) time.second);
0922: setTime(time.hour, time.minute, milis);
0923: }
0924:
0925: /**
0926: * Sets current millisecond.
0927: *
0928: * @param m milliseconds to set
0929: */
0930: public void setMillisecond(int m) {
0931: setTime(time.hour, time.minute, ((int) time.second) + m
0932: / 1000.0);
0933: }
0934:
0935: // ---------------------------------------------------------------- date/time gets
0936:
0937: /**
0938: * Returns current year.
0939: */
0940: public int getYear() {
0941: return time.year;
0942: }
0943:
0944: /**
0945: * Returns current month.
0946: */
0947: public int getMonth() {
0948: return time.month;
0949: }
0950:
0951: /**
0952: * Returns current day of month.
0953: * @see #getDayOfMonth
0954: */
0955: public int getDay() {
0956: return time.day;
0957: }
0958:
0959: /**
0960: * Returns current day of month.
0961: * @see #getDay
0962: */
0963: public int getDayOfMonth() {
0964: return time.day;
0965: }
0966:
0967: /**
0968: * Returns current hour.
0969: */
0970: public int getHour() {
0971: return time.hour;
0972: }
0973:
0974: /**
0975: * Returns current minutes.
0976: */
0977: public int getMinute() {
0978: return time.minute;
0979: }
0980:
0981: /**
0982: * Return current secodns. For an integer value, just cast the returned
0983: * value.
0984: */
0985: public double getSecond() {
0986: return time.second;
0987: }
0988:
0989: /**
0990: * Returns current milliseconds.
0991: */
0992: public int getMillisecond() {
0993: return (int) ((time.second - (int) time.second) * 1000 + 1e-9);
0994: }
0995:
0996: // ---------------------------------------------------------------- other gets
0997:
0998: /**
0999: * Returns current day of week.
1000: */
1001: public int getDayOfWeek() {
1002: return dayofweek;
1003: }
1004:
1005: /**
1006: * Returns current day of year.
1007: */
1008: public int getDayOfYear() {
1009: return dayofyear;
1010: }
1011:
1012: /**
1013: * Returns <code>true</code> if current year is leap year.
1014: */
1015: public boolean isLeapYear() {
1016: return leap;
1017: }
1018:
1019: /**
1020: * Returns current week of year.
1021: */
1022: public int getWeekOfYear() {
1023: return weekofyear;
1024: }
1025:
1026: /**
1027: * Returns current week of month.
1028: */
1029: public int getWeekOfMonth() {
1030: return weekofmonth;
1031: }
1032:
1033: /**
1034: * Length of months. Do not use directly!!!
1035: */
1036: private static final int MONTH_LENGTH[] = { 0, 31, 0, 31, 30, 31,
1037: 30, 31, 31, 30, 31, 30, 31 };
1038:
1039: /**
1040: * Returns the length of the specified month in days.
1041: */
1042: public int getMonthLength(int m) {
1043: if ((m < 1) || (m > 12)) {
1044: throw new IllegalArgumentException("Invalid month index: '"
1045: + m + "'.");
1046: }
1047: if (m == 2) {
1048: return this .leap ? 29 : 28;
1049: }
1050: if ((time.year == 1582) && (time.month == 10)) {
1051: return 21;
1052: }
1053: return MONTH_LENGTH[m];
1054: }
1055:
1056: /**
1057: * Returns the length of the current month in days.
1058: */
1059: public int getMonthLength() {
1060: return getMonthLength(time.month);
1061: }
1062:
1063: // ---------------------------------------------------------------- current date time
1064:
1065: /**
1066: * Sets current local date and time.
1067: */
1068: public void setCurrentTime() {
1069: setTimeInMillis(System.currentTimeMillis());
1070: }
1071:
1072: /**
1073: * Constructor that sets current local date and time.
1074: */
1075: public JDateTime() {
1076: this .setCurrentTime();
1077: }
1078:
1079: // ---------------------------------------------------------------- conversion (general)
1080:
1081: /**
1082: * Loads date time <b>from</b> an object using a converter registered in {@link JdtConverterManager}.
1083: * @see jodd.datetime.converter.JdtConverter#loadFrom(JDateTime, Object)
1084: */
1085: public void loadFrom(Object source) {
1086: JdtConverter converter = JdtConverterManager.lookup(source
1087: .getClass());
1088: if (converter == null) {
1089: throw new IllegalArgumentException("JdtConverter for '"
1090: + source.getClass() + "' type not registered.");
1091: }
1092: converter.loadFrom(this , source);
1093: }
1094:
1095: /**
1096: * Converts date time <b>to</b> an another type.
1097: * All default converters have direct method for easier usage.
1098: * @see jodd.datetime.converter.JdtConverter#convertTo(JDateTime)
1099: */
1100: public Object convertTo(Class type) {
1101: JdtConverter converter = JdtConverterManager.lookup(type);
1102: if (converter == null) {
1103: throw new IllegalArgumentException("JdtConverter for '"
1104: + type + "' type not registered.");
1105: }
1106: return converter.convertTo(this );
1107: }
1108:
1109: /**
1110: * Stores date time <b>to</b> a destination instance.
1111: * @see jodd.datetime.converter.JdtConverter#storeTo(JDateTime, Object)
1112: */
1113: public void storeTo(Object destination) {
1114: JdtConverter converter = JdtConverterManager.lookup(destination
1115: .getClass());
1116: if (converter == null) {
1117: throw new IllegalArgumentException("JdtConverter for '"
1118: + destination.getClass() + "' type not registered.");
1119: }
1120: converter.storeTo(this , destination);
1121: }
1122:
1123: // ---------------------------------------------------------------- conversions
1124:
1125: /**
1126: * Converts to {@link Calendar} instance.
1127: * @see #convertTo(Class)
1128: */
1129: public java.util.Calendar convertToCalendar() {
1130: return (java.util.Calendar) convertTo(java.util.Calendar.class);
1131: }
1132:
1133: /**
1134: * Converts to {@link java.util.Date} instance.
1135: * @see #convertTo(Class)
1136: */
1137: public java.util.Date convertToDate() {
1138: return (java.util.Date) convertTo(java.util.Date.class);
1139: }
1140:
1141: /**
1142: * Converts to {@link java.util.GregorianCalendar} instance.
1143: * @see #convertTo(Class)
1144: */
1145: public java.util.GregorianCalendar convertToGregorianCalendar() {
1146: return (java.util.GregorianCalendar) convertTo(java.util.GregorianCalendar.class);
1147: }
1148:
1149: /**
1150: * Converts to {@link java.sql.Date} instance.
1151: * @see #convertTo(Class)
1152: */
1153: public java.sql.Date convertToSqlDate() {
1154: return (java.sql.Date) convertTo(java.sql.Date.class);
1155: }
1156:
1157: /**
1158: * Converts to {@link java.sql.Timestamp} instance.
1159: * @see #convertTo(Class)
1160: */
1161: public java.sql.Timestamp convertToSqlTimestamp() {
1162: return (java.sql.Timestamp) convertTo(java.sql.Timestamp.class);
1163: }
1164:
1165: /**
1166: * Converts to {@link DateTimeStamp} instance.
1167: * @see #convertTo(Class)
1168: */
1169: public DateTimeStamp convertToDateTimeStamp() {
1170: return (DateTimeStamp) convertTo(DateTimeStamp.class);
1171: }
1172:
1173: // ---------------------------------------------------------------- ctors from conversions
1174:
1175: /**
1176: * Contructs <code>JDateTime</code> from a objects.
1177: * @see #loadFrom(Object)
1178: */
1179: public JDateTime(Object o) {
1180: loadFrom(o);
1181: }
1182:
1183: /**
1184: * Contructs <code>JDateTime</code> from <code>DateTimeStamp</code>.
1185: */
1186: public JDateTime(DateTimeStamp dts) {
1187: setDateTimeStamp(dts);
1188: }
1189:
1190: /**
1191: * Contructs <code>JDateTime</code> from <code>JulianDateStamp</code>.
1192: */
1193: public JDateTime(JulianDateStamp jds) {
1194: setJulianDate(jds);
1195: }
1196:
1197: /**
1198: * Constructs <code>JDateTime</code> from a string.
1199: */
1200: public JDateTime(String s) {
1201: fromString(s);
1202: }
1203:
1204: /**
1205: * Constructs <code>JDateTime</code> from a string, using specified template.
1206: */
1207: public JDateTime(String s, String template) {
1208: fromString(s, template);
1209: }
1210:
1211: // ---------------------------------------------------------------- monthFix
1212:
1213: /**
1214: * Default month fix value.
1215: */
1216: public static boolean DEFAULT_MONTH_FIX = true;
1217:
1218: private Boolean monthFix;
1219:
1220: /**
1221: * Sets custom month fix value.
1222: * Setting to <code>null</code> will reset value to {@link #DEFAULT_MONTH_FIX}.
1223: */
1224: public void setMonthFix(Boolean monthFix) {
1225: this .monthFix = monthFix;
1226: }
1227:
1228: /**
1229: * Reset to global default values.
1230: */
1231: public void setDefaultMonthFix() {
1232: setMonthFix(null);
1233: }
1234:
1235: /**
1236: * Returns currently <b>active</b> month fix value.
1237: * <b>IMPORTANT</b>: When no specific value is set, it returns {@link #DEFAULT_MONTH_FIX}.
1238: */
1239: public boolean getMonthFix() {
1240: return (monthFix != null ? monthFix.booleanValue()
1241: : DEFAULT_MONTH_FIX);
1242: }
1243:
1244: // ---------------------------------------------------------------- timezone
1245:
1246: private TimeZone timezone;
1247:
1248: /**
1249: * Set custom timezone. Current time is changed if time zone has been changed.
1250: * Setting to <code>null</code> will reset value to default time zone.
1251: */
1252: public void setTimeZone(TimeZone timezone) {
1253: long now = getTimeInMillis();
1254: int offsetBefore = getTimeZone().getOffset(now);
1255: this .timezone = timezone;
1256: int offsetAfter = getTimeZone().getOffset(now);
1257: if (offsetAfter != offsetBefore) {
1258: addMillisecond(offsetAfter - offsetBefore);
1259: }
1260: }
1261:
1262: /**
1263: * Reset to global default values.
1264: */
1265: public void setDefaultTimeZone() {
1266: setTimeZone(null);
1267: }
1268:
1269: /**
1270: * Return currently <b>active</b> time zone.
1271: * <b>IMPORTANT</b>: When no specific value set, it returns default timezone.
1272: */
1273: public TimeZone getTimeZone() {
1274: return (timezone != null ? timezone : TimeZone.getDefault());
1275: }
1276:
1277: // ---------------------------------------------------------------- locale
1278:
1279: private Locale locale;
1280:
1281: /**
1282: * Set custom locale.
1283: * Setting to <code>null</null> will reset value to default locale.
1284: */
1285: public void setLocale(Locale locale) {
1286: this .locale = locale;
1287: }
1288:
1289: /**
1290: * Reset to global default values.
1291: */
1292: public void setDefaultLocale() {
1293: setLocale(null);
1294: }
1295:
1296: /**
1297: * Return currently <b>active</b> locale.
1298: * <b>IMPORTANT</b>: When no specific value set, it returns default locale.
1299: */
1300: public Locale getLocale() {
1301: return (locale != null ? locale : Locale.getDefault());
1302: }
1303:
1304: // ---------------------------------------------------------------- formats
1305:
1306: /**
1307: * Default date time format.
1308: */
1309: public static String DEFAULT_FORMAT = "YYYY-MM-DD hh:mm:ss.mss";
1310:
1311: private String format;
1312:
1313: /**
1314: * Defines custom format.
1315: * Setting to <code>null</code> will reset value to {@link #DEFAULT_FORMAT}.
1316: */
1317: public void setFormat(String newFormat) {
1318: format = newFormat;
1319: }
1320:
1321: /**
1322: * Reset to global default values.
1323: */
1324: public void setDefaultFormat() {
1325: setFormat(null);
1326: }
1327:
1328: /**
1329: * Returns actual format.
1330: * <b>IMPORTANT</b>: When no specific format set, it returns {@link #DEFAULT_FORMAT}.
1331: */
1332: public String getFormat() {
1333: return (format != null ? format : DEFAULT_FORMAT);
1334: }
1335:
1336: // ---------------------------------------------------------------- formatter
1337:
1338: /**
1339: * Default {@link JdtFormatter}.
1340: */
1341: public static JdtFormatter DEFAULT_JDTFORMATTER = new DefaultFormatter();
1342:
1343: private JdtFormatter jdtFormatter;
1344:
1345: /**
1346: * Defines custom formatter.
1347: * Setting to <code>null</code> will reset value to {@link #DEFAULT_JDTFORMATTER}.
1348: */
1349: public void setJdtFormatter(JdtFormatter jdtFormatter) {
1350: this .jdtFormatter = jdtFormatter;
1351: }
1352:
1353: public void setDefaultJdtFormatter() {
1354: setJdtFormatter(null);
1355: }
1356:
1357: /**
1358: * Returns actual {@link JdtFormatter}.
1359: * <b>IMPORTANT</b>: When no specific formatter set, it returns {@link #DEFAULT_JDTFORMATTER}.
1360: */
1361: public JdtFormatter getJdtFormatter() {
1362: return (jdtFormatter != null ? jdtFormatter
1363: : DEFAULT_JDTFORMATTER);
1364: }
1365:
1366: // ---------------------------------------------------------------- toString
1367:
1368: /**
1369: * Returns string representation of date/time in specified format.
1370: * @param template format template
1371: */
1372: public String toString(String template) {
1373: return getJdtFormatter().convert(this , template);
1374: }
1375:
1376: /**
1377: * Returns spring representation of current date/time in currently active format.
1378: * @see #getFormat()
1379: */
1380: @Override
1381: public String toString() {
1382: return toString(getFormat());
1383: }
1384:
1385: /**
1386: * Sets date/time from a string given in provided template.
1387: * @param s string containing date time information
1388: * @param template format template
1389: */
1390: public void fromString(String s, String template) {
1391: DateTimeStamp dts = getJdtFormatter().parse(s, template);
1392: if (dts != null) {
1393: setDateTimeStamp(dts);
1394: }
1395: }
1396:
1397: /**
1398: * Sets date/time from a string and currently active template.
1399: * @param s string containing date time information
1400: * @see #getFormat()
1401: */
1402: public void fromString(String s) {
1403: fromString(s, getFormat());
1404: }
1405:
1406: /**
1407: * Checks if some string represents a valid date using actual template.
1408: *
1409: * @return <code>true</code> if date is valid, otherwise <code>false</code>
1410: */
1411: public boolean isValid(String s) {
1412: return isValid(s, getFormat());
1413: }
1414:
1415: /**
1416: * Checks if some string represents a valid date using provided template.
1417: *
1418: * @return <code>true</code> if date is valid, otherwise <code>false</code>
1419: */
1420: public boolean isValid(String s, String template) {
1421: DateTimeStamp dtsOriginal = getJdtFormatter()
1422: .parse(s, template);
1423: if (dtsOriginal == null) {
1424: return false;
1425: }
1426: return TimeUtil.isValidDateTime(dtsOriginal);
1427: }
1428:
1429: // ---------------------------------------------------------------- week definitions
1430:
1431: /**
1432: * Default first day of the week value, in range [1-7].
1433: */
1434: public static int DEFAULT_FIRST_DAY_OF_WEEK = 1;
1435:
1436: private int firstDayOfWeek;
1437:
1438: /**
1439: * Default day of the first week that will be in the week, in range [1-7].
1440: */
1441: public static int DEFAULT_MUST_HAVE_DAY_OF_FIRST_WEEK = 4;
1442:
1443: private int mustHaveDayOfFirstWeek;
1444:
1445: /**
1446: * Defines week definitions. If provided parameter is <code>0</code>,
1447: * it resets value back to default.
1448: *
1449: * @param start first day in week, [1-7], 0 resets to default
1450: * @param must must have day of the 1st week, [1-7], 0 resets to default
1451: */
1452: public void setWeekDefinition(int start, int must) {
1453: if (start == 0) {
1454: firstDayOfWeek = 0;
1455: } else if ((start >= 1) && (start <= 7)) {
1456: firstDayOfWeek = start;
1457: }
1458: if (must == 0) {
1459: mustHaveDayOfFirstWeek = 0;
1460: } else if ((must >= 1) && (must <= 7)) {
1461: mustHaveDayOfFirstWeek = must;
1462: minDaysInFirstWeek = convertMin2Must(getFirstDayOfWeek(),
1463: must);
1464: }
1465: }
1466:
1467: /**
1468: * Reset to global default values.
1469: */
1470: public void setDefaultWeekDefinition() {
1471: setWeekDefinition(0, 0);
1472: }
1473:
1474: /**
1475: * Returns <b>actual</b> the first day of the week.
1476: */
1477: public int getFirstDayOfWeek() {
1478: return (firstDayOfWeek != 0 ? firstDayOfWeek
1479: : DEFAULT_FIRST_DAY_OF_WEEK);
1480: }
1481:
1482: /**
1483: * Returns <b>actual</b> must have day of the 1st week.
1484: */
1485: public int getMustHaveDayOfFirstWeek() {
1486: return (mustHaveDayOfFirstWeek != 0 ? mustHaveDayOfFirstWeek
1487: : DEFAULT_MUST_HAVE_DAY_OF_FIRST_WEEK);
1488: }
1489:
1490: // ---------------------------------------------------------------- week definitions (alt)
1491:
1492: /**
1493: * Default minimal days in first week, in range [1-7].
1494: */
1495: public static int DEFAULT_MIN_DAYS_IN_FIRST_WEEK = 4;
1496:
1497: private int minDaysInFirstWeek;
1498:
1499: /**
1500: * Returns <b>actual</b> minimal number of days of the first week. It is
1501: * calculated from must have day of the first week.
1502: *
1503: * @return minimal number of days of the first week
1504: */
1505: public int getMinDaysInFirstWeek() {
1506: return (minDaysInFirstWeek != 0 ? minDaysInFirstWeek
1507: : DEFAULT_MIN_DAYS_IN_FIRST_WEEK);
1508: }
1509:
1510: /**
1511: * Defines week alternatively.
1512: *
1513: * @param start first day in week
1514: * @param min minimal days of week
1515: */
1516:
1517: public void setWeekDefinitionAlt(int start, int min) {
1518: if (start == 0) {
1519: firstDayOfWeek = 0;
1520: }
1521: if ((start >= 1) && (start <= 7)) {
1522: firstDayOfWeek = start;
1523: }
1524: if (min == 0) {
1525: mustHaveDayOfFirstWeek = 0;
1526: minDaysInFirstWeek = 0;
1527: } else if ((min >= 1) && (min <= 7)) {
1528: mustHaveDayOfFirstWeek = convertMin2Must(
1529: getFirstDayOfWeek(), min);
1530: minDaysInFirstWeek = min;
1531: }
1532: }
1533:
1534: /**
1535: * Reset to global default values.
1536: */
1537: public void setDefaultWeekDefinitionAlt() {
1538: setWeekDefinitionAlt(0, 0);
1539: }
1540:
1541: /**
1542: * Converts minimal day of week to must have day of week.
1543: * Method is symmetrical.
1544: *
1545: * @param start first day of week
1546: * @param min minimal day of week
1547: *
1548: * @return must have day of week
1549: */
1550: private static int convertMin2Must(int start, int min) {
1551: int must = 8 - min + (start - 1);
1552: if (must > 7) {
1553: must -= 7;
1554: }
1555: return must;
1556: }
1557:
1558: // ---------------------------------------------------------------- equals & hashCode
1559:
1560: Object[] state = new Object[] { jdate, locale, timezone, format,
1561: jdtFormatter, };
1562:
1563: @Override
1564: public boolean equals(Object object) {
1565: if (this == object) {
1566: return true;
1567: }
1568: if (ObjectUtil.equalsType(object, this ) == false) {
1569: return false;
1570: }
1571: final JDateTime jdt = (JDateTime) object;
1572: return (this .monthFix == jdt.monthFix)
1573: && (this .firstDayOfWeek == jdt.firstDayOfWeek)
1574: && (this .mustHaveDayOfFirstWeek == jdt.mustHaveDayOfFirstWeek)
1575: && ObjectUtil.equalsEx(state, jdt.state);
1576: }
1577:
1578: @Override
1579: public int hashCode() {
1580: int result = HashCodeUtil.SEED;
1581: result = HashCodeUtil.hash(result, state);
1582: result = HashCodeUtil.hash(result, monthFix);
1583: result = HashCodeUtil.hash(result, firstDayOfWeek);
1584: result = HashCodeUtil.hash(result, mustHaveDayOfFirstWeek);
1585: return result;
1586: }
1587:
1588: // ---------------------------------------------------------------- clone
1589:
1590: @Override
1591: public Object clone() {
1592: JDateTime jdt = new JDateTime(this .jdate);
1593: jdt.monthFix = this .monthFix;
1594: jdt.timezone = this .timezone;
1595: jdt.locale = this .locale;
1596: jdt.format = this .format;
1597: jdt.jdtFormatter = this .jdtFormatter;
1598: jdt.firstDayOfWeek = this .firstDayOfWeek;
1599: jdt.mustHaveDayOfFirstWeek = this .mustHaveDayOfFirstWeek;
1600: return jdt;
1601: }
1602:
1603: // ---------------------------------------------------------------- compare
1604:
1605: /**
1606: * Compares current JDateTime object with another one, up to 1 millisecond.
1607: *
1608: * @param gt JDateTime to compare
1609: *
1610: * @return -1 if the current object is less than the argument, 0 if the argument is
1611: * equal, and 1 if the current object is greater than the argument
1612: */
1613: public int compareTo(Object gt) {
1614: return time.compareTo(((JDateTime) gt).getDateTimeStamp());
1615: }
1616:
1617: }
|