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 any date information is present, in either this <tt>DateTime</tt> or <tt>aThat</tt>, 
1097        then full year-month-day must be present in <em>both</em>; if not, then the date portion will be ignored, and only the 
1098        time portion will contribute to the calculation.
1099      */
1100      public long numSecondsFrom(DateTime aThat) {
1101        long result = 0;
1102        aThat.ensureParsed(); //since the boolean test may short-circuit for aThat
1103        if(hasYearMonthDay() && aThat.hasYearMonthDay()){
1104          result = numDaysFrom(aThat) * 86400; //intermediate value, just the day portion
1105        }
1106        result = result - this.numSecondsInTimePortion() + aThat.numSecondsInTimePortion();
1107        return result;
1108      }
1109    
1110      /**
1111       Output this <tt>DateTime</tt> as a formatted String using numbers, with no localizable text.
1112       
1113       <P>Example:
1114       <PRE>dt.format("YYYY-MM-DD hh:mm:ss");</PRE>
1115       would generate text of the form
1116       <PRE>2009-09-09 18:23:59</PRE>
1117       
1118       <P>If months, weekdays, or AM/PM indicators are output as localizable text, you must use {@link #format(String, Locale)}.
1119       @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1120       */
1121      public String format(String aFormat) {
1122        DateTimeFormatter format = new DateTimeFormatter(aFormat);
1123        return format.format(this);
1124      }
1125    
1126      /**
1127       Output this <tt>DateTime</tt> as a formatted String using numbers and/or localizable text.
1128       
1129       <P>This method is intended for alphanumeric output, such as '<tt>Sunday, November 14, 1858 10:00 AM</tt>'.
1130       <P>If months and weekdays are output as numbers, you are encouraged to use {@link #format(String)} instead.
1131       
1132       @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1133       @param aLocale used to generate text for Month, Weekday and AM/PM indicator; required only by patterns which return localized 
1134       text, instead of numeric forms.
1135       */
1136      public String format(String aFormat, Locale aLocale) {
1137        DateTimeFormatter format = new DateTimeFormatter(aFormat, aLocale);
1138        return format.format(this);
1139      }
1140    
1141      /**
1142       Output this <tt>DateTime</tt> as a formatted String using numbers and explicit text for months, weekdays, and AM/PM indicator. 
1143    
1144       <P>Use of this method is likely relatively rare; it should be used only if the output of {@link #format(String, Locale)}  is 
1145       inadequate. 
1146       
1147       @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1148       @param aMonths contains text for all 12 months, starting with January; size must be 12. 
1149       @param aWeekdays contains text for all 7 weekdays, starting with Sunday; size must be 7. 
1150       @param aAmPmIndicators contains text for A.M and P.M. indicators (in that order); size must be 2. 
1151       */
1152      public String format(String aFormat, List<String> aMonths, List<String> aWeekdays, List<String> aAmPmIndicators) {
1153        DateTimeFormatter format = new DateTimeFormatter(aFormat, aMonths, aWeekdays, aAmPmIndicators);
1154        return format.format(this);
1155      }
1156    
1157      /**
1158       Return the current date-time.
1159       <P>Combines the return value of {@link System#currentTimeMillis()} with the given {@link TimeZone}.
1160       
1161       <P>Only millisecond precision is possible for this method.
1162       */
1163      public static DateTime now(TimeZone aTimeZone) {
1164        return forInstant(System.currentTimeMillis(), aTimeZone);
1165      }
1166    
1167      /** 
1168       Return the current date.
1169       <P>As in {@link #now(TimeZone)}, but truncates the time portion, leaving only year-month-day.
1170      */
1171      public static DateTime today(TimeZone aTimeZone) {
1172        DateTime result = now(aTimeZone);
1173        return result.truncate(Unit.DAY);
1174      }
1175    
1176      /** Return <tt>true</tt> only if this date is in the future, with respect to {@link #now(TimeZone)}. */
1177      public boolean isInTheFuture(TimeZone aTimeZone) {
1178        return now(aTimeZone).lt(this);
1179      }
1180    
1181      /** Return <tt>true</tt> only if this date is in the past, with respect to {@link #now(TimeZone)}. */
1182      public boolean isInThePast(TimeZone aTimeZone) {
1183        return now(aTimeZone).gt(this);
1184      }
1185      
1186      /**
1187        Return a <tt>DateTime</tt> corresponding to a change from one {@link TimeZone} to another.
1188        
1189        <P>A <tt>DateTime</tt> object has an implicit and immutable time zone. 
1190        If you need to change the implicit time zone, you can use this method to do so.
1191        
1192        <P>Example :
1193        <PRE>
1194    TimeZone fromUK = TimeZone.getTimeZone("Europe/London");
1195    TimeZone toIndonesia = TimeZone.getTimeZone("Asia/Jakarta");
1196    DateTime newDt = oldDt.changeTimeZone(fromUK, toIndonesia);
1197        </PRE>
1198         
1199       <P>Requires year-month-day-hour to be present; if not, a runtime exception is thrown.
1200       @param aFromTimeZone the implicit time zone of this object.
1201       @param aToTimeZone the implicit time zone of the <tt>DateTime</tt> returned by this method.
1202       @return aDateTime corresponding to the change of time zone implied by the 2 parameters.
1203       */
1204      public DateTime changeTimeZone(TimeZone aFromTimeZone, TimeZone aToTimeZone){
1205        DateTime result = null;
1206        ensureHasYearMonthDay();
1207        if (unitsAllAbsent(Unit.HOUR)){
1208          throw new IllegalArgumentException("DateTime does not include the hour. Cannot change the time zone if no hour is present.");
1209        }
1210        Calendar fromDate = new GregorianCalendar(aFromTimeZone);
1211        fromDate.set(Calendar.YEAR, getYear());
1212        fromDate.set(Calendar.MONTH, getMonth()-1);
1213        fromDate.set(Calendar.DAY_OF_MONTH, getDay());
1214        fromDate.set(Calendar.HOUR_OF_DAY, getHour());
1215        if(getMinute() != null) {
1216          fromDate.set(Calendar.MINUTE, getMinute());
1217        }
1218        else {
1219          fromDate.set(Calendar.MINUTE, 0);
1220        }
1221        //other items zeroed out here, since they don't matter for time zone calculations
1222        fromDate.set(Calendar.SECOND, 0);
1223        fromDate.set(Calendar.MILLISECOND, 0);
1224    
1225        //millisecond precision is OK here, since the seconds/nanoseconds are not part of the calc
1226        Calendar toDate = new GregorianCalendar(aToTimeZone);
1227        toDate.setTimeInMillis(fromDate.getTimeInMillis());
1228        //needed if this date has hour, but no minute (bit of an oddball case) :
1229        Integer minute = getMinute() != null ? toDate.get(Calendar.MINUTE) : null;
1230        result = new DateTime(
1231          toDate.get(Calendar.YEAR), toDate.get(Calendar.MONTH) + 1, toDate.get(Calendar.DAY_OF_MONTH), 
1232          toDate.get(Calendar.HOUR_OF_DAY), minute, getSecond(), getNanoseconds() 
1233        );
1234        return result;
1235      }
1236    
1237      /**
1238       Compare this object to another, for ordering purposes.
1239       <P> Uses the 7 date-time elements (year..nanosecond). The Year is considered the most
1240       significant item, and the Nanosecond the least significant item. Null items are placed first in this comparison.
1241       */
1242      public int compareTo(DateTime aThat) {
1243        if (this == aThat) return EQUAL;
1244        ensureParsed();
1245        aThat.ensureParsed();
1246    
1247        ModelUtil.NullsGo nullsGo = ModelUtil.NullsGo.FIRST;
1248        int comparison = ModelUtil.comparePossiblyNull(this.fYear, aThat.fYear, nullsGo);
1249        if (comparison != EQUAL)  return comparison;
1250    
1251        comparison = ModelUtil.comparePossiblyNull(this.fMonth, aThat.fMonth, nullsGo);
1252        if (comparison != EQUAL)  return comparison;
1253    
1254        comparison = ModelUtil.comparePossiblyNull(this.fDay, aThat.fDay, nullsGo);
1255        if (comparison != EQUAL)  return comparison;
1256    
1257        comparison = ModelUtil.comparePossiblyNull(this.fHour, aThat.fHour, nullsGo);
1258        if (comparison != EQUAL)  return comparison;
1259    
1260        comparison = ModelUtil.comparePossiblyNull(this.fMinute, aThat.fMinute, nullsGo);
1261        if (comparison != EQUAL)  return comparison;
1262    
1263        comparison = ModelUtil.comparePossiblyNull(this.fSecond, aThat.fSecond, nullsGo);
1264        if (comparison != EQUAL)  return comparison;
1265    
1266        comparison = ModelUtil.comparePossiblyNull(this.fNanosecond, aThat.fNanosecond, nullsGo);
1267        if (comparison != EQUAL)  return comparison;
1268    
1269        return EQUAL;
1270      }
1271    
1272      /**
1273       Equals method for this object.
1274       
1275       <P>Equality is determined by the 7 date-time elements (year..nanosecond).
1276       */
1277      @Override public boolean equals(Object aThat) {
1278        /*
1279         * Implementation note: it was considered branching this method, according to whether
1280         * the objects are already parsed. That was rejected, since maintaining 'synchronicity'
1281         * with hashCode would not then be possible, since hashCode is based only on one object,
1282         * not two.
1283         */
1284        ensureParsed();
1285        Boolean result = ModelUtil.quickEquals(this, aThat);
1286        if (result == null) {
1287          DateTime that = (DateTime)aThat;
1288          that.ensureParsed();
1289          result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
1290        }
1291        return result;
1292      }
1293    
1294      /**
1295       Hash code for this object.
1296       
1297       <P> Uses the same 7 date-time elements (year..nanosecond) as used by
1298       {@link #equals(Object)}.
1299       */
1300      @Override public int hashCode() {
1301        if (fHashCode == 0) {
1302          ensureParsed();
1303          fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
1304        }
1305        return fHashCode;
1306      }
1307    
1308      /**
1309       Intended for <i>debugging and logging</i> only.
1310       
1311       <P><b>To format this <tt>DateTime</tt> for presentation to the user, see the various <tt>format</tt> methods.</b>
1312       
1313       <P>If the {@link #DateTime(String)} constructor was called, then return that String. 
1314       
1315       <P>Otherwise, the return value is constructed from each date-time element, in a fixed format, depending 
1316       on which time units are present. Example values :
1317       <ul>
1318        <li>2011-04-30 13:59:59.123456789
1319        <li>2011-04-30 13:59:59
1320        <li>2011-04-30
1321        <li>2011-04-30 13:59
1322        <li>13:59:59.123456789
1323        <li>13:59:59
1324        <li>and so on...
1325       </ul>
1326       
1327       <P>In the great majority of cases, this will give reasonable output for debugging and logging statements.
1328       
1329       <P>In cases where a bizarre combinations of time units is present, the return value is presented in a verbose form.
1330       For example, if all time units are present <i>except</i> for minutes, the return value has this form:
1331       <PRE>Y:2001 M:1 D:31 h:13 m:null s:59 f:123456789</PRE> 
1332      */
1333      @Override public String toString() {
1334        String result = "";
1335        if (Util.textHasContent(fDateTime)) {
1336          result = fDateTime;
1337        }
1338        else {
1339          String format = calcToStringFormat();
1340          if(format != null){
1341            result = format(calcToStringFormat());
1342          }
1343          else {
1344            StringBuilder builder = new StringBuilder();
1345            addToString("Y", fYear, builder);
1346            addToString("M", fMonth, builder);
1347            addToString("D", fDay, builder);
1348            addToString("h", fHour, builder);
1349            addToString("m", fMinute, builder);
1350            addToString("s", fSecond, builder);
1351            addToString("f", fNanosecond, builder);
1352            result = builder.toString().trim();
1353          }
1354        }
1355        return result;
1356      }
1357    
1358      // PACKAGE-PRIVATE (for unit testing, mostly)
1359    
1360      static final class ItemOutOfRange extends RuntimeException {
1361        ItemOutOfRange(String aMessage) {
1362          super(aMessage);
1363        }
1364        private static final long serialVersionUID = 4760138291907517660L;
1365      }
1366    
1367      static final class MissingItem extends RuntimeException {
1368        MissingItem(String aMessage) {
1369          super(aMessage);
1370        }
1371        private static final long serialVersionUID = -7359967338896127755L;
1372      }
1373    
1374      /** Intended as internal tool, for testing only. Note scope is not public! */
1375      void ensureParsed() {
1376        if (!fIsAlreadyParsed) {
1377          parseDateTimeText();
1378        }
1379      }
1380    
1381      /**
1382       Return the number of days in the given month. The returned value depends on the year as
1383       well, because of leap years. Returns <tt>null</tt> if either year or month are
1384       absent. WRONG - should be public??
1385       Package-private, needed for interval calcs.
1386       */
1387      static Integer getNumDaysInMonth(Integer aYear, Integer aMonth) {
1388        Integer result = null;
1389        if (aYear != null && aMonth != null) {
1390          if (aMonth == 1) {
1391            result = 31;
1392          }
1393          else if (aMonth == 2) {
1394            result = isLeapYear(aYear) ? 29 : 28;
1395          }
1396          else if (aMonth == 3) {
1397            result = 31;
1398          }
1399          else if (aMonth == 4) {
1400            result = 30;
1401          }
1402          else if (aMonth == 5) {
1403            result = 31;
1404          }
1405          else if (aMonth == 6) {
1406            result = 30;
1407          }
1408          else if (aMonth == 7) {
1409            result = 31;
1410          }
1411          else if (aMonth == 8) {
1412            result = 31;
1413          }
1414          else if (aMonth == 9) {
1415            result = 30;
1416          }
1417          else if (aMonth == 10) {
1418            result = 31;
1419          }
1420          else if (aMonth == 11) {
1421            result = 30;
1422          }
1423          else if (aMonth == 12) {
1424            result = 31;
1425          }
1426          else {
1427            throw new AssertionError("Month is out of range 1..12:" + aMonth);
1428          }
1429        }
1430        return result;
1431      }
1432    
1433      static DateTime fromJulianDayNumberAtNoon(int aJDAtNoon) {
1434        //http://www.hermetic.ch/cal_stud/jdn.htm
1435        int l = aJDAtNoon + 68569;
1436        int n = (4 * l) / 146097;
1437        l = l - (146097 * n + 3) / 4;
1438        int i = (4000 * (l + 1)) / 1461001;
1439        l = l - (1461 * i) / 4 + 31;
1440        int j = (80 * l) / 2447;
1441        int d = l - (2447 * j) / 80;
1442        l = j / 11;
1443        int m = j + 2 - (12 * l);
1444        int y = 100 * (n - 49) + i + l;
1445        return DateTime.forDateOnly(y, m, d);
1446      }
1447    
1448      // PRIVATE
1449    
1450      /*
1451       There are 2 representations of a date - a text form, and a 'parsed' form, in which all
1452       of the elements of the date are separated out. A DateTime starts out with one of these
1453       forms, and may need to generate the other.
1454       */
1455    
1456      /** The text form of a date. @serial */
1457      private String fDateTime;
1458    
1459      /* The following 7 items represent the parsed form of a DateTime. */
1460      /**  @serial */
1461      private Integer fYear;
1462      /**  @serial */
1463      private Integer fMonth;
1464      /**  @serial */
1465      private Integer fDay;
1466      /**  @serial */
1467      private Integer fHour;
1468      /**  @serial */
1469      private Integer fMinute;
1470      /**  @serial */
1471      private Integer fSecond;
1472      /**  @serial */
1473      private Integer fNanosecond;
1474    
1475      /** Indicates if this DateTime has been parsed into its 7 constituents. @serial */
1476      private boolean fIsAlreadyParsed;
1477    
1478      /** @serial */
1479      private int fHashCode;
1480      
1481      private static final int EQUAL = 0;
1482      
1483      private static int EPOCH_MODIFIED_JD = 2400000;
1484    
1485      private static final int MILLION = 1000000;
1486      
1487      private static final long serialVersionUID =  -1300068157085493891L; 
1488        
1489      /**
1490       Return a the whole number, with no fraction.
1491       The JD at noon is 1 more than the JD at midnight. 
1492       */
1493      private int calculateJulianDayNumberAtNoon() {
1494        //http://www.hermetic.ch/cal_stud/jdn.htm
1495        int y = fYear;
1496        int m = fMonth;
1497        int d = fDay;
1498        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;
1499        return result;
1500      }
1501    
1502      private void ensureHasYearMonthDay() {
1503        ensureParsed();
1504        if (!hasYearMonthDay()) {
1505          throw new MissingItem("DateTime does not include year/month/day.");
1506        }
1507      }
1508    
1509      /** Return the number of seconds in any existing time portion of the date. */
1510      private int numSecondsInTimePortion() {
1511        int result = 0;
1512        if (fSecond != null) {
1513          result = result + fSecond;
1514        }
1515        if (fMinute != null) {
1516          result = result + 60 * fMinute;
1517        }
1518        if (fHour != null) {
1519          result = result + 3600 * fHour;
1520        }
1521        return result;
1522      }
1523    
1524      private void validateState() {
1525        checkRange(fYear, 1, 9999, "Year");
1526        checkRange(fMonth, 1, 12, "Month");
1527        checkRange(fDay, 1, 31, "Day");
1528        checkRange(fHour, 0, 23, "Hour");
1529        checkRange(fMinute, 0, 59, "Minute");
1530        checkRange(fSecond, 0, 59, "Second");
1531        checkRange(fNanosecond, 0, 999999999, "Nanosecond");
1532        checkNumDaysInMonth(fYear, fMonth, fDay);
1533      }
1534    
1535      private void checkRange(Integer aValue, int aMin, int aMax, String aName) {
1536        if(aValue != null){
1537          if (aValue < aMin || aValue > aMax){
1538            throw new ItemOutOfRange(aName + " is not in the range " + aMin + ".." + aMax + ". Value is:" + aValue);
1539          }
1540        }
1541      }
1542    
1543      private void checkNumDaysInMonth(Integer aYear, Integer aMonth, Integer aDay) {
1544        if (hasYearMonthDay(aYear, aMonth, aDay) && aDay > getNumDaysInMonth(aYear, aMonth)) {
1545          throw new ItemOutOfRange("The day-of-the-month value '" + aDay + "' exceeds the number of days in the month: " + getNumDaysInMonth(aYear, aMonth));
1546        }
1547      }
1548    
1549      private void parseDateTimeText() {
1550        DateTimeParser parser = new DateTimeParser();
1551        DateTime dateTime = parser.parse(fDateTime);
1552        /*
1553         * This is unusual - we essentially copy from one object to another. This could be
1554         * avoided by building another interface, But defining a top-level interface for this
1555         * simple task is too high a price.
1556         */
1557        fYear = dateTime.fYear;
1558        fMonth = dateTime.fMonth;
1559        fDay = dateTime.fDay;
1560        fHour = dateTime.fHour;
1561        fMinute = dateTime.fMinute;
1562        fSecond = dateTime.fSecond;
1563        fNanosecond = dateTime.fNanosecond;
1564        validateState();
1565      }
1566    
1567      private boolean hasYearMonthDay(Integer aYear, Integer aMonth, Integer aDay) {
1568        return isPresent(aYear, aMonth, aDay);
1569      }
1570    
1571      private static boolean isLeapYear(Integer aYear) {
1572        boolean result = false;
1573        if (aYear % 100 == 0) {
1574          // this is a century year
1575          if (aYear % 400 == 0) {
1576            result = true;
1577          }
1578        }
1579        else if (aYear % 4 == 0) {
1580          result = true;
1581        }
1582        return result;
1583      }
1584    
1585      private Object[] getSignificantFields() {
1586        return new Object[]{fYear, fMonth, fDay, fHour, fMinute, fSecond, fNanosecond};
1587      }
1588    
1589      private void addToString(String aName, Object aValue, StringBuilder aBuilder) {
1590        aBuilder.append(aName + ":" + String.valueOf(aValue) + " ");
1591      }
1592    
1593      /** Return true only if all the given arguments are non-null. */
1594      private boolean isPresent(Object... aItems) {
1595        boolean result = true;
1596        for (Object item : aItems) {
1597          if (item == null) {
1598            result = false;
1599            break;
1600          }
1601        }
1602        return result;
1603      }
1604    
1605      private DateTime getStartEndDateTime(Integer aDay, Integer aHour, Integer aMinute, Integer aSecond, Integer aNanosecond) {
1606        ensureHasYearMonthDay();
1607        return new DateTime(fYear, fMonth, aDay, aHour, aMinute, aSecond, aNanosecond);
1608      }
1609      
1610      private String calcToStringFormat(){
1611        String result = null; //caller will check for this; null means the set of units is bizarre
1612        if(unitsAllPresent(Unit.YEAR) && unitsAllAbsent(Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1613          result = "YYYY";
1614        }
1615        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH) && unitsAllAbsent(Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1616          result = "YYYY-MM";
1617        }
1618        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY) && unitsAllAbsent(Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1619          result = "YYYY-MM-DD";
1620        }
1621        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR) && unitsAllAbsent(Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1622          result = "YYYY-MM-DD hh";
1623        }
1624        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE) && unitsAllAbsent(Unit.SECOND, Unit.NANOSECONDS)){
1625          result = "YYYY-MM-DD hh:mm";
1626        }
1627        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND) && unitsAllAbsent(Unit.NANOSECONDS)){
1628          result = "YYYY-MM-DD hh:mm:ss";
1629        }
1630        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1631          result = "YYYY-MM-DD hh:mm:ss.fffffffff";
1632        }
1633        else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY) && unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1634          result = "hh:mm:ss.fffffffff";
1635        }
1636        else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.NANOSECONDS) && unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND)){
1637          result = "hh:mm:ss";
1638        }
1639        else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.SECOND, Unit.NANOSECONDS) && unitsAllPresent(Unit.HOUR, Unit.MINUTE)){
1640          result = "hh:mm";
1641        }
1642        return result;
1643      }
1644      
1645      /**
1646        Always treat de-serialization as a full-blown constructor, by
1647        validating the final state of the de-serialized object.
1648      */
1649      private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException {
1650        //always perform the default de-serialization first
1651        aInputStream.defaultReadObject();
1652        //no mutable fields in this case
1653        validateState();
1654      }
1655    
1656       /**
1657        This is the default implementation of writeObject.
1658        Customise if necessary.
1659      */
1660      private void writeObject(ObjectOutputStream aOutputStream) throws IOException {
1661        //perform the default serialization for all non-transient, non-static fields
1662        aOutputStream.defaultWriteObject();
1663      }
1664      
1665    }