001: /* Dates.java
002:
003: {{IS_NOTE
004:
005: Purpose:
006: Description:
007: History:
008: 2001/12/3, Henri Chen: Created.
009:
010: }}IS_NOTE
011:
012: Copyright (C) 2001 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019:
020: package org.zkoss.util;
021:
022: import java.util.Date;
023: import java.util.Calendar;
024: import java.util.TimeZone;
025: import java.util.Locale;
026:
027: import org.zkoss.lang.SystemException;
028:
029: /**
030: * Utilities for java.util.Date
031: *
032: * @author henrichen
033: * @author tomyeh
034: */
035: public class Dates {
036: /**
037: * Truncates date to the nearest precision milliseconds. MS SQLServer2000
038: * with only the maximum accuracy of 1/300 seconds would not be able to
039: * store up to one millisecond accurarcy. That is, User must round the
040: * millisecond to some less precisions; or the data in that db would be
041: * inconsistence with that in memory.
042: * It is useful to store a Date object in a database.
043: * Without rounding, if you want to get it back and compare with the
044: * one in the memory. See {@link #now} for details.
045: *
046: * @param precision the divider of the precision(e.g. 10 for precision
047: * of 10 milliseconds;i.e. all millisecond less than 10 would be truncated)
048: * @see #now
049: * @see #round(long, int)
050: */
051: public static final Date round(Date date, int precision) {
052: date.setTime(round(date.getTime(), precision));
053: return date;
054: }
055:
056: /**
057: * Rounds a date represented in long to the specified precision of
058: * milliseconds.
059: *
060: * @param time the date represented in long.
061: * @param precision the divider of the precision(e.g. 10 for precision
062: * of 10 milliseconds;i.e. all millisecond less than 10 would be truncated)
063: * @see #now
064: * @see #round(Date, int)
065: */
066: public static final long round(long time, int precision) {
067: return time - (time % precision);
068: }
069:
070: /** Tests whether a date is rounded.
071: * It is mainly used for debugging.
072: */
073: public static final boolean isRounded(Date date, int precision) {
074: return (date.getTime() % precision) == 0;
075: }
076:
077: /** Returns the current time but rounding to the specified precision
078: * of milliseconds. It is useful if you want to create the current time
079: * which will be stored in the database and want to compare it with
080: * something with what you store in the database. Otherwise, that you
081: * get back the one you store might be different, because the resolution
082: * of database timestamp is usually less than one milisecond,
083: * e.g., MS SQL: 0.003 second.
084: *
085: * <p>If you don't cache it in the memory (remember entity beans
086: * always cache by the container), you don't need to round. If you
087: * are not sure, round it.
088: *
089: * @see #round(Date, int)
090: */
091: public static final Date now(int precision) {
092: return new Date(round(System.currentTimeMillis(), precision));
093: }
094:
095: /** Returns the current time without rounding.
096: */
097: public static final Date now() {
098: return new Date();
099: }
100:
101: /** Returns today by setting time to 0:0:0.
102: */
103: public static final Date today() {
104: return beginOfDate(new Date(), null);
105: }
106:
107: /**
108: * Given a date, return the previouse date of the given date (24 hrs before).
109: */
110: final public static Date previousDate(Date when) {
111: long time = when.getTime() - 24 * 60 * 60 * 1000;
112: return new Date(time);
113: }
114:
115: /**
116: * Return the beginning date of this month.
117: */
118: final public static Date beginOfMonth() {
119: return beginOfMonth(new Date(), null);
120: }
121:
122: /**
123: * Given a date, a proper TimeZone, return the beginning date of the
124: * month of the specified date and TimeZone.
125: * If TimeZone is null, meaning use default TimeZone of the JVM.
126: */
127: final public static Date beginOfMonth(Date when, TimeZone tz) {
128: if (tz == null)
129: tz = TimeZones.getCurrent();
130: final Calendar cal = Calendar.getInstance(tz);
131: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
132:
133: final int year = cal.get(Calendar.YEAR);
134: final int month = cal.get(Calendar.MONTH);
135: cal.clear();
136: cal.set(year, month, 1);
137: return cal.getTime();
138: }
139:
140: /**
141: * Return the ending date of this month.
142: */
143: final public static Date endOfMonth() {
144: return endOfMonth(new Date(), null);
145: }
146:
147: /**
148: * Given a date, a proper TimeZone, return the ending date of the
149: * month of the specified date and TimeZone.
150: * If TimeZone is null, meaning use default TimeZone of the JVM.
151: */
152: final public static Date endOfMonth(Date when, TimeZone tz) {
153: if (tz == null)
154: tz = TimeZones.getCurrent();
155: final Calendar cal = Calendar.getInstance(tz);
156: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
157:
158: final int year = cal.get(Calendar.YEAR);
159: final int month = cal.get(Calendar.MONTH);
160: final int monthDays = cal
161: .getActualMaximum(Calendar.DAY_OF_MONTH);
162: cal.clear();
163: cal.set(year, month, monthDays + 1);
164: cal.setTimeInMillis(cal.getTimeInMillis() - 1);
165: return cal.getTime();
166: }
167:
168: /**
169: * Whether the given date in the specified TimeZone is the last day of that
170: * month. If TimeZone is null, meaning use default TimeZone of the JVM.
171: */
172: final public static boolean isEndOfMonth(Date when, TimeZone tz) {
173: if (tz == null)
174: tz = TimeZones.getCurrent();
175: final Calendar cal = Calendar.getInstance(tz);
176: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
177:
178: final int day = cal.get(Calendar.DAY_OF_MONTH);
179: final int maxDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
180: return day == maxDay;
181: }
182:
183: /**
184: * Whether the given date in the specified TimeZone is the first day of that
185: * month. If TimeZone is null, meaning use default TimeZone of the JVM.
186: */
187: final public static boolean isBeginOfMonth(Date when, TimeZone tz) {
188: if (tz == null)
189: tz = TimeZones.getCurrent();
190: final Calendar cal = Calendar.getInstance(tz);
191: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
192:
193: final int day = cal.get(Calendar.DAY_OF_MONTH);
194: return day == 1;
195: }
196:
197: /**
198: * Given a date, a proper TimeZone, return the beginning date of
199: * the specified date and TimeZone. If TimeZone is null, meaning use Defult
200: * TimeZone of the JVM.
201: */
202: final public static Date beginOfDate(Date when, TimeZone tz) {
203: if (tz == null)
204: tz = TimeZones.getCurrent();
205:
206: final Calendar cal = Calendar.getInstance(tz);
207: cal.setTimeInMillis(when.getTime());//don't call cal.setTime(Date) which will reset the TimeZone.
208: final int day = cal.get(Calendar.DAY_OF_MONTH);
209: final int year = cal.get(Calendar.YEAR);
210: final int month = cal.get(Calendar.MONTH);
211: cal.clear();
212: cal.set(year, month, day);
213:
214: return cal.getTime();
215: }
216:
217: /**
218: * Given a date, a proper TimeZone, return the last millisecond date of
219: * the specified date and TimeZone. If TimeZone is null, meaning use Defult
220: * TimeZone of the JVM.
221: */
222: final public static Date endOfDate(Date when, TimeZone tz) {
223: if (tz == null)
224: tz = TimeZones.getCurrent();
225:
226: final Calendar cal = Calendar.getInstance(tz);
227: cal.setTimeInMillis(when.getTime());//don't call cal.setTime(Date) which will reset the TimeZone.
228: final int day = cal.get(Calendar.DAY_OF_MONTH);
229: final int year = cal.get(Calendar.YEAR);
230: final int month = cal.get(Calendar.MONTH);
231: cal.clear();
232: cal.set(year, month, day + 1);
233: cal.setTimeInMillis(cal.getTimeInMillis() - 1);
234:
235: return cal.getTime();
236: }
237:
238: /**
239: * Return the beginning date of this year.
240: */
241: final public static Date beginOfYear() {
242: return beginOfYear(new Date(), null);
243: }
244:
245: /**
246: * Given a date, a proper TimeZone, return the beginning date of the
247: * month of the specified date and TimeZone.
248: * If TimeZone is null, meaning use default TimeZone of the JVM.
249: */
250: final public static Date beginOfYear(Date when, TimeZone tz) {
251: if (tz == null)
252: tz = TimeZones.getCurrent();
253: final Calendar cal = Calendar.getInstance(tz);
254: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
255:
256: final int year = cal.get(Calendar.YEAR);
257: cal.clear();
258: cal.set(year, Calendar.JANUARY, 1);
259: return cal.getTime();
260: }
261:
262: /**
263: * Return the ending date of this year.
264: */
265: final public static Date endOfYear() {
266: return endOfYear(new Date(), null);
267: }
268:
269: /**
270: * Given a date, a proper TimeZone, return the ending date of the
271: * month of the specified date and TimeZone.
272: * If TimeZone is null, meaning use default TimeZone of the JVM.
273: */
274: final public static Date endOfYear(Date when, TimeZone tz) {
275: if (tz == null)
276: tz = TimeZones.getCurrent();
277: final Calendar cal = Calendar.getInstance(tz);
278: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
279:
280: final int year = cal.get(Calendar.YEAR);
281: cal.clear();
282: cal.set(year + 1, Calendar.JANUARY, 1);
283: cal.setTimeInMillis(cal.getTimeInMillis() - 1);
284: return cal.getTime();
285: }
286:
287: /**
288: * Return the ending date of this year.
289: */
290: final public static short twoMonthShort() {
291: return twoMonthShort(new Date(), null);
292: }
293:
294: /**
295: * Given a date, a proper TimeZone, return the two month int. eg. 1, 3, 5, 7, 9, 11.
296: * If TimeZone is null, meaning use default TimeZone of the JVM.
297: */
298: final public static short twoMonthShort(Date when, TimeZone tz) {
299: if (tz == null)
300: tz = TimeZones.getCurrent();
301: final Calendar cal = Calendar.getInstance(tz);
302: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
303:
304: final int month = (cal.get(Calendar.MONTH) / 2) * 2 + 1;
305: return (short) month;
306: }
307:
308: /**
309: * Get the year of a date.
310: * @param when The date.
311: * @param tz The time zone; if null, the current time zone is assumed.
312: * @see #localizedYearOfDate
313: */
314: public static final int yearOfDate(Date when, TimeZone tz) {
315: if (tz == null)
316: tz = TimeZones.getCurrent();
317: final Calendar cal = Calendar.getInstance(tz);
318: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
319:
320: return cal.get(Calendar.YEAR);
321: }
322:
323: /**
324: * Get the year of a date in the specified locale.
325: *
326: * <p>Currenty, only Locale.ZH_TW is supported, i.e.,
327: * "year - 1911" and it's may be less than 0. Otherwise, it is the same
328: * as {@link #yearOfDate}.
329: *
330: * @param when The date.
331: * @param locale the locale; if null, the current locale is assumed.
332: * @param tz The time zone; if null, the current time zone is assumed.
333: * @see #yearOfDate
334: */
335: public static final int localizedYearOfDate(Date when,
336: Locale locale, TimeZone tz) {
337: if (locale == null)
338: locale = Locales.getCurrent();
339:
340: final int year = yearOfDate(when, tz);
341: if (locale.equals(Locale.TAIWAN))
342: return year - 1911;
343: return year;
344: }
345:
346: /**
347: * Get the month of a date. The first month of the year is JANUARY which is 0.
348: * @param when The date.
349: * @param tz The time zone; if null, the current time zone is assumed.
350: */
351: public static final int monthOfDate(Date when, TimeZone tz) {
352: if (tz == null)
353: tz = TimeZones.getCurrent();
354: final Calendar cal = Calendar.getInstance(tz);
355: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
356:
357: return cal.get(Calendar.MONTH);
358: }
359:
360: /**
361: * Get the month of a date. The first month of the year is JANUARY which is 1.
362: * @param when The date.
363: * @param tz The time zone; if null, the current time zone is assumed.
364: */
365: public static final int monthOfDatePlus1(Date when, TimeZone tz) {
366: if (tz == null)
367: tz = TimeZones.getCurrent();
368: final Calendar cal = Calendar.getInstance(tz);
369: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
370:
371: return cal.get(Calendar.MONTH) + 1;
372: }
373:
374: /**
375: * Get the day of month of a date. The first day of the month has value 1.
376: * @param when The date.
377: * @param tz The time zone; if null, the current time zone is assumed.
378: */
379: public static final int dayMonthOfDate(Date when, TimeZone tz) {
380: if (tz == null)
381: tz = TimeZones.getCurrent();
382: final Calendar cal = Calendar.getInstance(tz);
383: cal.setTimeInMillis(when.getTime()); //don't call cal.setTime(Date) which will reset the TimeZone.
384:
385: return cal.get(Calendar.DAY_OF_MONTH);
386: }
387:
388: /**
389: * Date Arithmetic function. Adds the specified (signed) amount of time to the given date,
390: * based on the calendar's rules.
391: * @param when The based date.
392: * @param tz The time zone; if null, the current time zone is assumed.
393: * @param field The time field.
394: * @param amount The amount of date or time to be added to the field.
395: */
396: public static final Date add(Date when, TimeZone tz, int field,
397: int amount) {
398: if (tz == null)
399: tz = TimeZones.getCurrent();
400:
401: final Calendar cal = Calendar.getInstance(tz);
402: cal.setTimeInMillis(when.getTime());//don't call cal.setTime(Date) which will reset the TimeZone.
403:
404: cal.add(field, amount);
405: return cal.getTime();
406: }
407:
408: /**
409: * Date Arithmetic function (date2 - date1). subtract a date from another date, return the
410: * difference as the required fields. E.g. if specified Calendar.Date, the
411: * smaller range of fields is ignored and this method return the difference
412: * of days.
413: *
414: * @param date2 The date2.
415: * @param tz The time zone.
416: * @param field The time field; e.g., Calendar.DATE, Calendar.YEAR, it's default value is Calendar.DATE
417: * @param date1 The date1.
418: */
419: public static final long subtract(Date date2, TimeZone tz,
420: int field, Date date1) {
421: if (tz == null)
422: tz = TimeZones.getCurrent();
423:
424: boolean negative = false;
425: if (date1.after(date2)) {
426: negative = true;
427: final Date d = date1;
428: date1 = date2;
429: date2 = d;
430: }
431:
432: final Calendar cal1 = Calendar.getInstance(tz);
433: cal1.setTimeInMillis(date1.getTime());//don't call cal.setTime(Date) which will reset the TimeZone.
434:
435: final Calendar cal2 = Calendar.getInstance(tz);
436: cal2.setTimeInMillis(date2.getTime());//don't call cal.setTime(Date) which will reset the TimeZone.
437:
438: int year1 = cal1.get(Calendar.YEAR);
439: int year2 = cal2.get(Calendar.YEAR);
440:
441: switch (field) {
442: case Calendar.YEAR: {
443: return negative ? (year1 - year2) : (year2 - year1);
444: }
445: case Calendar.MONTH: {
446: int month1 = cal1.get(Calendar.MONTH);
447: int month2 = cal2.get(Calendar.MONTH);
448: int months = 12 * (year2 - year1) + month2 - month1;
449: return negative ? -months : months;
450: }
451: case Calendar.HOUR: {
452: int hour1 = cal1.get(Calendar.HOUR_OF_DAY);
453: int hour2 = cal2.get(Calendar.HOUR_OF_DAY);
454: int day1 = cal1.get(Calendar.DAY_OF_YEAR);
455: int day2 = cal2.get(Calendar.DAY_OF_YEAR);
456:
457: int maxDay1 = year1 == year2 ? 0 : cal1
458: .getActualMaximum(Calendar.DAY_OF_YEAR);
459: int hours = 24 * (maxDay1 - day1 + day2) + hour2 - hour1;
460:
461: final Calendar cal = Calendar.getInstance(tz);
462: for (int year = year1 + 1; year < year2; year++) {
463: cal.set(Calendar.YEAR, year);
464: hours += 24 * cal
465: .getActualMaximum(Calendar.DAY_OF_YEAR);
466: }
467: return negative ? -hours : hours;
468: }
469: case Calendar.MINUTE: {
470: int minute1 = cal1.get(Calendar.MINUTE);
471: int minute2 = cal2.get(Calendar.MINUTE);
472: int hour1 = cal1.get(Calendar.HOUR_OF_DAY);
473: int hour2 = cal2.get(Calendar.HOUR_OF_DAY);
474: int day1 = cal1.get(Calendar.DAY_OF_YEAR);
475: int day2 = cal2.get(Calendar.DAY_OF_YEAR);
476:
477: int maxHour1 = day1 == day2 ? 0 : 24;
478: int maxDay1 = year1 == year2 ? 0 : cal1
479: .getActualMaximum(Calendar.DAY_OF_YEAR);
480:
481: int minutes = 60 * (maxHour1 - hour1 + hour2) + minute2
482: - minute1 + 24 * 60 * (maxDay1 - day1 + day2);
483:
484: for (int year = year1 + 1; year < year2; year++) {
485: final Calendar cal = Calendar.getInstance(tz);
486: cal.set(Calendar.YEAR, year);
487: minutes += 24 * 60 * cal
488: .getActualMaximum(Calendar.DAY_OF_YEAR);
489: }
490: return negative ? -minutes : minutes;
491: }
492: case Calendar.SECOND: {
493: long sec1 = date1.getTime() / 1000;
494: long sec2 = date2.getTime() / 1000;
495: return negative ? (sec1 - sec2) : (sec2 - sec1);
496: }
497: case Calendar.MILLISECOND: {
498: return negative ? (date1.getTime() - date2.getTime())
499: : (date2.getTime() - date1.getTime());
500: }
501: case Calendar.DATE:
502: default: /*default, like -1*/
503: {
504: int day1 = cal1.get(Calendar.DAY_OF_YEAR);
505: int day2 = cal2.get(Calendar.DAY_OF_YEAR);
506:
507: int maxDay1 = year1 == year2 ? 0 : cal1
508: .getActualMaximum(Calendar.DAY_OF_YEAR);
509: int days = maxDay1 - day1 + day2;
510:
511: final Calendar cal = Calendar.getInstance(tz);
512: for (int year = year1 + 1; year < year2; year++) {
513: cal.set(Calendar.YEAR, year);
514: days += cal.getActualMaximum(Calendar.DAY_OF_YEAR);
515: }
516: return negative ? -days : days;
517: }
518: }
519: }
520:
521: /**
522: * merge the date part and time part of two specified dates into a date.
523: * @param datePart The date part date.
524: * @param timePart The time part date.
525: * @param tz The time zone.
526: */
527: public static final Date merge(Date datePart, Date timePart,
528: TimeZone tz) {
529: if (tz == null)
530: tz = TimeZones.getCurrent();
531:
532: final Calendar dateCal = Calendar.getInstance(tz);
533: dateCal.setTimeInMillis(datePart.getTime());//don't call cal.setTime(Date) which will reset the TimeZone.
534:
535: final Calendar timeCal = Calendar.getInstance(tz);
536: timeCal.setTimeInMillis(timePart.getTime());//don't call cal.setTime(Date) which will reset the TimeZone.
537:
538: final int hour = timeCal.get(Calendar.HOUR);
539: final int minute = timeCal.get(Calendar.MINUTE);
540: final int second = timeCal.get(Calendar.SECOND);
541: final int msillisecond = timeCal.get(Calendar.MILLISECOND);
542:
543: dateCal.set(Calendar.HOUR, hour);
544: dateCal.set(Calendar.MINUTE, minute);
545: dateCal.set(Calendar.SECOND, second);
546: dateCal.set(Calendar.MILLISECOND, msillisecond);
547:
548: return dateCal.getTime();
549: }
550: }
|