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