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    }