1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2004, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it under the terms
  10. * of the GNU Lesser General Public License as published by the Free Software Foundation;
  11. * either version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  14. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. * See the GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License along with this
  18. * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  19. * Boston, MA 02111-1307, USA.
  20. *
  21. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  22. * in the United States and other countries.]
  23. *
  24. * --------
  25. * Day.java
  26. * --------
  27. * (C) Copyright 2001-2004, by Object Refinery Limited.
  28. *
  29. * Original Author: David Gilbert (for Object Refinery Limited);
  30. * Contributor(s): -;
  31. *
  32. * $Id: Day.java,v 1.4 2005/01/14 17:29:49 mungady Exp $
  33. *
  34. * Changes
  35. * -------
  36. * 11-Oct-2001 : Version 1 (DG);
  37. * 15-Nov-2001 : Updated Javadoc comments (DG);
  38. * 04-Dec-2001 : Added static method to parse a string into a Day object (DG);
  39. * 19-Dec-2001 : Added new constructor as suggested by Paul English (DG);
  40. * 29-Jan-2002 : Changed getDay() method to getSerialDate() (DG);
  41. * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to evaluate with reference
  42. * to a particular time zone (DG);
  43. * 19-Mar-2002 : Changed the API for the TimePeriod classes (DG);
  44. * 29-May-2002 : Fixed bug in equals method (DG);
  45. * 24-Jun-2002 : Removed unnecessary imports (DG);
  46. * 10-Sep-2002 : Added getSerialIndex() method (DG);
  47. * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  48. * 10-Jan-2003 : Changed base class and method names (DG);
  49. * 13-Mar-2003 : Moved to com.jrefinery.data.time package, and implemented Serializable (DG);
  50. * 21-Oct-2003 : Added hashCode() method (DG);
  51. * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
  52. * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for JDK 1.3 (DG);
  53. *
  54. */
  55. package org.jfree.data.time;
  56. import java.io.Serializable;
  57. import java.text.DateFormat;
  58. import java.text.ParseException;
  59. import java.text.SimpleDateFormat;
  60. import java.util.Calendar;
  61. import java.util.Date;
  62. import java.util.TimeZone;
  63. import org.jfree.date.SerialDate;
  64. /**
  65. * Represents a single day in the range 1-Jan-1900 to 31-Dec-9999. This class
  66. * is immutable, which is a requirement for all {@link RegularTimePeriod}
  67. * subclasses.
  68. */
  69. public class Day extends RegularTimePeriod implements Serializable {
  70. /** A standard date formatter. */
  71. protected static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
  72. /** A date formatter for the default locale. */
  73. protected static final DateFormat
  74. DATE_FORMAT_SHORT = DateFormat.getDateInstance(DateFormat.SHORT);
  75. /** A date formatter for the default locale. */
  76. protected static final DateFormat
  77. DATE_FORMAT_MEDIUM = DateFormat.getDateInstance(DateFormat.MEDIUM);
  78. /** A date formatter for the default locale. */
  79. protected static final DateFormat
  80. DATE_FORMAT_LONG = DateFormat.getDateInstance(DateFormat.LONG);
  81. /** The day (uses SerialDate for convenience). */
  82. private SerialDate serialDate;
  83. /**
  84. * Creates a new instance, derived from the system date/time (and assuming the default
  85. * timezone).
  86. */
  87. public Day() {
  88. this(new Date());
  89. }
  90. /**
  91. * Constructs a new one day time period.
  92. *
  93. * @param day the day-of-the-month.
  94. * @param month the month (1 to 12).
  95. * @param year the year (1900 <= year <= 9999).
  96. */
  97. public Day(int day, int month, int year) {
  98. this.serialDate = SerialDate.createInstance(day, month, year);
  99. }
  100. /**
  101. * Constructs a new one day time period.
  102. *
  103. * @param serialDate the day (<code>null</code> not permitted).
  104. */
  105. public Day(SerialDate serialDate) {
  106. if (serialDate == null) {
  107. throw new IllegalArgumentException("Null 'serialDate' argument.");
  108. }
  109. this.serialDate = serialDate;
  110. }
  111. /**
  112. * Constructs a new instance, based on a particular date/time and the default
  113. * time zone.
  114. *
  115. * @param time the time (<code>null</code> not permitted).
  116. */
  117. public Day(Date time) {
  118. // defer argument checking...
  119. this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
  120. }
  121. /**
  122. * Constructs a new instance, based on a particular date/time and time zone.
  123. *
  124. * @param time the date/time.
  125. * @param zone the time zone.
  126. */
  127. public Day(Date time, TimeZone zone) {
  128. if (time == null) {
  129. throw new IllegalArgumentException("Null 'time' argument.");
  130. }
  131. if (zone == null) {
  132. throw new IllegalArgumentException("Null 'zone' argument.");
  133. }
  134. Calendar calendar = Calendar.getInstance(zone);
  135. calendar.setTime(time);
  136. int d = calendar.get(Calendar.DAY_OF_MONTH);
  137. int m = calendar.get(Calendar.MONTH) + 1;
  138. int y = calendar.get(Calendar.YEAR);
  139. this.serialDate = SerialDate.createInstance(d, m, y);
  140. }
  141. /**
  142. * Returns the day as a {@link SerialDate}.
  143. * <P>
  144. * Note: the reference that is returned should be an
  145. * instance of an immutable {@link SerialDate} (otherwise the caller could use the
  146. * reference to alter the state of this Day instance, and Day is supposed
  147. * to be immutable).
  148. *
  149. * @return The day as a SerialDate.
  150. */
  151. public SerialDate getSerialDate() {
  152. return this.serialDate;
  153. }
  154. /**
  155. * Returns the year.
  156. *
  157. * @return The year.
  158. */
  159. public int getYear() {
  160. return this.serialDate.getYYYY();
  161. }
  162. /**
  163. * Returns the month.
  164. *
  165. * @return The month.
  166. */
  167. public int getMonth() {
  168. return this.serialDate.getMonth();
  169. }
  170. /**
  171. * Returns the day of the month.
  172. *
  173. * @return The day of the month.
  174. */
  175. public int getDayOfMonth() {
  176. return this.serialDate.getDayOfMonth();
  177. }
  178. /**
  179. * Returns the day preceding this one.
  180. *
  181. * @return The day preceding this one.
  182. */
  183. public RegularTimePeriod previous() {
  184. Day result;
  185. int serial = this.serialDate.toSerial();
  186. if (serial > SerialDate.SERIAL_LOWER_BOUND) {
  187. SerialDate yesterday = SerialDate.createInstance(serial - 1);
  188. return new Day(yesterday);
  189. }
  190. else {
  191. result = null;
  192. }
  193. return result;
  194. }
  195. /**
  196. * Returns the day following this one, or <code>null</code> if some limit
  197. * has been reached.
  198. *
  199. * @return The day following this one, or <code>null</code> if some limit has been reached.
  200. */
  201. public RegularTimePeriod next() {
  202. Day result;
  203. int serial = this.serialDate.toSerial();
  204. if (serial < SerialDate.SERIAL_UPPER_BOUND) {
  205. SerialDate tomorrow = SerialDate.createInstance(serial + 1);
  206. return new Day(tomorrow);
  207. }
  208. else {
  209. result = null;
  210. }
  211. return result;
  212. }
  213. /**
  214. * Returns a serial index number for the day.
  215. *
  216. * @return The serial index number.
  217. */
  218. public long getSerialIndex() {
  219. return this.serialDate.toSerial();
  220. }
  221. /**
  222. * Returns the first millisecond of the day, evaluated using the supplied
  223. * calendar (which determines the time zone).
  224. *
  225. * @param calendar calendar to use.
  226. *
  227. * @return The start of the day as milliseconds since 01-01-1970.
  228. */
  229. public long getFirstMillisecond(Calendar calendar) {
  230. int year = this.serialDate.getYYYY();
  231. int month = this.serialDate.getMonth();
  232. int day = this.serialDate.getDayOfMonth();
  233. calendar.clear();
  234. calendar.set(year, month - 1, day, 0, 0, 0);
  235. calendar.set(Calendar.MILLISECOND, 0);
  236. //return calendar.getTimeInMillis(); // this won't work for JDK 1.3
  237. return calendar.getTime().getTime();
  238. }
  239. /**
  240. * Returns the last millisecond of the day, evaluated using the supplied
  241. * calendar (which determines the time zone).
  242. *
  243. * @param calendar calendar to use.
  244. *
  245. * @return the end of the day as milliseconds since 01-01-1970.
  246. */
  247. public long getLastMillisecond(Calendar calendar) {
  248. int year = this.serialDate.getYYYY();
  249. int month = this.serialDate.getMonth();
  250. int day = this.serialDate.getDayOfMonth();
  251. calendar.clear();
  252. calendar.set(year, month - 1, day, 23, 59, 59);
  253. calendar.set(Calendar.MILLISECOND, 999);
  254. //return calendar.getTimeInMillis(); // this won't work for JDK 1.3
  255. return calendar.getTime().getTime();
  256. }
  257. /**
  258. * Tests the equality of this Day object to an arbitrary object. Returns
  259. * true if the target is a Day instance or a SerialDate instance
  260. * representing the same day as this object. In all other cases,
  261. * returns false.
  262. *
  263. * @param obj the object.
  264. *
  265. * @return a flag indicating whether or not an object is equal to this day.
  266. */
  267. public boolean equals(Object obj) {
  268. if (obj == this) {
  269. return true;
  270. }
  271. if (!(obj instanceof Day)) {
  272. return false;
  273. }
  274. Day that = (Day) obj;
  275. if (!this.serialDate.equals(that.getSerialDate())) {
  276. return false;
  277. }
  278. return true;
  279. }
  280. /**
  281. * Returns a hash code for this object instance.
  282. * <p>
  283. * The approach described by Joshua Bloch in "Effective Java" has been used here:
  284. * <p>
  285. * <code>http://developer.java.sun.com/developer/Books/effectivejava/Chapter3.pdf</code>
  286. *
  287. * @return A hash code.
  288. */
  289. public int hashCode() {
  290. return this.serialDate.hashCode();
  291. }
  292. /**
  293. * Returns an integer indicating the order of this Day object relative to
  294. * the specified object:
  295. *
  296. * negative == before, zero == same, positive == after.
  297. *
  298. * @param o1 the object to compare.
  299. *
  300. * @return negative == before, zero == same, positive == after.
  301. */
  302. public int compareTo(Object o1) {
  303. int result;
  304. // CASE 1 : Comparing to another Day object
  305. // ----------------------------------------
  306. if (o1 instanceof Day) {
  307. Day d = (Day) o1;
  308. result = -d.getSerialDate().compare(this.serialDate);
  309. }
  310. // CASE 2 : Comparing to another TimePeriod object
  311. // -----------------------------------------------
  312. else if (o1 instanceof RegularTimePeriod) {
  313. // more difficult case - evaluate later...
  314. result = 0;
  315. }
  316. // CASE 3 : Comparing to a non-TimePeriod object
  317. // ---------------------------------------------
  318. else {
  319. // consider time periods to be ordered after general objects
  320. result = 1;
  321. }
  322. return result;
  323. }
  324. /**
  325. * Returns a string representing the day.
  326. *
  327. * @return A string representing the day.
  328. */
  329. public String toString() {
  330. return this.serialDate.toString();
  331. }
  332. /**
  333. * Parses the string argument as a day.
  334. * <P>
  335. * This method is required to recognise YYYY-MM-DD as a valid format.
  336. * Anything else, for now, is a bonus.
  337. *
  338. * @param s the date string to parse.
  339. *
  340. * @return <code>null</code> if the string does not contain any parseable
  341. * string, the day otherwise.
  342. */
  343. public static Day parseDay(String s) {
  344. try {
  345. return new Day (Day.DATE_FORMAT.parse(s));
  346. }
  347. catch (ParseException e1) {
  348. try {
  349. return new Day (Day.DATE_FORMAT_SHORT.parse(s));
  350. }
  351. catch (ParseException e2) {
  352. // ignore
  353. }
  354. }
  355. return null;
  356. }
  357. }