001 package hirondelle.date4j;
002
003 import java.io.IOException;
004 import java.io.ObjectInputStream;
005 import java.io.ObjectOutputStream;
006 import java.io.Serializable;
007 import java.util.Calendar;
008 import java.util.GregorianCalendar;
009 import java.util.List;
010 import java.util.Locale;
011 import java.util.TimeZone;
012
013 /**
014 Building block class for an immutable date-time, with no time zone.
015
016 <P>
017 This class is provided as an alternative to java.util.{@link java.util.Date}.
018
019 <P>This class can hold :
020 <ul>
021 <li>a date-and-time : <tt>1958-03-31 18:59:56.123456789</tt>
022 <li>a date only : <tt>1958-03-31</tt>
023 <li>a time only : <tt>18:59:56.123456789</tt>
024 </ul>
025
026 <P>
027 <a href='#Examples'>Examples</a><br>
028 <a href='#JustificationForThisClass'>Justification For This Class</a><br>
029 <a href='#DatesAndTimesInGeneral'>Dates and Times In General</a><br>
030 <a href='#TheApproachUsedByThisClass'>The Approach Used By This Class</a><br>
031 <a href='#TwoSetsOfOperations'>Two Sets Of Operations</a><br>
032 <a href='#ParsingDateTimeAcceptedFormats'>Parsing DateTime - Accepted Formats</a><br>
033 <a href='#FormattingLanguage'>Mini-Language for Formatting</a><br>
034 <a href='#PassingDateTimeToTheDatabase'>Passing DateTime Objects to the Database</a>
035
036 <a name='Examples'></a>
037 <h3> Examples</h3>
038 Some quick examples of using this class :
039 <PRE>
040 DateTime dateAndTime = new DateTime("2010-01-19 23:59:59");
041 //highest precision is nanosecond, not millisecond:
042 DateTime dateAndTime = new DateTime("2010-01-19 23:59:59.123456789");
043
044 DateTime dateOnly = new DateTime("2010-01-19");
045 DateTime timeOnly = new DateTime("23:59:59");
046
047 DateTime dateOnly = DateTime.forDateOnly(2010,01,19);
048 DateTime timeOnly = DateTime.forTimeOnly(23,59,59,0);
049
050 DateTime dt = new DateTime("2010-01-15 13:59:15");
051 boolean leap = dt.isLeapYear(); //false
052 dt.getNumDaysInMonth(); //31
053 dt.getStartOfMonth(); //2010-01-01, 00:00:00
054 dt.getEndOfDay(); //2010-01-15, 23:59:59
055 dt.format("YYYY-MM-DD"); //formats as '2010-01-15'
056 dt.plusDays(30); //30 days after Jan 15
057 dt.numDaysFrom(someDate); //returns an int
058 dueDate.lt(someDate); //less-than
059 dueDate.lteq(someDate); //less-than-or-equal-to
060
061 DateTime.now(aTimeZone);
062 DateTime.today(aTimeZone);
063 DateTime fromMilliseconds = DateTime.forInstant(31313121L, aTimeZone);
064 birthday.isInFuture(aTimeZone);
065 </PRE>
066
067 <a name='JustificationForThisClass'></a>
068 <h3> Justification For This Class</h3>
069 The fundamental reasons why this class exists are :
070 <ul>
071 <li>to avoid the embarrassing number of distasteful inadequacies in the JDK's date classes
072 <li>to oppose the very "mental model" of the JDK's date-time classes with something significantly simpler
073 </ul>
074
075 <a name='MentalModels'></a>
076 <P><b>There are 2 distinct mental models for date-times, and they don't play well together</b> :
077 <ul>
078 <li><b>timeline</b> - an instant on the timeline, as a physicist would picture it, representing the number of
079 seconds from some epoch. In this picture, such a date-time can have many, many different
080 representations according to calendar and time zone. That is, the date-time, <i> as seen and understood by
081 the end user</i>, can change according to "who's looking at it". It's important to understand that a timeline instant,
082 before being presented to the user, <i>must always have an associated time zone - even in the case of
083 a date only, with no time.</i>
084 <li><b>everyday</b> - a date-time in the Gregorian calendar, such as '2009-05-25 18:25:00',
085 which never changes according to "who's looking at it". Here, <i>the time zone is always both implicit and immutable</i>.
086 </ul>
087
088 <P>The problem is that java.util.{@link java.util.Date} uses <i>only</i> the timeline style, while <i>most</i> users, <i>most</i>
089 of the time, think in terms of the <i>other</i> mental model - the 'everday' style.
090
091 In particular, there are a large number of applications which experience
092 <a href='http://martinfowler.com/bliki/TimeZoneUncertainty.html'>problems with time zones</a>, because the timeline model
093 is used instead of the everday model.
094 <i>Such problems are often seen by end users as serious bugs, because telling people the wrong date or time is often a serious issue.</i>
095 <b>These problems make you look stupid.</b>
096
097 <a name='JDKDatesMediocre'></a>
098 <h4>Date Classes in the JDK are Mediocre</h4>
099 The JDK's classes related to dates are widely regarded as frustrating to work with, for various reasons:
100 <ul>
101 <li>mistakes regarding time zones are very common
102 <li>month indexes are 0-based, leading to off-by-one errors
103 <li>difficulty of calculating simple time intervals
104 <li><tt>java.util.Date</tt> is mutable, but 'building block' classes should be
105 immutable
106 <li>numerous other minor nuisances
107 </ul>
108
109 <a name='JodaTimeDrawbacks'></a>
110 <h4>Joda Time Has Drawbacks As Well</h4>
111 The <a href='http://joda-time.sourceforge.net/'>Joda Time</a> library is used by some programmers as an alternative
112 to the JDK classes. Joda Time has the following drawbacks :
113 <ul>
114 <li>it limits precision to milliseconds. Database timestamp values almost always have a precision of microseconds
115 or even nanoseconds. This is a serious defect: <b>a library should never truncate your data, for any reason.</b>
116 <li>it's large, with well over 100 items in its <a href='http://joda-time.sourceforge.net/api-release/index.html'>javadoc</a>
117 <li>in order to stay current, it needs to be manually updated occasionally with fresh time zone data
118 <li>it has mutable versions of classes
119 <li>it always coerces March 31 + 1 Month to April 30 (for example), without giving you any choice in the matter
120 <li>some databases allow invalid date values such as '0000-00-00', but Joda Time doesn't seem to be able to handle them
121 </ul>
122
123
124 <a name='DatesAndTimesInGeneral'></a>
125 <h3>Dates and Times in General</h3>
126
127 <h4>Civil Timekeeping Is Complex</h4>
128 Civil timekeeping is a byzantine hodge-podge of arcane and arbitrary rules. Consider the following :
129 <ul>
130 <li>months have varying numbers of days
131 <li>one month (February) has a length which depends on the year
132 <li>not all years have the same number of days
133 <li>time zone rules spring forth arbitrarily from the fecund imaginations of legislators
134 <li>summer hours mean that an hour is 'lost' in the spring, while another hour must
135 repeat itself in the autumn, during the switch back to normal time
136 <li>summer hour logic varies widely across various jurisdictions
137 <li>the cutover from the Julian calendar to the Gregorian calendar happened at different times in
138 different places, which causes a varying number of days to be 'lost' during the cutover
139 <li>occasional insertion of leap seconds are used to ensure synchronization with the
140 rotating Earth (whose speed of rotation is gradually slowing down, in an irregular way)
141 <li>there is no year 0 (1 BC is followed by 1 AD), except in the reckoning used by
142 astronomers
143 </ul>
144
145 <h4>How Databases Treat Dates</h4>
146 <b>Most databases model dates and times using the Gregorian Calendar in an aggressively simplified form</b>,
147 in which :
148 <ul>
149 <li>the Gregorian calendar is extended back in time as if it was in use previous to its
150 inception (the 'proleptic' Gregorian calendar)
151 <li>the transition between Julian and Gregorian calendars is entirely ignored
152 <li>leap seconds are entirely ignored
153 <li>summer hours are entirely ignored
154 <li>often, even time zones are ignored, in the sense that <i>the underlying database
155 column doesn't usually explicitly store any time zone information</i>.
156 </ul>
157
158 <P><a name='NoTimeZoneInDb'></a>The final point requires elaboration.
159 Some may doubt its veracity, since they have seen date-time information "change time zone" when
160 retrieved from a database. But this sort of change is usually applied using logic which is <i>external</i> to the data
161 stored in the particular column.
162
163 <P> For example, the following items might be used in the calculation of a time zone difference :
164 <ul>
165 <li>time zone setting for the client (or JDBC driver)
166 <li>time zone setting for the client's connection to the database server
167 <li>time zone setting of the database server
168 <li>time zone setting of the host where the database server resides
169 </ul>
170
171 <P>(Note as well what's <i>missing</i> from the above list: your own application's logic, and the user's time zone preference.)
172
173 <P>When an end user sees such changes to a date-time, all they will say to you is
174 <i>"Why did you change it? That's not what I entered"</i> - and this is a completely valid question.
175 Why <i>did</i> you change it? Because you're using the timeline model instead of the everyday model.
176 Perhaps you're using a inappropriate abstraction for what the user really wants.
177
178 <a name='TheApproachUsedByThisClass'></a>
179 <h3>The Approach Used By This Class</h3>
180
181 This class takes the following design approach :
182 <ul>
183 <li>it models time in the "everyday" style, not in the "timeline" style (see <a href='#MentalModels'>above</a>)
184 <li>its precision matches the highest precision used by databases (nanosecond)
185 <li>it uses only the proleptic Gregorian Calendar, over the years <tt>1..9999</tt>
186 <li><i>it ignores all non-linearities</i>: summer-hours, leap seconds, and the cutover
187 from Julian to Gregorian calendars
188 <li><i>it ignores time zones</i>. Most date-times are stored in columns whose type
189 does <i>not</i> include time zone information (see note <a href='#NoTimeZoneInDb'>above</a>).
190 <li>it has (very basic) support for wonky dates, such as the magic value <tt>0000-00-00</tt> used by MySQL
191 <li>it's immutable
192 <li>it lets you choose among 4 policies for 'day overflow' conditions during calculations
193 </ul>
194
195 <P>Even though the above list may appear restrictive, it's very likely true that
196 <tt>DateTime</tt> can handle the dates and times you're currently storing in your database.
197
198 <a name='TwoSetsOfOperations'></a>
199 <h3>Two Sets Of Operations</h3>
200 This class allows for 2 sets of operations: a few "basic" operations, and many "computational" ones.
201
202 <P><b>Basic operations</b> model the date-time as a simple, dumb String, with absolutely no parsing or substructure.
203 This will always allow your application to reflect exactly what is in a <tt>ResultSet</tt>, with
204 absolutely no modification for time zone, locale, or for anything else.
205
206 <P>This is meant as a back-up, to ensure that <i>your application will always be able
207 to, at the very least, display a date-time exactly as it appears in your
208 <tt>ResultSet</tt> from the database</i>. This style is particularly useful for handling invalid
209 dates such as <tt>2009-00-00</tt>, which can in fact be stored by some databases (MySQL, for
210 example). It can also be used to handle unusual items, such as MySQL's
211 <a href='http://dev.mysql.com/doc/refman/5.1/en/time.html'>TIME</a> datatype.
212
213 <P>The basic operations are represented by {@link #DateTime(String)}, {@link #toString()}, and {@link #getRawDateString()}.
214
215 <P><b>Computational operations</b> allow for calculations and formatting.
216 If a computational operation is performed by this class (for example, if the caller asks for the month),
217 then any underlying date-time String must be parseable by this class into its components - year, month, day, and so on.
218 Computational operations require such parsing, while the basic operations do not. Almost all methods in this class
219 are categorized as computational operations.
220
221 <a name="ParsingDateTimeAcceptedFormats"></a>
222 <h3>Parsing DateTime - Accepted Formats</h3>
223 The {@link #DateTime(String)} constructor accepts a <tt>String</tt> representation of a date-time.
224 The format of the String can take a number of forms. When retrieving date-times from a database, the
225 majority of cases will have little problem in conforming to these formats. If necessary, your SQL statements
226 can almost always use database formatting functions to generate a String whose format conforms to one of the
227 many formats accepted by the {@link #DateTime(String)} constructor.
228
229 <p>The {@link #isParseable(String)} method lets you explicitly test if a given String is in a form that can be parsed by this class.
230
231 <a name="FormattingLanguage"></a>
232 <h3>Mini-Language for Formatting</h3>
233 This class defines a simple mini-language for formatting a <tt>DateTime</tt>, used by the various <tt>format</tt> methods.
234
235 <P>The following table defines the symbols used by this mini-language, and the corresponding text they
236 would generate given the date:
237 <PRE>1958-04-09 Wednesday, 03:05:06.123456789 AM</PRE>
238 in an English Locale. (Items related to date are in upper case, and items related to time are in lower case.)
239
240 <P><table border='1' cellpadding='3' cellspacing='0'>
241 <tr><th>Format</th><th>Output</th> <th>Description</th><th>Needs Locale?</th></tr>
242 <tr><td>YYYY</td> <td>1958</td> <td>Year</td><td>...</td></tr>
243 <tr><td>YY</td> <td>58</td> <td>Year without century</td><td>...</td></tr>
244 <tr><td>M</td> <td>4</td> <td>Month 1..12</td><td>...</td></tr>
245 <tr><td>MM</td> <td>04</td> <td>Month 01..12</td><td>...</td></tr>
246 <tr><td>MMM</td> <td>Apr</td> <td>Month Jan..Dec</td><td>Yes</td></tr>
247 <tr><td>MMMM</td> <td>April</td> <td>Month January..December</td><td>Yes</td></tr>
248 <tr><td>DD</td> <td>09</td> <td>Day 01..31</td><td>...</td></tr>
249 <tr><td>D</td> <td>9</td> <td>Day 1..31</td><td>...</td></tr>
250 <tr><td>WWWW</td> <td>Wednesday</td> <td>Weekday Sunday..Saturday</td><td>Yes</td></tr>
251 <tr><td>WWW</td> <td>Wed</td> <td>Weekday Sun..Sat</td><td>Yes</td></tr>
252 <tr><td>hh</td> <td>03</td> <td>Hour 01..23</td><td>...</td></tr>
253 <tr><td>h</td> <td>3</td> <td>Hour 1..23</td><td>...</td></tr>
254 <tr><td>hh12</td> <td>03</td> <td>Hour 01..12</td><td>...</td></tr>
255 <tr><td>h12</td> <td>3</td> <td>Hour 1..12</td><td>...</td></tr>
256 <tr><td>a</td> <td>AM</td> <td>AM/PM Indicator</td><td>Yes</td></tr>
257 <tr><td>mm</td> <td>05</td> <td>Minutes 01..59</td><td>...</td></tr>
258 <tr><td>m</td> <td>5</td> <td>Minutes 1..59</td><td>...</td></tr>
259 <tr><td>ss</td> <td>06</td> <td>Seconds 01..59</td><td>...</td></tr>
260 <tr><td>s</td> <td>6</td> <td>Seconds 1..59</td><td>...</td></tr>
261 <tr><td>f</td> <td>1</td> <td>Fractional Seconds, 1 decimal</td><td>...</td></tr>
262 <tr><td>ff</td> <td>12</td> <td>Fractional Seconds, 2 decimals</td><td>...</td></tr>
263 <tr><td>fff</td> <td>123</td> <td>Fractional Seconds, 3 decimals</td><td>...</td></tr>
264 <tr><td>ffff</td> <td>1234</td> <td>Fractional Seconds, 4 decimals</td><td>...</td></tr>
265 <tr><td>fffff</td> <td>12345</td> <td>Fractional Seconds, 5 decimals</td><td>...</td></tr>
266 <tr><td>ffffff</td> <td>123456</td> <td>Fractional Seconds, 6 decimals</td><td>...</td></tr>
267 <tr><td>fffffff</td> <td>1234567</td> <td>Fractional Seconds, 7 decimals</td><td>...</td></tr>
268 <tr><td>ffffffff</td> <td>12345678</td> <td>Fractional Seconds, 8 decimals</td><td>...</td></tr>
269 <tr><td>fffffffff</td> <td>123456789</td> <td>Fractional Seconds, 9 decimals</td><td>...</td></tr>
270 <tr><td>|</td> <td>(no example)</td> <td>Escape characters</td><td>...</td></tr>
271 </table>
272
273 <P>As indicated above, some of these symbols can only be used with an accompanying <tt>Locale</tt>.
274 In general, if the output is text, not a number, then a <tt>Locale</tt> will be needed.
275 For example, 'September' is localizable text, while '09' is a numeric representation, which doesn't require a <tt>Locale</tt>.
276 Thus, the symbol 'MM' can be used without a <tt>Locale</tt>, while 'MMMM' and 'MMM' both require a <tt>Locale</tt>, since they
277 generate text, not a number.
278
279 <P>The fractional seconds 'f' doesn't perform any rounding.
280
281 <P>The escape character '|' allows you
282 to insert arbitrary text. The escape character always appears in pairs; these pairs define a range of characters
283 over which the text will not be interpreted using the special format symbols defined above.
284
285 <P>Here are some practical examples of using the above formatting symbols:
286 <table border='1' cellpadding='3' cellspacing='0'>
287 <tr><th>Format</th><th>Output</th></tr>
288 <tr><td>YYYY-MM-DD hh:mm:ss.fffffffff a</td> <td>1958-04-09 03:05:06.123456789 AM</td></tr>
289 <tr><td>YYYY-MM-DD hh:mm:ss.fff a</td> <td>1958-04-09 03:05:06.123 AM</td></tr>
290 <tr><td>YYYY-MM-DD</td> <td>1958-04-09</td></tr>
291 <tr><td>hh:mm:ss.fffffffff</td> <td>03:05:06.123456789</td></tr>
292 <tr><td>hh:mm:ss</td> <td>03:05:06</td></tr>
293 <tr><td>YYYY-M-D h:m:s</td> <td>1958-4-9 3:5:6</td></tr>
294 <tr><td>WWWW, MMMM D, YYYY</td> <td>Wednesday, April 9, 1958</td></tr>
295 <tr><td>WWWW, MMMM D, YYYY |at| D a</td> <td>Wednesday, April 9, 1958 at 3 AM</td></tr>
296 </table>
297
298 <P>In the last example, the escape characters are needed only because 'a', the formating symbol for am/pm, appears in the text.
299
300 <a name='PassingDateTimeToTheDatabase'></a>
301 <h3>Passing DateTime Objects to the Database</h3>
302 When a <tt>DateTime</tt> is passed as a parameter to an SQL statement, the <tt>DateTime</tt> can always
303 be formatted into a <tt>String</tt> of a form accepted by the database, using one of the <tt>format</tt> methods.
304 */
305 public final class DateTime implements Comparable<DateTime>, Serializable {
306
307 /** The seven parts of a <tt>DateTime</tt> object. The <tt>DAY</tt> represents the day of the month (1..31), not the weekday. */
308 public enum Unit {
309 YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, NANOSECONDS;
310 }
311
312 /**
313 Policy for treating 'day-of-the-month overflow' conditions encountered during some date calculations.
314
315 <P>Months are different from other units of time, since the length of a month is not fixed, but rather varies with
316 both month and year. This leads to problems. Take the following simple calculation, for example :
317
318 <PRE>May 31 + 1 month = ?</PRE>
319
320 <P>What's the answer? Since there is no such thing as June 31, the result of this operation is inherently ambiguous.
321 This <tt>DayOverflow</tt> enumeration lists the various policies for treating such situations, as supported by
322 <tt>DateTime</tt>.
323
324 <P>This table illustrates how the policies behave :
325 <P><table BORDER="1" CELLPADDING="3" CELLSPACING="0">
326 <tr>
327 <th>Date</th>
328 <th>DayOverflow</th>
329 <th>Result</th>
330 </tr>
331 <tr>
332 <td>May 31 + 1 Month</td>
333 <td>LastDay</td>
334 <td>June 30</td>
335 </tr>
336 <tr>
337 <td>May 31 + 1 Month</td>
338 <td>FirstDay</td>
339 <td>July 1</td>
340 </tr>
341 <tr>
342 <td>December 31, 2001 + 2 Months</td>
343 <td>Spillover</td>
344 <td>March 3</td>
345 </tr>
346 <tr>
347 <td>May 31 + 1 Month</td>
348 <td>Abort</td>
349 <td>RuntimeException</td>
350 </tr>
351 </table>
352 */
353 public enum DayOverflow {
354 /** Coerce the day to the last day of the month. */
355 LastDay,
356 /** Coerce the day to the first day of the next month. */
357 FirstDay,
358 /** Spillover the day into the next month. */
359 Spillover,
360 /** Throw a RuntimeException. */
361 Abort;
362 }
363
364 /**
365 Constructor taking a date-time as a String. The text is trimmed by this class.
366
367 <P> When this constructor is called, the underlying text can be in an absolutely arbitrary
368 form, since it will not, initially, be parsed in any way. This policy of extreme
369 leniency allows you to use dates in an arbitrary format, without concern over possible
370 transformations of the date (time zone in particular), and without concerns over possibly bizarre content, such
371 as '2005-00-00', as seen in some databases, such as MySQL.
372
373 <P><i>However</i>, the moment you attempt to call <a href='#TwoSetsOfOperations'>almost any method</a>
374 in this class, an attempt will be made to parse
375 the given date-time string into its constituent parts. Then, if the date-time string does not match one of the
376 example formats listed below, a <tt>RuntimeException</tt> will be thrown.
377
378 <P>Before calling this constructor, you may wish to call {@link #isParseable(String)} to explicitly test whether a
379 given String is parseable by this class.
380
381 <P>The full date format expected by this class is <tt>'YYYY-MM-YY hh:mm:ss.fffffffff'</tt>.
382 All fields except for the fraction of a second have a fixed width.
383 In addition, various portions of this format are also accepted by this class.
384
385 <P>All of the following dates can be parsed by this class to make a <tt>DateTime</tt> :
386 <ul>
387 <li><tt>2009-12-31 00:00:00.123456789</tt>
388 <li><tt>2009-12-31T00:00:00.123456789</tt>
389 <li><tt>2009-12-31 00:00:00.12345678</tt>
390 <li><tt>2009-12-31 00:00:00.1234567</tt>
391 <li><tt>2009-12-31 00:00:00.123456</tt>
392 <li><tt>2009-12-31 23:59:59.12345</tt>
393 <li><tt>2009-01-31 16:01:01.1234</tt>
394 <li><tt>2009-01-01 16:59:00.123</tt>
395 <li><tt>2009-01-01 16:00:01.12</tt>
396 <li><tt>2009-02-28 16:25:17.1</tt>
397 <li><tt>2009-01-01 00:01:01</tt>
398 <li><tt>2009-01-01T00:01:01</tt>
399 <li><tt>2009-01-01 16:01</tt>
400 <li><tt>2009-01-01 16</tt>
401 <li><tt>2009-01-01</tt>
402 <li><tt>2009-01</tt>
403 <li><tt>2009</tt>
404 <li><tt>0009</tt>
405 <li><tt>9</tt>
406 <li><tt>00:00:00.123456789</tt>
407 <li><tt>00:00:00.12345678</tt>
408 <li><tt>00:00:00.1234567</tt>
409 <li><tt>00:00:00.123456</tt>
410 <li><tt>23:59:59.12345</tt>
411 <li><tt>01:59:59.1234</tt>
412 <li><tt>23:01:59.123</tt>
413 <li><tt>00:00:00.12</tt>
414 <li><tt>00:59:59.1</tt>
415 <li><tt>23:59:00</tt>
416 <li><tt>23:00:10</tt>
417 <li><tt>00:59</tt>
418 </ul>
419
420 <P>The range of each field is :
421 <ul>
422 <li>year: 1..9999 (leading zeroes optional)
423 <li>month: 01..12
424 <li>day: 01..31
425 <li>hour: 00..23
426 <li>minute: 00..59
427 <li>second: 00..59
428 <li>nanosecond: 0..999999999
429 </ul>
430
431 <P>Note that <b>database format functions</b> are an option when dealing with date formats.
432 Since your application is always in control of the SQL used to talk to the database, you can, if needed, usually
433 use database format functions to alter the format of dates returned in a <tt>ResultSet</tt>.
434 */
435 public DateTime(String aDateTime) {
436 fIsAlreadyParsed = false;
437 if (aDateTime == null) {
438 throw new IllegalArgumentException("String passed to DateTime constructor is null. You can use an empty string, but not a null reference.");
439 }
440 fDateTime = aDateTime;
441 }
442
443 /**
444 Return <tt>true</tt> only if the given String follows one of the formats documented by {@link #DateTime(String)}.
445 <P>If the text is not from a trusted source, then the caller may use this method to validate whether the text
446 is in a form that's parseable by this class.
447 */
448 public static boolean isParseable(String aCandidateDateTime){
449 boolean result = true;
450 try {
451 DateTime dt = new DateTime(aCandidateDateTime);
452 dt.ensureParsed();
453 }
454 catch (RuntimeException ex){
455 result = false;
456 }
457 return result;
458 }
459
460 /**
461 Constructor taking each time unit explicitly.
462
463 <P>Although all parameters are optional, many operations on this class require year-month-day to be
464 present.
465
466 @param aYear 1..9999, optional
467 @param aMonth 1..12 , optional
468 @param aDay 1..31, cannot exceed the number of days in the given month/year, optional
469 @param aHour 0..23, optional
470 @param aMinute 0..59, optional
471 @param aSecond 0..59, optional
472 @param aNanoseconds 0..999,999,999, optional (allows for databases that store timestamps up to
473 nanosecond precision).
474 */
475 public DateTime(Integer aYear, Integer aMonth, Integer aDay, Integer aHour, Integer aMinute, Integer aSecond, Integer aNanoseconds) {
476 fIsAlreadyParsed = true;
477 fYear = aYear;
478 fMonth = aMonth;
479 fDay = aDay;
480 fHour = aHour;
481 fMinute = aMinute;
482 fSecond = aSecond;
483 fNanosecond = aNanoseconds;
484 validateState();
485 }
486
487 /**
488 Factory method returns a <tt>DateTime</tt> having year-month-day only, with no time portion.
489 <P>See {@link #DateTime(Integer, Integer, Integer, Integer, Integer, Integer, Integer)} for constraints on the parameters.
490 */
491 public static DateTime forDateOnly(Integer aYear, Integer aMonth, Integer aDay) {
492 return new DateTime(aYear, aMonth, aDay, null, null, null, null);
493 }
494
495 /**
496 Factory method returns a <tt>DateTime</tt> having hour-minute-second-nanosecond only, with no date portion.
497 <P>See {@link #DateTime(Integer, Integer, Integer, Integer, Integer, Integer, Integer)} for constraints on the parameters.
498 */
499 public static DateTime forTimeOnly(Integer aHour, Integer aMinute, Integer aSecond, Integer aNanoseconds) {
500 return new DateTime(null, null, null, aHour, aMinute, aSecond, aNanoseconds);
501 }
502
503 /**
504 Constructor taking a millisecond value and a {@link TimeZone}.
505 This constructor may be use to convert a <tt>java.util.Date</tt> into a <tt>DateTime</tt>.
506
507 <P>To use nanosecond precision, please use {@link #forInstantNanos(long, TimeZone)} instead.
508
509 @param aMilliseconds must be in the range corresponding to the range of dates supported by this class (year 1..9999); corresponds
510 to a millisecond instant on the time-line, measured from the epoch used by {@link java.util.Date}.
511 */
512 public static DateTime forInstant(long aMilliseconds, TimeZone aTimeZone) {
513 Calendar calendar = new GregorianCalendar(aTimeZone);
514 calendar.setTimeInMillis(aMilliseconds);
515 int year = calendar.get(Calendar.YEAR);
516 int month = calendar.get(Calendar.MONTH) + 1; // 0-based
517 int day = calendar.get(Calendar.DAY_OF_MONTH);
518 int hour = calendar.get(Calendar.HOUR_OF_DAY); // 0..23
519 int minute = calendar.get(Calendar.MINUTE);
520 int second = calendar.get(Calendar.SECOND);
521 int milliseconds = calendar.get(Calendar.MILLISECOND);
522 int nanoseconds = milliseconds * 1000 * 1000;
523 return new DateTime(year, month, day, hour, minute, second, nanoseconds);
524 }
525
526 /**
527 For the given time zone, return the corresponding time in milliseconds-since-epoch for this <tt>DateTime</tt>.
528
529 <P>This method is meant to help you convert between a <tt>DateTime</tt> and the
530 JDK's date-time classes, which are based on the combination of a time zone and a
531 millisecond value from the Java epoch.
532 <P>Since <tt>DateTime</tt> can go to nanosecond accuracy, the return value can
533 lose precision. The nanosecond value is truncated to milliseconds, not rounded.
534 To retain nanosecond accuracy, please use {@link #getNanosecondsInstant(TimeZone)} instead.
535 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
536 */
537 public long getMilliseconds(TimeZone aTimeZone){
538 Integer year = getYear();
539 Integer month = getMonth();
540 Integer day = getDay();
541 //coerce missing times to 0:
542 Integer hour = getHour() == null ? 0 : getHour();
543 Integer minute = getMinute() == null ? 0 : getMinute();
544 Integer second = getSecond() == null ? 0 : getSecond();
545 Integer nanos = getNanoseconds() == null ? 0 : getNanoseconds();
546
547 Calendar calendar = new GregorianCalendar(aTimeZone);
548 calendar.set(Calendar.YEAR, year);
549 calendar.set(Calendar.MONTH, month-1); // 0-based
550 calendar.set(Calendar.DAY_OF_MONTH, day);
551 calendar.set(Calendar.HOUR_OF_DAY, hour); // 0..23
552 calendar.set(Calendar.MINUTE, minute);
553 calendar.set(Calendar.SECOND, second);
554 calendar.set(Calendar.MILLISECOND, nanos/1000000);
555
556 return calendar.getTimeInMillis();
557 }
558
559 /**
560 Constructor taking a nanosecond value and a {@link TimeZone}.
561
562 <P>To use milliseconds instead of nanoseconds, please use {@link #forInstant(long, TimeZone)}.
563
564 @param aNanoseconds must be in the range corresponding to the range of dates supported by this class (year 1..9999); corresponds
565 to a nanosecond instant on the time-line, measured from the epoch used by {@link java.util.Date}.
566 */
567 public static DateTime forInstantNanos(long aNanoseconds, TimeZone aTimeZone) {
568 //these items can be of either sign
569 long millis = aNanoseconds / MILLION; //integer division truncates towards 0, doesn't round
570 long nanosRemaining = aNanoseconds % MILLION; //size 0..999,999
571 //when negative: go to the previous millis, and take the complement of nanosRemaining
572 if(aNanoseconds < 0){
573 millis = millis - 1;
574 nanosRemaining = MILLION + nanosRemaining; //-1 remaining coerced to 999,999
575 }
576
577 //base calculation in millis
578 Calendar calendar = new GregorianCalendar(aTimeZone);
579 calendar.setTimeInMillis(millis);
580 int year = calendar.get(Calendar.YEAR);
581 int month = calendar.get(Calendar.MONTH) + 1; // 0-based
582 int day = calendar.get(Calendar.DAY_OF_MONTH);
583 int hour = calendar.get(Calendar.HOUR_OF_DAY); // 0..23
584 int minute = calendar.get(Calendar.MINUTE);
585 int second = calendar.get(Calendar.SECOND);
586 int milliseconds = calendar.get(Calendar.MILLISECOND);
587
588 DateTime withoutNanos = new DateTime(year, month, day, hour, minute, second, milliseconds * MILLION);
589 //adjust for nanos - this cast is acceptable, because the value's range is 0..999,999:
590 DateTime withNanos = withoutNanos.plus(0, 0, 0, 0, 0, 0, (int)nanosRemaining, DayOverflow.Spillover);
591 return withNanos;
592 }
593
594 /**
595 For the given time zone, return the corresponding time in nanoseconds-since-epoch for this <tt>DateTime</tt>.
596
597 <P>For conversion between a <tt>DateTime</tt> and the JDK's date-time classes,
598 you should likely use {@link #getMilliseconds(TimeZone)} instead.
599 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
600 */
601 public long getNanosecondsInstant(TimeZone aTimeZone){
602 // these are always positive:
603 Integer year = getYear();
604 Integer month = getMonth();
605 Integer day = getDay();
606 //coerce missing times to 0:
607 Integer hour = getHour() == null ? 0 : getHour();
608 Integer minute = getMinute() == null ? 0 : getMinute();
609 Integer second = getSecond() == null ? 0 : getSecond();
610 Integer nanos = getNanoseconds() == null ? 0 : getNanoseconds();
611
612 int millis = nanos / MILLION; //integer division truncates, doesn't round
613 int nanosRemaining = nanos % MILLION; //0..999,999 - always positive
614
615 //base calculation in millis
616 Calendar calendar = new GregorianCalendar(aTimeZone);
617 calendar.set(Calendar.YEAR, year);
618 calendar.set(Calendar.MONTH, month-1); // 0-based
619 calendar.set(Calendar.DAY_OF_MONTH, day);
620 calendar.set(Calendar.HOUR_OF_DAY, hour); // 0..23
621 calendar.set(Calendar.MINUTE, minute);
622 calendar.set(Calendar.SECOND, second);
623 calendar.set(Calendar.MILLISECOND, millis);
624
625 long baseResult = calendar.getTimeInMillis() * MILLION; // either sign
626 //the adjustment for nanos is always positive, toward the future:
627 return baseResult + nanosRemaining;
628 }
629
630 /**
631 Return the raw date-time String passed to the {@link #DateTime(String)} constructor.
632 Returns <tt>null</tt> if that constructor was not called. See {@link #toString()} as well.
633 */
634 public String getRawDateString() {
635 return fDateTime;
636 }
637
638 /** Return the year, 1..9999. */
639 public Integer getYear() {
640 ensureParsed();
641 return fYear;
642 }
643
644 /** Return the Month, 1..12. */
645 public Integer getMonth() {
646 ensureParsed();
647 return fMonth;
648 }
649
650 /** Return the Day of the Month, 1..31. */
651 public Integer getDay() {
652 ensureParsed();
653 return fDay;
654 }
655
656 /** Return the Hour, 0..23. */
657 public Integer getHour() {
658 ensureParsed();
659 return fHour;
660 }
661
662 /** Return the Minute, 0..59. */
663 public Integer getMinute() {
664 ensureParsed();
665 return fMinute;
666 }
667
668 /** Return the Second, 0..59. */
669 public Integer getSecond() {
670 ensureParsed();
671 return fSecond;
672 }
673
674 /** Return the Nanosecond, 0..999999999. */
675 public Integer getNanoseconds() {
676 ensureParsed();
677 return fNanosecond;
678 }
679
680 /**
681 Return the Modified Julian Day Number.
682 <P>The Modified Julian Day Number is defined by astronomers for simplifying the calculation of the number of days between 2 dates.
683 Returns a monotonically increasing sequence number.
684 Day 0 is November 17, 1858 00:00:00 (whose Julian Date was 2400000.5).
685
686 <P>Using the Modified Julian Day Number instead of the Julian Date has 2 advantages:
687 <ul>
688 <li>it's a smaller number
689 <li>it starts at midnight, not noon (Julian Date starts at noon)
690 </ul>
691
692 <P>Does not reflect any time portion, if present.
693
694 <P>(In spite of its name, this method, like all other methods in this class, uses the
695 proleptic Gregorian calendar - not the Julian calendar.)
696
697 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
698 */
699 public Integer getModifiedJulianDayNumber() {
700 ensureHasYearMonthDay();
701 int result = calculateJulianDayNumberAtNoon() - 1 - EPOCH_MODIFIED_JD;
702 return result;
703 }
704
705 /**
706 Return an index for the weekday for this <tt>DateTime</tt>.
707 Returns 1..7 for Sunday..Saturday.
708 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
709 */
710 public Integer getWeekDay() {
711 ensureHasYearMonthDay();
712 int dayNumber = calculateJulianDayNumberAtNoon() + 1;
713 int index = dayNumber % 7;
714 return index + 1;
715 }
716
717 /**
718 Return an integer in the range 1..366, representing a count of the number of days from the start of the year.
719 January 1 is counted as day 1.
720 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
721 */
722 public Integer getDayOfYear() {
723 ensureHasYearMonthDay();
724 int k = isLeapYear() ? 1 : 2;
725 Integer result = ((275 * fMonth) / 9) - k * ((fMonth + 9) / 12) + fDay - 30; // integer division
726 return result;
727 }
728
729 /**
730 Returns true only if the year is a leap year.
731 <P>Requires year to be present; if not, a runtime exception is thrown.
732 */
733 public Boolean isLeapYear() {
734 ensureParsed();
735 Boolean result = null;
736 if (isPresent(fYear)) {
737 result = isLeapYear(fYear);
738 }
739 else {
740 throw new MissingItem("Year is absent. Cannot determine if leap year.");
741 }
742 return result;
743 }
744
745 /**
746 Return the number of days in the month which holds this <tt>DateTime</tt>.
747 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
748 */
749 public int getNumDaysInMonth() {
750 ensureHasYearMonthDay();
751 return getNumDaysInMonth(fYear, fMonth);
752 }
753
754 /**
755 Return The week index of this <tt>DateTime</tt> with respect to a given starting <tt>DateTime</tt>.
756 <P>The single parameter to this method defines first day of week number 1.
757 See {@link #getWeekIndex()} as well.
758 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
759 */
760 public Integer getWeekIndex(DateTime aStartingFromDate) {
761 ensureHasYearMonthDay();
762 aStartingFromDate.ensureHasYearMonthDay();
763 int diff = getModifiedJulianDayNumber() - aStartingFromDate.getModifiedJulianDayNumber();
764 return (diff / 7) + 1; // integer division
765 }
766
767 /**
768 Return The week index of this <tt>DateTime</tt>, taking day 1 of week 1 as Sunday, January 2, 2000.
769 <P>See {@link #getWeekIndex(DateTime)} as well, which takes an arbitrary date to define
770 day 1 of week 1.
771 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
772 */
773 public Integer getWeekIndex() {
774 DateTime start = DateTime.forDateOnly(2000, 1, 2);
775 return getWeekIndex(start);
776 }
777
778 /**
779 Return <tt>true</tt> only if this <tt>DateTime</tt> has the same year-month-day as the given parameter.
780 Time is ignored by this method.
781 <P> Requires year-month-day to be present, both for this <tt>DateTime</tt> and for
782 <tt>aThat</tt>; if not, a runtime exception is thrown.
783 */
784 public boolean isSameDayAs(DateTime aThat) {
785 boolean result = false;
786 ensureHasYearMonthDay();
787 aThat.ensureHasYearMonthDay();
788 result = (fYear.equals(aThat.fYear) && fMonth.equals(aThat.fMonth) && fDay.equals(aThat.fDay));
789 return result;
790 }
791
792 /**
793 'Less than' comparison.
794 Return <tt>true</tt> only if this <tt>DateTime</tt> comes before the given parameter, according to {@link #compareTo(DateTime)}.
795 */
796 public boolean lt(DateTime aThat) {
797 return compareTo(aThat) < EQUAL;
798 }
799
800 /**
801 'Less than or equal to' comparison.
802 Return <tt>true</tt> only if this <tt>DateTime</tt> comes before the given parameter, according to {@link #compareTo(DateTime)},
803 or this <tt>DateTime</tt> equals the given parameter.
804 */
805 public boolean lteq(DateTime aThat) {
806 return compareTo(aThat) < EQUAL || equals(aThat);
807 }
808
809 /**
810 'Greater than' comparison.
811 Return <tt>true</tt> only if this <tt>DateTime</tt> comes after the given parameter, according to {@link #compareTo(DateTime)}.
812 */
813 public boolean gt(DateTime aThat) {
814 return compareTo(aThat) > EQUAL;
815 }
816
817 /**
818 'Greater than or equal to' comparison.
819 Return <tt>true</tt> only if this <tt>DateTime</tt> comes after the given parameter, according to {@link #compareTo(DateTime)},
820 or this <tt>DateTime</tt> equals the given parameter.
821 */
822 public boolean gteq(DateTime aThat) {
823 return compareTo(aThat) > EQUAL || equals(aThat);
824 }
825
826 /** Return the smallest non-null time unit encapsulated by this <tt>DateTime</tt>. */
827 public Unit getPrecision() {
828 ensureParsed();
829 Unit result = null;
830 if (isPresent(fNanosecond)) {
831 result = Unit.NANOSECONDS;
832 }
833 else if (isPresent(fSecond)) {
834 result = Unit.SECOND;
835 }
836 else if (isPresent(fMinute)) {
837 result = Unit.MINUTE;
838 }
839 else if (isPresent(fHour)) {
840 result = Unit.HOUR;
841 }
842 else if (isPresent(fDay)) {
843 result = Unit.DAY;
844 }
845 else if (isPresent(fMonth)) {
846 result = Unit.MONTH;
847 }
848 else if (isPresent(fYear)) {
849 result = Unit.YEAR;
850 }
851 return result;
852 }
853
854 /**
855 Truncate this <tt>DateTime</tt> to the given precision.
856 <P>The return value will have all items lower than the given precision simply set to
857 <tt>null</tt>. In addition, the return value will not include any date-time String passed to the
858 {@link #DateTime(String)} constructor.
859
860 @param aPrecision takes any value <i>except</i> {@link Unit#NANOSECONDS} (since it makes no sense to truncate to the highest
861 available precision).
862 */
863 public DateTime truncate(Unit aPrecision) {
864 ensureParsed();
865 DateTime result = null;
866 if (Unit.NANOSECONDS == aPrecision) {
867 throw new IllegalArgumentException("It makes no sense to truncate to nanosecond precision, since that's the highest precision available.");
868 }
869 else if (Unit.SECOND == aPrecision) {
870 result = new DateTime(fYear, fMonth, fDay, fHour, fMinute, fSecond, null);
871 }
872 else if (Unit.MINUTE == aPrecision) {
873 result = new DateTime(fYear, fMonth, fDay, fHour, fMinute, null, null);
874 }
875 else if (Unit.HOUR == aPrecision) {
876 result = new DateTime(fYear, fMonth, fDay, fHour, null, null, null);
877 }
878 else if (Unit.DAY == aPrecision) {
879 result = new DateTime(fYear, fMonth, fDay, null, null, null, null);
880 }
881 else if (Unit.MONTH == aPrecision) {
882 result = new DateTime(fYear, fMonth, null, null, null, null, null);
883 }
884 else if (Unit.YEAR == aPrecision) {
885 result = new DateTime(fYear, null, null, null, null, null, null);
886 }
887 return result;
888 }
889
890 /**
891 Return <tt>true</tt> only if all of the given units are present in this <tt>DateTime</tt>.
892 If a unit is <i>not</i> included in the argument list, then no test is made for its presence or absence
893 in this <tt>DateTime</tt> by this method.
894 */
895 public boolean unitsAllPresent(Unit... aUnits) {
896 boolean result = true;
897 ensureParsed();
898 for (Unit unit : aUnits) {
899 if (Unit.NANOSECONDS == unit) {
900 result = result && fNanosecond != null;
901 }
902 else if (Unit.SECOND == unit) {
903 result = result && fSecond != null;
904 }
905 else if (Unit.MINUTE == unit) {
906 result = result && fMinute != null;
907 }
908 else if (Unit.HOUR == unit) {
909 result = result && fHour != null;
910 }
911 else if (Unit.DAY == unit) {
912 result = result && fDay != null;
913 }
914 else if (Unit.MONTH == unit) {
915 result = result && fMonth != null;
916 }
917 else if (Unit.YEAR == unit) {
918 result = result && fYear != null;
919 }
920 }
921 return result;
922 }
923
924 /**
925 Return <tt>true</tt> only if this <tt>DateTime</tt> has a non-null values for year, month, and day.
926 */
927 public boolean hasYearMonthDay() {
928 return unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY);
929 }
930
931 /**
932 Return <tt>true</tt> only if this <tt>DateTime</tt> has a non-null values for hour, minute, and second.
933 */
934 public boolean hasHourMinuteSecond() {
935 return unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND);
936 }
937
938 /**
939 Return <tt>true</tt> only if all of the given units are absent from this <tt>DateTime</tt>.
940 If a unit is <i>not</i> included in the argument list, then no test is made for its presence or absence
941 in this <tt>DateTime</tt> by this method.
942 */
943 public boolean unitsAllAbsent(Unit... aUnits) {
944 boolean result = true;
945 ensureParsed();
946 for (Unit unit : aUnits) {
947 if (Unit.NANOSECONDS == unit) {
948 result = result && fNanosecond == null;
949 }
950 else if (Unit.SECOND == unit) {
951 result = result && fSecond == null;
952 }
953 else if (Unit.MINUTE == unit) {
954 result = result && fMinute == null;
955 }
956 else if (Unit.HOUR == unit) {
957 result = result && fHour == null;
958 }
959 else if (Unit.DAY == unit) {
960 result = result && fDay == null;
961 }
962 else if (Unit.MONTH == unit) {
963 result = result && fMonth == null;
964 }
965 else if (Unit.YEAR == unit) {
966 result = result && fYear == null;
967 }
968 }
969 return result;
970 }
971
972 /**
973 Return this <tt>DateTime</tt> with the time portion coerced to '00:00:00.000000000'.
974 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
975 */
976 public DateTime getStartOfDay() {
977 ensureHasYearMonthDay();
978 return getStartEndDateTime(fDay, 0, 0, 0, 0);
979 }
980
981 /**
982 Return this <tt>DateTime</tt> with the time portion coerced to '23:59:59.999999999'.
983 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
984 */
985 public DateTime getEndOfDay() {
986 ensureHasYearMonthDay();
987 return getStartEndDateTime(fDay, 23, 59, 59, 999999999);
988 }
989
990 /**
991 Return this <tt>DateTime</tt> with the time portion coerced to '00:00:00.000000000',
992 and the day coerced to 1.
993 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
994 */
995 public DateTime getStartOfMonth() {
996 ensureHasYearMonthDay();
997 return getStartEndDateTime(1, 0, 0, 0, 0);
998 }
999
1000 /**
1001 Return this <tt>DateTime</tt> with the time portion coerced to '23:59:59.999999999',
1002 and the day coerced to the end of the month.
1003 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
1004 */
1005 public DateTime getEndOfMonth() {
1006 ensureHasYearMonthDay();
1007 return getStartEndDateTime(getNumDaysInMonth(), 23, 59, 59, 999999999);
1008 }
1009
1010 /**
1011 Create a new <tt>DateTime</tt> by adding an interval to this one.
1012
1013 <P>See {@link #plusDays(Integer)} as well.
1014
1015 <P>Changes are always applied by this class <i>in order of decreasing units of time</i>:
1016 years first, then months, and so on. After changing both the year and month, a check on the month-day combination is made before
1017 any change is made to the day. If the day exceeds the number of days in the given month/year, then
1018 (and only then) the given {@link DayOverflow} policy applied, and the day-of-the-month is adusted accordingly.
1019
1020 <P>Afterwards, the day is then changed in the usual way, followed by the remaining items (hour, minute, second, and nanosecond).
1021
1022 <P><em>The mental model for this method is very similar to that of a car's odometer.</em> When a limit is reach for one unit of time,
1023 then a rollover occurs for a neighbouring unit of time.
1024
1025 <P>The returned value cannot come after <tt>9999-12-13 23:59:59</tt>.
1026
1027 <P>This class works with <tt>DateTime</tt>'s having the following items present :
1028 <ul>
1029 <li>year-month-day and hour-minute-second (and optional nanoseconds)
1030 <li>year-month-day only. In this case, if a calculation with a time part is performed, that time part
1031 will be initialized by this class to 00:00:00.0, and the <tt>DateTime</tt> returned by this class will include a time part.
1032 <li>hour-minute-second (and optional nanoseconds) only. In this case, the calculation is done starting with the
1033 the arbitrary date <tt>0001-01-01</tt> (in order to remain within a valid state space of <tt>DateTime</tt>).
1034 </ul>
1035
1036 @param aNumYears positive, required, in range 0...9999
1037 @param aNumMonths positive, required, in range 0...9999
1038 @param aNumDays positive, required, in range 0...9999
1039 @param aNumHours positive, required, in range 0...9999
1040 @param aNumMinutes positive, required, in range 0...9999
1041 @param aNumSeconds positive, required, in range 0...9999
1042 @param aNumNanoseconds positive, required, in range 0...999999999
1043 */
1044 public DateTime plus(Integer aNumYears, Integer aNumMonths, Integer aNumDays, Integer aNumHours, Integer aNumMinutes, Integer aNumSeconds, Integer aNumNanoseconds, DayOverflow aDayOverflow) {
1045 DateTimeInterval interval = new DateTimeInterval(this, aDayOverflow);
1046 return interval.plus(aNumYears, aNumMonths, aNumDays, aNumHours, aNumMinutes, aNumSeconds, aNumNanoseconds);
1047 }
1048
1049 /**
1050 Create a new <tt>DateTime</tt> by subtracting an interval to this one.
1051
1052 <P>See {@link #minusDays(Integer)} as well.
1053 <P>This method has nearly the same behavior as {@link #plus(Integer, Integer, Integer, Integer, Integer, Integer, Integer, DayOverflow)},
1054 except that the return value cannot come before <tt>0001-01-01 00:00:00</tt>.
1055 */
1056 public DateTime minus(Integer aNumYears, Integer aNumMonths, Integer aNumDays, Integer aNumHours, Integer aNumMinutes, Integer aNumSeconds, Integer aNumNanoseconds, DayOverflow aDayOverflow) {
1057 DateTimeInterval interval = new DateTimeInterval(this, aDayOverflow);
1058 return interval.minus(aNumYears, aNumMonths, aNumDays, aNumHours, aNumMinutes, aNumSeconds, aNumNanoseconds);
1059 }
1060
1061 /**
1062 Return a new <tt>DateTime</tt> by adding an integral number of days to this one.
1063
1064 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
1065 @param aNumDays can be either sign; if negative, then the days are subtracted.
1066 */
1067 public DateTime plusDays(Integer aNumDays) {
1068 ensureHasYearMonthDay();
1069 int thisJDAtNoon = getModifiedJulianDayNumber() + 1 + EPOCH_MODIFIED_JD;
1070 int resultJD = thisJDAtNoon + aNumDays;
1071 DateTime datePortion = fromJulianDayNumberAtNoon(resultJD);
1072 return new DateTime(datePortion.getYear(), datePortion.getMonth(), datePortion.getDay(), fHour, fMinute, fSecond, fNanosecond);
1073 }
1074
1075 /**
1076 Return a new <tt>DateTime</tt> by subtracting an integral number of days from this one.
1077
1078 <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
1079 @param aNumDays can be either sign; if negative, then the days are added.
1080 */
1081 public DateTime minusDays(Integer aNumDays) {
1082 return plusDays(-1 * aNumDays);
1083 }
1084
1085 /**
1086 The whole number of days between this <tt>DateTime</tt> and the given parameter.
1087 <P>Requires year-month-day to be present, both for this <tt>DateTime</tt> and for the <tt>aThat</tt>
1088 parameter; if not, a runtime exception is thrown.
1089 */
1090 public int numDaysFrom(DateTime aThat) {
1091 return aThat.getModifiedJulianDayNumber() - this.getModifiedJulianDayNumber();
1092 }
1093
1094 /**
1095 The number of seconds between this <tt>DateTime</tt> and the given argument.
1096 <P>If only time information is present in both this <tt>DateTime</tt> and <tt>aThat</tt>, then there are
1097 no restrictions on the values of the time units.
1098 <P>If any date information is present, in either this <tt>DateTime</tt> or <tt>aThat</tt>,
1099 then full year-month-day must be present in both; if not, then the date portion will be ignored, and only the
1100 time portion will contribute to the calculation.
1101 */
1102 public long numSecondsFrom(DateTime aThat) {
1103 long result = 0;
1104 if(hasYearMonthDay() && aThat.hasYearMonthDay()){
1105 result = numDaysFrom(aThat) * 86400; //just the day portion
1106 }
1107 result = result - this.numSecondsInTimePortion() + aThat.numSecondsInTimePortion();
1108 return result;
1109 }
1110
1111 /**
1112 Output this <tt>DateTime</tt> as a formatted String using numbers, with no localizable text.
1113
1114 <P>Example:
1115 <PRE>dt.format("YYYY-MM-DD hh:mm:ss");</PRE>
1116 would generate text of the form
1117 <PRE>2009-09-09 18:23:59</PRE>
1118
1119 <P>If months, weekdays, or AM/PM indicators are output as localizable text, you must use {@link #format(String, Locale)}.
1120 @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1121 */
1122 public String format(String aFormat) {
1123 DateTimeFormatter format = new DateTimeFormatter(aFormat);
1124 return format.format(this);
1125 }
1126
1127 /**
1128 Output this <tt>DateTime</tt> as a formatted String using numbers and/or localizable text.
1129
1130 <P>This method is intended for alphanumeric output, such as '<tt>Sunday, November 14, 1858 10:00 AM</tt>'.
1131 <P>If months and weekdays are output as numbers, you are encouraged to use {@link #format(String)} instead.
1132
1133 @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1134 @param aLocale used to generate text for Month, Weekday and AM/PM indicator; required only by patterns which return localized
1135 text, instead of numeric forms.
1136 */
1137 public String format(String aFormat, Locale aLocale) {
1138 DateTimeFormatter format = new DateTimeFormatter(aFormat, aLocale);
1139 return format.format(this);
1140 }
1141
1142 /**
1143 Output this <tt>DateTime</tt> as a formatted String using numbers and explicit text for months, weekdays, and AM/PM indicator.
1144
1145 <P>Use of this method is likely relatively rare; it should be used only if the output of {@link #format(String, Locale)} is
1146 inadequate.
1147
1148 @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1149 @param aMonths contains text for all 12 months, starting with January; size must be 12.
1150 @param aWeekdays contains text for all 7 weekdays, starting with Sunday; size must be 7.
1151 @param aAmPmIndicators contains text for A.M and P.M. indicators (in that order); size must be 2.
1152 */
1153 public String format(String aFormat, List<String> aMonths, List<String> aWeekdays, List<String> aAmPmIndicators) {
1154 DateTimeFormatter format = new DateTimeFormatter(aFormat, aMonths, aWeekdays, aAmPmIndicators);
1155 return format.format(this);
1156 }
1157
1158 /**
1159 Return the current date-time.
1160 <P>Combines the return value of {@link System#currentTimeMillis()} with the given {@link TimeZone}.
1161
1162 <P>Only millisecond precision is possible for this method.
1163 */
1164 public static DateTime now(TimeZone aTimeZone) {
1165 return forInstant(System.currentTimeMillis(), aTimeZone);
1166 }
1167
1168 /**
1169 Return the current date.
1170 <P>As in {@link #now(TimeZone)}, but truncates the time portion, leaving only year-month-day.
1171 */
1172 public static DateTime today(TimeZone aTimeZone) {
1173 DateTime result = now(aTimeZone);
1174 return result.truncate(Unit.DAY);
1175 }
1176
1177 /** Return <tt>true</tt> only if this date is in the future, with respect to {@link #now(TimeZone)}. */
1178 public boolean isInTheFuture(TimeZone aTimeZone) {
1179 return now(aTimeZone).lt(this);
1180 }
1181
1182 /** Return <tt>true</tt> only if this date is in the past, with respect to {@link #now(TimeZone)}. */
1183 public boolean isInThePast(TimeZone aTimeZone) {
1184 return now(aTimeZone).gt(this);
1185 }
1186
1187 /**
1188 Return a <tt>DateTime</tt> corresponding to a change from one {@link TimeZone} to another.
1189
1190 <P>A <tt>DateTime</tt> object has an implicit and immutable time zone.
1191 If you need to change the implicit time zone, you can use this method to do so.
1192
1193 <P>Example :
1194 <PRE>
1195 TimeZone fromUK = TimeZone.getTimeZone("Europe/London");
1196 TimeZone toIndonesia = TimeZone.getTimeZone("Asia/Jakarta");
1197 DateTime newDt = oldDt.changeTimeZone(fromUK, toIndonesia);
1198 </PRE>
1199
1200 <P>Requires year-month-day-hour to be present; if not, a runtime exception is thrown.
1201 @param aFromTimeZone the implicit time zone of this object.
1202 @param aToTimeZone the implicit time zone of the <tt>DateTime</tt> returned by this method.
1203 @return aDateTime corresponding to the change of time zone implied by the 2 parameters.
1204 */
1205 public DateTime changeTimeZone(TimeZone aFromTimeZone, TimeZone aToTimeZone){
1206 DateTime result = null;
1207 ensureHasYearMonthDay();
1208 if (unitsAllAbsent(Unit.HOUR)){
1209 throw new IllegalArgumentException("DateTime does not include the hour. Cannot change the time zone if no hour is present.");
1210 }
1211 Calendar fromDate = new GregorianCalendar(aFromTimeZone);
1212 fromDate.set(Calendar.YEAR, getYear());
1213 fromDate.set(Calendar.MONTH, getMonth()-1);
1214 fromDate.set(Calendar.DAY_OF_MONTH, getDay());
1215 fromDate.set(Calendar.HOUR_OF_DAY, getHour());
1216 if(getMinute() != null) {
1217 fromDate.set(Calendar.MINUTE, getMinute());
1218 }
1219 else {
1220 fromDate.set(Calendar.MINUTE, 0);
1221 }
1222 //other items zeroed out here, since they don't matter for time zone calculations
1223 fromDate.set(Calendar.SECOND, 0);
1224 fromDate.set(Calendar.MILLISECOND, 0);
1225
1226 //millisecond precision is OK here, since the seconds/nanoseconds are not part of the calc
1227 Calendar toDate = new GregorianCalendar(aToTimeZone);
1228 toDate.setTimeInMillis(fromDate.getTimeInMillis());
1229 //needed if this date has hour, but no minute (bit of an oddball case) :
1230 Integer minute = getMinute() != null ? toDate.get(Calendar.MINUTE) : null;
1231 result = new DateTime(
1232 toDate.get(Calendar.YEAR), toDate.get(Calendar.MONTH) + 1, toDate.get(Calendar.DAY_OF_MONTH),
1233 toDate.get(Calendar.HOUR_OF_DAY), minute, getSecond(), getNanoseconds()
1234 );
1235 return result;
1236 }
1237
1238 /**
1239 Compare this object to another, for ordering purposes.
1240 <P> Uses the 7 date-time elements (year..nanosecond). The Year is considered the most
1241 significant item, and the Nanosecond the least significant item. Null items are placed first in this comparison.
1242 */
1243 public int compareTo(DateTime aThat) {
1244 if (this == aThat) return EQUAL;
1245 ensureParsed();
1246 aThat.ensureParsed();
1247
1248 ModelUtil.NullsGo nullsGo = ModelUtil.NullsGo.FIRST;
1249 int comparison = ModelUtil.comparePossiblyNull(this.fYear, aThat.fYear, nullsGo);
1250 if (comparison != EQUAL) return comparison;
1251
1252 comparison = ModelUtil.comparePossiblyNull(this.fMonth, aThat.fMonth, nullsGo);
1253 if (comparison != EQUAL) return comparison;
1254
1255 comparison = ModelUtil.comparePossiblyNull(this.fDay, aThat.fDay, nullsGo);
1256 if (comparison != EQUAL) return comparison;
1257
1258 comparison = ModelUtil.comparePossiblyNull(this.fHour, aThat.fHour, nullsGo);
1259 if (comparison != EQUAL) return comparison;
1260
1261 comparison = ModelUtil.comparePossiblyNull(this.fMinute, aThat.fMinute, nullsGo);
1262 if (comparison != EQUAL) return comparison;
1263
1264 comparison = ModelUtil.comparePossiblyNull(this.fSecond, aThat.fSecond, nullsGo);
1265 if (comparison != EQUAL) return comparison;
1266
1267 comparison = ModelUtil.comparePossiblyNull(this.fNanosecond, aThat.fNanosecond, nullsGo);
1268 if (comparison != EQUAL) return comparison;
1269
1270 return EQUAL;
1271 }
1272
1273 /**
1274 Equals method for this object.
1275
1276 <P>Equality is determined by the 7 date-time elements (year..nanosecond).
1277 */
1278 @Override public boolean equals(Object aThat) {
1279 /*
1280 * Implementation note: it was considered branching this method, according to whether
1281 * the objects are already parsed. That was rejected, since maintaining 'synchronicity'
1282 * with hashCode would not then be possible, since hashCode is based only on one object,
1283 * not two.
1284 */
1285 ensureParsed();
1286 Boolean result = ModelUtil.quickEquals(this, aThat);
1287 if (result == null) {
1288 DateTime that = (DateTime)aThat;
1289 that.ensureParsed();
1290 result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
1291 }
1292 return result;
1293 }
1294
1295 /**
1296 Hash code for this object.
1297
1298 <P> Uses the same 7 date-time elements (year..nanosecond) as used by
1299 {@link #equals(Object)}.
1300 */
1301 @Override public int hashCode() {
1302 if (fHashCode == 0) {
1303 ensureParsed();
1304 fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
1305 }
1306 return fHashCode;
1307 }
1308
1309 /**
1310 Intended for <i>debugging and logging</i> only.
1311
1312 <P><b>To format this <tt>DateTime</tt> for presentation to the user, see the various <tt>format</tt> methods.</b>
1313
1314 <P>If the {@link #DateTime(String)} constructor was called, then return that String.
1315
1316 <P>Otherwise, the return value is constructed from each date-time element, in a fixed format, depending
1317 on which time units are present. Example values :
1318 <ul>
1319 <li>2011-04-30 13:59:59.123456789
1320 <li>2011-04-30 13:59:59
1321 <li>2011-04-30
1322 <li>2011-04-30 13:59
1323 <li>13:59:59.123456789
1324 <li>13:59:59
1325 <li>and so on...
1326 </ul>
1327
1328 <P>In the great majority of cases, this will give reasonable output for debugging and logging statements.
1329
1330 <P>In cases where a bizarre combinations of time units is present, the return value is presented in a verbose form.
1331 For example, if all time units are present <i>except</i> for minutes, the return value has this form:
1332 <PRE>Y:2001 M:1 D:31 h:13 m:null s:59 f:123456789</PRE>
1333 */
1334 @Override public String toString() {
1335 String result = "";
1336 if (Util.textHasContent(fDateTime)) {
1337 result = fDateTime;
1338 }
1339 else {
1340 String format = calcToStringFormat();
1341 if(format != null){
1342 result = format(calcToStringFormat());
1343 }
1344 else {
1345 StringBuilder builder = new StringBuilder();
1346 addToString("Y", fYear, builder);
1347 addToString("M", fMonth, builder);
1348 addToString("D", fDay, builder);
1349 addToString("h", fHour, builder);
1350 addToString("m", fMinute, builder);
1351 addToString("s", fSecond, builder);
1352 addToString("f", fNanosecond, builder);
1353 result = builder.toString().trim();
1354 }
1355 }
1356 return result;
1357 }
1358
1359 // PACKAGE-PRIVATE (for unit testing, mostly)
1360
1361 static final class ItemOutOfRange extends RuntimeException {
1362 ItemOutOfRange(String aMessage) {
1363 super(aMessage);
1364 }
1365 private static final long serialVersionUID = 4760138291907517660L;
1366 }
1367
1368 static final class MissingItem extends RuntimeException {
1369 MissingItem(String aMessage) {
1370 super(aMessage);
1371 }
1372 private static final long serialVersionUID = -7359967338896127755L;
1373 }
1374
1375 /** Intended as internal tool, for testing only. Note scope is not public! */
1376 void ensureParsed() {
1377 if (!fIsAlreadyParsed) {
1378 parseDateTimeText();
1379 }
1380 }
1381
1382 /**
1383 Return the number of days in the given month. The returned value depends on the year as
1384 well, because of leap years. Returns <tt>null</tt> if either year or month are
1385 absent. WRONG - should be public??
1386 Package-private, needed for interval calcs.
1387 */
1388 static Integer getNumDaysInMonth(Integer aYear, Integer aMonth) {
1389 Integer result = null;
1390 if (aYear != null && aMonth != null) {
1391 if (aMonth == 1) {
1392 result = 31;
1393 }
1394 else if (aMonth == 2) {
1395 result = isLeapYear(aYear) ? 29 : 28;
1396 }
1397 else if (aMonth == 3) {
1398 result = 31;
1399 }
1400 else if (aMonth == 4) {
1401 result = 30;
1402 }
1403 else if (aMonth == 5) {
1404 result = 31;
1405 }
1406 else if (aMonth == 6) {
1407 result = 30;
1408 }
1409 else if (aMonth == 7) {
1410 result = 31;
1411 }
1412 else if (aMonth == 8) {
1413 result = 31;
1414 }
1415 else if (aMonth == 9) {
1416 result = 30;
1417 }
1418 else if (aMonth == 10) {
1419 result = 31;
1420 }
1421 else if (aMonth == 11) {
1422 result = 30;
1423 }
1424 else if (aMonth == 12) {
1425 result = 31;
1426 }
1427 else {
1428 throw new AssertionError("Month is out of range 1..12:" + aMonth);
1429 }
1430 }
1431 return result;
1432 }
1433
1434 static DateTime fromJulianDayNumberAtNoon(int aJDAtNoon) {
1435 //http://www.hermetic.ch/cal_stud/jdn.htm
1436 int l = aJDAtNoon + 68569;
1437 int n = (4 * l) / 146097;
1438 l = l - (146097 * n + 3) / 4;
1439 int i = (4000 * (l + 1)) / 1461001;
1440 l = l - (1461 * i) / 4 + 31;
1441 int j = (80 * l) / 2447;
1442 int d = l - (2447 * j) / 80;
1443 l = j / 11;
1444 int m = j + 2 - (12 * l);
1445 int y = 100 * (n - 49) + i + l;
1446 return DateTime.forDateOnly(y, m, d);
1447 }
1448
1449 // PRIVATE
1450
1451 /*
1452 There are 2 representations of a date - a text form, and a 'parsed' form, in which all
1453 of the elements of the date are separated out. A DateTime starts out with one of these
1454 forms, and may need to generate the other.
1455 */
1456
1457 /** The text form of a date. @serial */
1458 private String fDateTime;
1459
1460 /* The following 7 items represent the parsed form of a DateTime. */
1461 /** @serial */
1462 private Integer fYear;
1463 /** @serial */
1464 private Integer fMonth;
1465 /** @serial */
1466 private Integer fDay;
1467 /** @serial */
1468 private Integer fHour;
1469 /** @serial */
1470 private Integer fMinute;
1471 /** @serial */
1472 private Integer fSecond;
1473 /** @serial */
1474 private Integer fNanosecond;
1475
1476 /** Indicates if this DateTime has been parsed into its 7 constituents. @serial */
1477 private boolean fIsAlreadyParsed;
1478
1479 /** @serial */
1480 private int fHashCode;
1481
1482 private static final int EQUAL = 0;
1483
1484 private static int EPOCH_MODIFIED_JD = 2400000;
1485
1486 private static final int MILLION = 1000000;
1487
1488 private static final long serialVersionUID = -1300068157085493891L;
1489
1490 /**
1491 Return a the whole number, with no fraction.
1492 The JD at noon is 1 more than the JD at midnight.
1493 */
1494 private int calculateJulianDayNumberAtNoon() {
1495 //http://www.hermetic.ch/cal_stud/jdn.htm
1496 int y = fYear;
1497 int m = fMonth;
1498 int d = fDay;
1499 int result = (1461 * (y + 4800 + (m - 14) / 12)) / 4 + (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 - (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075;
1500 return result;
1501 }
1502
1503 private void ensureHasYearMonthDay() {
1504 ensureParsed();
1505 if (!hasYearMonthDay()) {
1506 throw new MissingItem("DateTime does not include year/month/day.");
1507 }
1508 }
1509
1510 /** Return the number of seconds in any existing time portion of the date. */
1511 private int numSecondsInTimePortion() {
1512 int result = 0;
1513 if (fSecond != null) {
1514 result = result + fSecond;
1515 }
1516 if (fMinute != null) {
1517 result = result + 60 * fMinute;
1518 }
1519 if (fHour != null) {
1520 result = result + 3600 * fHour;
1521 }
1522 return result;
1523 }
1524
1525 private void validateState() {
1526 checkRange(fYear, 1, 9999, "Year");
1527 checkRange(fMonth, 1, 12, "Month");
1528 checkRange(fDay, 1, 31, "Day");
1529 checkRange(fHour, 0, 23, "Hour");
1530 checkRange(fMinute, 0, 59, "Minute");
1531 checkRange(fSecond, 0, 59, "Second");
1532 checkRange(fNanosecond, 0, 999999999, "Nanosecond");
1533 checkNumDaysInMonth(fYear, fMonth, fDay);
1534 }
1535
1536 private void checkRange(Integer aValue, int aMin, int aMax, String aName) {
1537 if(aValue != null){
1538 if (aValue < aMin || aValue > aMax){
1539 throw new ItemOutOfRange(aName + " is not in the range " + aMin + ".." + aMax + ". Value is:" + aValue);
1540 }
1541 }
1542 }
1543
1544 private void checkNumDaysInMonth(Integer aYear, Integer aMonth, Integer aDay) {
1545 if (hasYearMonthDay(aYear, aMonth, aDay) && aDay > getNumDaysInMonth(aYear, aMonth)) {
1546 throw new ItemOutOfRange("The day-of-the-month value '" + aDay + "' exceeds the number of days in the month: " + getNumDaysInMonth(aYear, aMonth));
1547 }
1548 }
1549
1550 private void parseDateTimeText() {
1551 DateTimeParser parser = new DateTimeParser();
1552 DateTime dateTime = parser.parse(fDateTime);
1553 /*
1554 * This is unusual - we essentially copy from one object to another. This could be
1555 * avoided by building another interface, But defining a top-level interface for this
1556 * simple task is too high a price.
1557 */
1558 fYear = dateTime.fYear;
1559 fMonth = dateTime.fMonth;
1560 fDay = dateTime.fDay;
1561 fHour = dateTime.fHour;
1562 fMinute = dateTime.fMinute;
1563 fSecond = dateTime.fSecond;
1564 fNanosecond = dateTime.fNanosecond;
1565 validateState();
1566 }
1567
1568 private boolean hasYearMonthDay(Integer aYear, Integer aMonth, Integer aDay) {
1569 return isPresent(aYear, aMonth, aDay);
1570 }
1571
1572 private static boolean isLeapYear(Integer aYear) {
1573 boolean result = false;
1574 if (aYear % 100 == 0) {
1575 // this is a century year
1576 if (aYear % 400 == 0) {
1577 result = true;
1578 }
1579 }
1580 else if (aYear % 4 == 0) {
1581 result = true;
1582 }
1583 return result;
1584 }
1585
1586 private Object[] getSignificantFields() {
1587 return new Object[]{fYear, fMonth, fDay, fHour, fMinute, fSecond, fNanosecond};
1588 }
1589
1590 private void addToString(String aName, Object aValue, StringBuilder aBuilder) {
1591 aBuilder.append(aName + ":" + String.valueOf(aValue) + " ");
1592 }
1593
1594 /** Return true only if all the given arguments are non-null. */
1595 private boolean isPresent(Object... aItems) {
1596 boolean result = true;
1597 for (Object item : aItems) {
1598 if (item == null) {
1599 result = false;
1600 break;
1601 }
1602 }
1603 return result;
1604 }
1605
1606 private DateTime getStartEndDateTime(Integer aDay, Integer aHour, Integer aMinute, Integer aSecond, Integer aNanosecond) {
1607 ensureHasYearMonthDay();
1608 return new DateTime(fYear, fMonth, aDay, aHour, aMinute, aSecond, aNanosecond);
1609 }
1610
1611 private String calcToStringFormat(){
1612 String result = null; //caller will check for this; null means the set of units is bizarre
1613 if(unitsAllPresent(Unit.YEAR) && unitsAllAbsent(Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1614 result = "YYYY";
1615 }
1616 else if (unitsAllPresent(Unit.YEAR, Unit.MONTH) && unitsAllAbsent(Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1617 result = "YYYY-MM";
1618 }
1619 else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY) && unitsAllAbsent(Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1620 result = "YYYY-MM-DD";
1621 }
1622 else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR) && unitsAllAbsent(Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1623 result = "YYYY-MM-DD hh";
1624 }
1625 else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE) && unitsAllAbsent(Unit.SECOND, Unit.NANOSECONDS)){
1626 result = "YYYY-MM-DD hh:mm";
1627 }
1628 else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND) && unitsAllAbsent(Unit.NANOSECONDS)){
1629 result = "YYYY-MM-DD hh:mm:ss";
1630 }
1631 else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1632 result = "YYYY-MM-DD hh:mm:ss.fffffffff";
1633 }
1634 else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY) && unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1635 result = "hh:mm:ss.fffffffff";
1636 }
1637 else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.NANOSECONDS) && unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND)){
1638 result = "hh:mm:ss";
1639 }
1640 else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.SECOND, Unit.NANOSECONDS) && unitsAllPresent(Unit.HOUR, Unit.MINUTE)){
1641 result = "hh:mm";
1642 }
1643 return result;
1644 }
1645
1646 /**
1647 Always treat de-serialization as a full-blown constructor, by
1648 validating the final state of the de-serialized object.
1649 */
1650 private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException {
1651 //always perform the default de-serialization first
1652 aInputStream.defaultReadObject();
1653 //no mutable fields in this case
1654 validateState();
1655 }
1656
1657 /**
1658 This is the default implementation of writeObject.
1659 Customise if necessary.
1660 */
1661 private void writeObject(ObjectOutputStream aOutputStream) throws IOException {
1662 //perform the default serialization for all non-transient, non-static fields
1663 aOutputStream.defaultWriteObject();
1664 }
1665
1666 }