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-31 00:00:00.12345678</tt>
413       <li><tt>2009-12-31 00:00:00.1234567</tt>
414       <li><tt>2009-12-31 00:00:00.123456</tt>
415       <li><tt>2009-12-31 23:59:59.12345</tt>
416       <li><tt>2009-01-31 16:01:01.1234</tt>
417       <li><tt>2009-01-01 16:59:00.123</tt>
418       <li><tt>2009-01-01 16:00:01.12</tt>
419       <li><tt>2009-02-28 16:25:17.1</tt>
420       <li><tt>2009-01-01 00:01:01</tt>
421       <li><tt>2009-01-01 16:01</tt>
422       <li><tt>2009-01-01 16</tt>
423       <li><tt>2009-01-01</tt>
424       <li><tt>2009-01</tt>
425       <li><tt>2009</tt>
426       <li><tt>0009</tt>
427       <li><tt>9</tt>
428       <li><tt>00:00:00.123456789</tt>
429       <li><tt>00:00:00.12345678</tt>
430       <li><tt>00:00:00.1234567</tt>
431       <li><tt>00:00:00.123456</tt>
432       <li><tt>23:59:59.12345</tt>
433       <li><tt>01:59:59.1234</tt>
434       <li><tt>23:01:59.123</tt>
435       <li><tt>00:00:00.12</tt>
436       <li><tt>00:59:59.1</tt>
437       <li><tt>23:59:00</tt>
438       <li><tt>23:00:10</tt>
439       <li><tt>00:59</tt>
440       </ul>
441       
442       <P>The range of each field is :
443       <ul>
444       <li>year: 1..9999 
445       <li>month: 01..12
446       <li>day: 01..31
447       <li>hour: 00..23
448       <li>minute: 00..59
449       <li>second: 00..59
450       <li>nanosecond: 0..999999999
451       </ul>
452       
453       <P>Note that <b>database format functions</b> are an option when dealing with date formats. 
454       Since your application is always in control of the SQL used to talk to the database, you can, if needed, usually
455        use database format functions to alter the format of dates returned in a <tt>ResultSet</tt>.
456       */
457      public DateTime(String aDateTime) {
458        fIsAlreadyParsed = false;
459        if (aDateTime == null) {
460          throw new IllegalArgumentException("String passed to DateTime constructor is null. You can use an empty string, but not a null reference.");
461        }
462        fDateTime = aDateTime;
463      }
464    
465      /**
466       Constructor taking each time unit explicitly.
467       
468       <P>Although all parameters are optional, many operations on this class require year-month-day to be 
469       present. 
470       
471       @param aYear 1..9999, optional 
472       @param aMonth 1..12 , optional
473       @param aDay 1..31, cannot exceed the number of days in the given month/year, optional
474       @param aHour 0..23, optional
475       @param aMinute 0..59, optional
476       @param aSecond 0..59, optional
477       @param aNanoseconds 0..999,999,999, optional (allows for databases that store timestamps up to
478       nanosecond precision).
479       */
480      public DateTime(Integer aYear, Integer aMonth, Integer aDay, Integer aHour, Integer aMinute, Integer aSecond, Integer aNanoseconds) {
481        fIsAlreadyParsed = true;
482        fYear = aYear;
483        fMonth = aMonth;
484        fDay = aDay;
485        fHour = aHour;
486        fMinute = aMinute;
487        fSecond = aSecond;
488        fNanosecond = aNanoseconds;
489        validateState();
490      }
491    
492      /**
493       Factory method returns a <tt>DateTime</tt> having year-month-day only, with no time portion.
494       <P>See {@link #DateTime(Integer, Integer, Integer, Integer, Integer, Integer, Integer)} for constraints on the parameters.
495       */
496      public static DateTime forDateOnly(Integer aYear, Integer aMonth, Integer aDay) {
497        return new DateTime(aYear, aMonth, aDay, null, null, null, null);
498      }
499    
500      /**
501       Factory method returns a <tt>DateTime</tt> having hour-minute-second-nanosecond only, with no date portion.
502       <P>See {@link #DateTime(Integer, Integer, Integer, Integer, Integer, Integer, Integer)} for constraints on the parameters.
503       */
504      public static DateTime forTimeOnly(Integer aHour, Integer aMinute, Integer aSecond, Integer aNanoseconds) {
505        return new DateTime(null, null, null, aHour, aMinute, aSecond, aNanoseconds);
506      }
507    
508      /** 
509       Constructor taking a millisecond value and a {@link TimeZone}.
510       This constructor may be use to convert a <tt>java.util.Date</tt> into a <tt>DateTime</tt>.
511       
512       <P>Unfortunately, only millisecond precision is possible for this method.
513       
514       @param aMilliseconds must be in the range corresponding to the range of dates supported by this class (year 1..9999); corresponds 
515       to a millisecond instant on the timeline, measured from the epoch used by {@link java.util.Date}.
516       */
517      public static DateTime forInstant(long aMilliseconds, TimeZone aTimeZone) {
518        Calendar calendar = new GregorianCalendar(aTimeZone);
519        calendar.setTimeInMillis(aMilliseconds);
520        int year = calendar.get(Calendar.YEAR);
521        int month = calendar.get(Calendar.MONTH) + 1; // 0-based
522        int day = calendar.get(Calendar.DAY_OF_MONTH);
523        int hour = calendar.get(Calendar.HOUR_OF_DAY); // 0..23
524        int minute = calendar.get(Calendar.MINUTE);
525        int second = calendar.get(Calendar.SECOND);
526        int milliseconds = calendar.get(Calendar.MILLISECOND);
527        int nanoseconds = milliseconds * 1000 * 1000;
528        return new DateTime(year, month, day, hour, minute, second, nanoseconds);
529      }
530      
531      /**
532        For the given time zone,  return the corresponding time in milliseconds for this <tt>DateTime</tt>.
533        
534        <P>This method is meant to help you convert between a <tt>DateTime</tt> and the 
535        JDK's date-time classes, which are based on the combination of a time zone and a 
536        millisecond value from the Java epoch.
537        <P>Since <tt>DateTime</tt> can go to nanosecond accuracy, the return value can 
538        lose precision. The nanosecond value is truncated to milliseconds, not rounded. 
539       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
540      */
541      public long getMilliseconds(TimeZone aTimeZone){
542        Integer year = getYear();
543        Integer month = getMonth();
544        Integer day = getDay();
545        //coerce missing times to 0:
546        Integer hour = getHour() == null ? 0 : getHour();
547        Integer minute = getMinute() == null ? 0 : getMinute();
548        Integer second = getSecond() == null ? 0 : getSecond();
549        Integer nanos = getNanoseconds() == null ? 0 : getNanoseconds();
550        
551        Calendar calendar = new GregorianCalendar(aTimeZone);
552        calendar.set(Calendar.YEAR, year);
553        calendar.set(Calendar.MONTH, month-1); // 0-based
554        calendar.set(Calendar.DAY_OF_MONTH, day);
555        calendar.set(Calendar.HOUR_OF_DAY, hour); // 0..23
556        calendar.set(Calendar.MINUTE, minute);
557        calendar.set(Calendar.SECOND, second);
558        calendar.set(Calendar.MILLISECOND, nanos/1000000);
559        
560        return calendar.getTimeInMillis();
561      }
562    
563      /**
564       Return the raw date-time String passed to the {@link #DateTime(String)} constructor.
565       Returns <tt>null</tt> if that constructor was not called. See {@link #toString()} as well.
566       */
567      public String getRawDateString() {
568        return fDateTime;
569      }
570    
571      /** Return the year, 1..9999. */
572      public Integer getYear() {
573        ensureParsed();
574        return fYear;
575      }
576    
577      /** Return the Month, 1..12. */
578      public Integer getMonth() {
579        ensureParsed();
580        return fMonth;
581      }
582    
583      /** Return the Day of the Month, 1..31. */
584      public Integer getDay() {
585        ensureParsed();
586        return fDay;
587      }
588    
589      /** Return the Hour, 0..23. */
590      public Integer getHour() {
591        ensureParsed();
592        return fHour;
593      }
594    
595      /** Return the Minute, 0..59. */
596      public Integer getMinute() {
597        ensureParsed();
598        return fMinute;
599      }
600    
601      /** Return the Second, 0..59. */
602      public Integer getSecond() {
603        ensureParsed();
604        return fSecond;
605      }
606    
607      /** Return the Nanosecond, 0..999999999. */
608      public Integer getNanoseconds() {
609        ensureParsed();
610        return fNanosecond;
611      }
612    
613      /**
614       Return the Modified Julian Day Number. 
615       <P>The Modified Julian Day Number is defined by astronomers for simplifying the calculation of the number of days between 2 dates. 
616       Returns a monotonically increasing sequence number. 
617       Day 0 is November 17, 1858 00:00:00 (whose Julian Date was 2400000.5).
618       
619       <P>Using the Modified Julian Day Number instead of the Julian Date has 2 advantages:
620       <ul>
621       <li>it's a smaller number
622       <li>it starts at midnight, not noon (Julian Date starts at noon)
623       </ul>
624       
625       <P>Does not reflect any time portion, if present. 
626       
627       <P>(In spite of its name, this method, like all other methods in this class, uses the 
628       proleptic Gregorian calendar - not the Julian calendar.)
629       
630       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
631       */
632      public Integer getModifiedJulianDayNumber() {
633        ensureHasYearMonthDay();
634        int result = calculateJulianDayNumberAtNoon() - 1 - EPOCH_MODIFIED_JD;
635        return result;
636      }
637    
638      /**
639       Return an index for the weekday for this <tt>DateTime</tt>.
640       Returns 1..7 for Sunday..Saturday.
641       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
642       */
643      public Integer getWeekDay() {
644        ensureHasYearMonthDay();
645        int dayNumber = calculateJulianDayNumberAtNoon() + 1;
646        int index = dayNumber % 7;
647        return index + 1;
648      }
649    
650      /**
651       Return an integer in the range 1..366, representing a count of the number of days from the start of the year.
652       January 1 is counted as day 1.
653       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
654       */
655      public Integer getDayOfYear() {
656        ensureHasYearMonthDay();
657        int k = isLeapYear() ? 1 : 2;
658        Integer result = ((275 * fMonth) / 9) - k * ((fMonth + 9) / 12) + fDay - 30; // integer division
659        return result;
660      }
661    
662      /**
663       Returns true only if the year is a leap year. 
664       <P>Requires year to be present; if not, a runtime exception is thrown.
665       */
666      public Boolean isLeapYear() {
667        ensureParsed();
668        Boolean result = null;
669        if (isPresent(fYear)) {
670          result = isLeapYear(fYear);
671        }
672        else {
673          throw new MissingItem("Year is absent. Cannot determine if leap year.");
674        }
675        return result;
676      }
677    
678      /** 
679       Return the number of days in the month which holds this <tt>DateTime</tt>. 
680       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
681       */
682      public int getNumDaysInMonth() {
683        ensureHasYearMonthDay();
684        return getNumDaysInMonth(fYear, fMonth);
685      }
686    
687      /**
688       Return The week index of this <tt>DateTime</tt> with respect to a given starting <tt>DateTime</tt>.
689       <P>The single parameter to this method defines first day of week number 1.
690       See {@link #getWeekIndex()} as well. 
691       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
692       */
693      public Integer getWeekIndex(DateTime aStartingFromDate) {
694        ensureHasYearMonthDay();
695        aStartingFromDate.ensureHasYearMonthDay();
696        int diff = getModifiedJulianDayNumber() - aStartingFromDate.getModifiedJulianDayNumber();
697        return (diff / 7) + 1; // integer division
698      }
699    
700      /**
701       Return The week index of this <tt>DateTime</tt>, taking day 1 of week 1 as Sunday, January 2, 2000. 
702       <P>See {@link #getWeekIndex(DateTime)} as well, which takes an arbitrary date to define 
703       day 1 of week 1. 
704       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
705       */
706      public Integer getWeekIndex() {
707        DateTime start = DateTime.forDateOnly(2000, 1, 2);
708        return getWeekIndex(start);
709      }
710    
711      /**
712       Return <tt>true</tt> only if this <tt>DateTime</tt> has the same year-month-day as the given parameter.
713       Time is ignored by this method.
714       <P> Requires year-month-day to be present, both for this <tt>DateTime</tt> and for
715       <tt>aThat</tt>; if not, a runtime exception is thrown.
716       */
717      public boolean isSameDayAs(DateTime aThat) {
718        boolean result = false;
719        ensureHasYearMonthDay();
720        aThat.ensureHasYearMonthDay();
721        result = (fYear.equals(aThat.fYear) && fMonth.equals(aThat.fMonth) && fDay.equals(aThat.fDay));
722        return result;
723      }
724    
725      /**  
726       'Less than' comparison.
727       Return <tt>true</tt> only if this <tt>DateTime</tt> comes before the given parameter, according to {@link #compareTo(DateTime)}.  
728      */
729      public boolean lt(DateTime aThat) {
730        return compareTo(aThat) < EQUAL;
731      }
732    
733      /**  
734       'Less than or equal to' comparison.
735       Return <tt>true</tt> only if this <tt>DateTime</tt> comes before the given parameter, according to {@link #compareTo(DateTime)}, 
736       or this <tt>DateTime</tt> equals the given parameter.  
737      */
738      public boolean lteq(DateTime aThat) {
739        return compareTo(aThat) < EQUAL || equals(aThat);
740      }
741    
742      /**
743       'Greater than' comparison.  
744       Return <tt>true</tt> only if this <tt>DateTime</tt> comes after the given parameter, according to {@link #compareTo(DateTime)}. 
745      */
746      public boolean gt(DateTime aThat) {
747        return compareTo(aThat) > EQUAL;
748      }
749      
750      /**  
751       'Greater than or equal to' comparison.
752       Return <tt>true</tt> only if this <tt>DateTime</tt> comes after the given parameter, according to {@link #compareTo(DateTime)}, 
753       or this <tt>DateTime</tt> equals the given parameter.  
754      */
755      public boolean gteq(DateTime aThat) {
756        return compareTo(aThat) > EQUAL || equals(aThat);
757      }
758    
759      /** Return the smallest non-null time unit encapsulated by this <tt>DateTime</tt>. */
760      public Unit getPrecision() {
761        ensureParsed();
762        Unit result = null;
763        if (isPresent(fNanosecond)) {
764          result = Unit.NANOSECONDS;
765        }
766        else if (isPresent(fSecond)) {
767          result = Unit.SECOND;
768        }
769        else if (isPresent(fMinute)) {
770          result = Unit.MINUTE;
771        }
772        else if (isPresent(fHour)) {
773          result = Unit.HOUR;
774        }
775        else if (isPresent(fDay)) {
776          result = Unit.DAY;
777        }
778        else if (isPresent(fMonth)) {
779          result = Unit.MONTH;
780        }
781        else if (isPresent(fYear)) {
782          result = Unit.YEAR;
783        }
784        return result;
785      }
786    
787      /**
788       Truncate this <tt>DateTime</tt> to the given precision.
789       <P>The return value will have all items lower than the given precision simply set to
790       <tt>null</tt>. In addition, the return value will not include any date-time String passed to the 
791       {@link #DateTime(String)} constructor.
792       
793       @param aPrecision takes any value <i>except</i> {@link Unit#NANOSECONDS} (since it makes no sense to truncate to the highest
794       available precision).
795       */
796      public DateTime truncate(Unit aPrecision) {
797        ensureParsed();
798        DateTime result = null;
799        if (Unit.NANOSECONDS == aPrecision) {
800          throw new IllegalArgumentException("It makes no sense to truncate to nanosecond precision, since that's the highest precision available.");
801        }
802        else if (Unit.SECOND == aPrecision) {
803          result = new DateTime(fYear, fMonth, fDay, fHour, fMinute, fSecond, null);
804        }
805        else if (Unit.MINUTE == aPrecision) {
806          result = new DateTime(fYear, fMonth, fDay, fHour, fMinute, null, null);
807        }
808        else if (Unit.HOUR == aPrecision) {
809          result = new DateTime(fYear, fMonth, fDay, fHour, null, null, null);
810        }
811        else if (Unit.DAY == aPrecision) {
812          result = new DateTime(fYear, fMonth, fDay, null, null, null, null);
813        }
814        else if (Unit.MONTH == aPrecision) {
815          result = new DateTime(fYear, fMonth, null, null, null, null, null);
816        }
817        else if (Unit.YEAR == aPrecision) {
818          result = new DateTime(fYear, null, null, null, null, null, null);
819        }
820        return result;
821      }
822    
823      /**
824       Return <tt>true</tt> only if all of the given units are present in this <tt>DateTime</tt>.
825       If a unit is <i>not</i> included in the argument list, then no test is made for its presence or absence
826       in this <tt>DateTime</tt> by this method.
827       */
828      public boolean unitsAllPresent(Unit... aUnits) {
829        boolean result = true;
830        ensureParsed();
831        for (Unit unit : aUnits) {
832          if (Unit.NANOSECONDS == unit) {
833            result = result && fNanosecond != null;
834          }
835          else if (Unit.SECOND == unit) {
836            result = result && fSecond != null;
837          }
838          else if (Unit.MINUTE == unit) {
839            result = result && fMinute != null;
840          }
841          else if (Unit.HOUR == unit) {
842            result = result && fHour != null;
843          }
844          else if (Unit.DAY == unit) {
845            result = result && fDay != null;
846          }
847          else if (Unit.MONTH == unit) {
848            result = result && fMonth != null;
849          }
850          else if (Unit.YEAR == unit) {
851            result = result && fYear != null;
852          }
853        }
854        return result;
855      }
856    
857      /**
858       Return <tt>true</tt> only if this <tt>DateTime</tt> has a non-null values for year, month, and day.
859      */
860      public boolean hasYearMonthDay() {
861        return unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY);
862      }
863    
864      /**
865       Return <tt>true</tt> only if this <tt>DateTime</tt> has a non-null values for hour, minute, and second.
866      */
867      public boolean hasHourMinuteSecond() {
868        return unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND);
869      }
870    
871      /**
872       Return <tt>true</tt> only if all of the given units are absent from this <tt>DateTime</tt>.
873       If a unit is <i>not</i> included in the argument list, then no test is made for its presence or absence
874       in this <tt>DateTime</tt> by this method.
875       */
876      public boolean unitsAllAbsent(Unit... aUnits) {
877        boolean result = true;
878        ensureParsed();
879        for (Unit unit : aUnits) {
880          if (Unit.NANOSECONDS == unit) {
881            result = result && fNanosecond == null;
882          }
883          else if (Unit.SECOND == unit) {
884            result = result && fSecond == null;
885          }
886          else if (Unit.MINUTE == unit) {
887            result = result && fMinute == null;
888          }
889          else if (Unit.HOUR == unit) {
890            result = result && fHour == null;
891          }
892          else if (Unit.DAY == unit) {
893            result = result && fDay == null;
894          }
895          else if (Unit.MONTH == unit) {
896            result = result && fMonth == null;
897          }
898          else if (Unit.YEAR == unit) {
899            result = result && fYear == null;
900          }
901        }
902        return result;
903      }
904    
905      /**
906       Return this <tt>DateTime</tt> with the time portion coerced to '00:00:00.000000000'.
907       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
908       */
909      public DateTime getStartOfDay() {
910        ensureHasYearMonthDay();
911        return getStartEndDateTime(fDay, 0, 0, 0, 0);
912      }
913    
914      /**
915       Return this <tt>DateTime</tt> with the time portion coerced to '23:59:59.999999999'. 
916       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
917       */
918      public DateTime getEndOfDay() {
919        ensureHasYearMonthDay();
920        return getStartEndDateTime(fDay, 23, 59, 59, 999999999);
921      }
922    
923      /**
924       Return this <tt>DateTime</tt> with the time portion coerced to '00:00:00.000000000', 
925       and the day coerced to 1.
926       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
927       */
928      public DateTime getStartOfMonth() {
929        ensureHasYearMonthDay();
930        return getStartEndDateTime(1, 0, 0, 0, 0);
931      }
932    
933      /**
934       Return this <tt>DateTime</tt> with the time portion coerced to '23:59:59.999999999', 
935       and the day coerced to the end of the month.
936       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
937       */
938      public DateTime getEndOfMonth() {
939        ensureHasYearMonthDay();
940        return getStartEndDateTime(getNumDaysInMonth(), 23, 59, 59, 999999999);
941      }
942    
943      /**
944       Create a new <tt>DateTime</tt> by adding an interval to this one.
945       
946       <P>See {@link #plusDays(Integer)} as well. 
947       
948       <P>Changes are always applied by this class <i>in order of decreasing units of time</i>: 
949       years first, then months, and so on. After changing both the year and month, a check on the month-day combination is made before 
950       any change is made to the day. If the day exceeds the number of days in the given month/year, then 
951       (and only then) the given {@link DayOverflow} policy applied, and the day-of-the-month is adusted accordingly.
952       
953       <P>Afterwards, the day is then changed in the usual way, followed by the remaining items (hour, minute, and second). 
954       Changes to the fractional seconds are not included in this method, since there doesn't seem to be much practical use for it. 
955       
956       <P>The returned value cannot come after <tt>9999-12-13 23:59:59</tt>.
957       
958       <P>This class works with <tt>DateTime</tt>'s having the following items present :
959       <ul>
960       <li>year-month-day and hour-minute-second (and optional nanoseconds)
961       <li>year-month-day only. In this case, if a calculation with a time part is performed, that time part 
962       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.
963       <li>hour-minute-second (and optional nanoseconds) only. In this case, the calculation is done starting with the   
964       the arbitrary date <tt>0001-01-01</tt> (in order to remain within a valid state space of <tt>DateTime</tt>). 
965       </ul>
966       
967       @param aNumYears positive, required, in range 0...9999 
968       @param aNumMonths positive, required, in range 0...9999 
969       @param aNumDays positive, required, in range 0...9999 
970       @param aNumHours positive, required, in range 0...9999 
971       @param aNumMinutes positive, required, in range 0...9999 
972       @param aNumSeconds positive, required, in range 0...9999 
973       */
974      public DateTime plus(Integer aNumYears, Integer aNumMonths, Integer aNumDays, Integer aNumHours, Integer aNumMinutes, Integer aNumSeconds, DayOverflow aDayOverflow) {
975        DateTimeInterval interval = new DateTimeInterval(this, aDayOverflow);
976        return interval.plus(aNumYears, aNumMonths, aNumDays, aNumHours, aNumMinutes, aNumSeconds);
977      }
978    
979      /**
980       Create a new <tt>DateTime</tt> by subtracting an interval to this one.
981       
982       <P>See {@link #minusDays(Integer)} as well. 
983       <P>This method has nearly the same behavior as {@link #plus(Integer, Integer, Integer, Integer, Integer, Integer, DayOverflow)},
984       except that the return value cannot come before <tt>0001-01-01 00:00:00</tt>.
985       */
986      public DateTime minus(Integer aNumYears, Integer aNumMonths, Integer aNumDays, Integer aNumHours, Integer aNumMinutes, Integer aNumSeconds, DayOverflow aDayOverflow) {
987        DateTimeInterval interval = new DateTimeInterval(this, aDayOverflow);
988        return interval.minus(aNumYears, aNumMonths, aNumDays, aNumHours, aNumMinutes, aNumSeconds);
989      }
990    
991      /**
992       Return a new <tt>DateTime</tt> by adding an integral number of days to this one.
993       
994       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
995       @param aNumDays can be either sign; if negative, then the days are subtracted. 
996       */
997      public DateTime plusDays(Integer aNumDays) {
998        ensureHasYearMonthDay();
999        int thisJDAtNoon = getModifiedJulianDayNumber() + 1 + EPOCH_MODIFIED_JD;
1000        int resultJD = thisJDAtNoon + aNumDays;
1001        DateTime datePortion = fromJulianDayNumberAtNoon(resultJD);
1002        return new DateTime(datePortion.getYear(), datePortion.getMonth(), datePortion.getDay(), fHour, fMinute, fSecond, fNanosecond);
1003      }
1004    
1005      /**
1006       Return a new <tt>DateTime</tt> by subtracting an integral number of days from this one.
1007       
1008       <P>Requires year-month-day to be present; if not, a runtime exception is thrown.
1009       @param aNumDays can be either sign; if negative, then the days are added.
1010       */
1011      public DateTime minusDays(Integer aNumDays) {
1012        return plusDays(-1 * aNumDays);
1013      }
1014    
1015      /**  
1016       The whole number of days between this <tt>DateTime</tt> and the given parameter. 
1017       <P>Requires year-month-day to be present, both for this <tt>DateTime</tt> and for the <tt>aThat</tt> 
1018       parameter; if not, a runtime exception is thrown.
1019      */
1020      public int numDaysFrom(DateTime aThat) {
1021        return aThat.getModifiedJulianDayNumber() - this.getModifiedJulianDayNumber();
1022      }
1023    
1024      /** 
1025        The number of seconds between this <tt>DateTime</tt> and the given argument.
1026        <P>If only time information is present in both this <tt>DateTime</tt> and <tt>aThat</tt>, then there are 
1027        no restrictions on the values of the time units. 
1028        <P>If any date information is present, in either this <tt>DateTime</tt> or <tt>aThat</tt>, 
1029        then full year-month-day must be present in both; if not, then the date portion will be ignored, and only the 
1030        time portion will contribute to the calculation.
1031      */
1032      public long numSecondsFrom(DateTime aThat) {
1033        long result = 0;
1034        if(hasYearMonthDay() && aThat.hasYearMonthDay()){
1035          result = numDaysFrom(aThat) * 86400; //just the day portion
1036        }
1037        result = result - this.numSecondsInTimePortion() + aThat.numSecondsInTimePortion();
1038        return result;
1039      }
1040    
1041      /**
1042       Output this <tt>DateTime</tt> as a formatted String using numbers, with no localizable text.
1043       
1044       <P>Example:
1045       <PRE>dt.format("YYYY-MM-DD hh:mm:ss");</PRE>
1046       would generate text of the form
1047       <PRE>2009-09-09 18:23:59</PRE>
1048       
1049       <P>If months, weekdays, or AM/PM indicators are output as localizable text, you must use {@link #format(String, Locale)}.
1050       @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1051       */
1052      public String format(String aFormat) {
1053        DateTimeFormatter format = new DateTimeFormatter(aFormat);
1054        return format.format(this);
1055      }
1056    
1057      /**
1058       Output this <tt>DateTime</tt> as a formatted String using numbers and/or localizable text.
1059       
1060       <P>This method is intended for alphanumeric output, such as '<tt>Sunday, November 14, 1858 10:00 AM</tt>'.
1061       <P>If months and weekdays are output as numbers, you are encouraged to use {@link #format(String)} instead.
1062       
1063       @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1064       @param aLocale used to generate text for Month, Weekday and AM/PM indicator; required only by patterns which return localized 
1065       text, instead of numeric forms.
1066       */
1067      public String format(String aFormat, Locale aLocale) {
1068        DateTimeFormatter format = new DateTimeFormatter(aFormat, aLocale);
1069        return format.format(this);
1070      }
1071    
1072      /**
1073       Output this <tt>DateTime</tt> as a formatted String using numbers and explicit text for months, weekdays, and AM/PM indicator. 
1074    
1075       <P>Use of this method is likely relatively rare; it should be used only if the output of {@link #format(String, Locale)}  is 
1076       inadequate. 
1077       
1078       @param aFormat uses the <a href="#FormattingLanguage">formatting mini-language</a> defined in the class comment.
1079       @param aMonths contains text for all 12 months, starting with January; size must be 12. 
1080       @param aWeekdays contains text for all 7 weekdays, starting with Sunday; size must be 7. 
1081       @param aAmPmIndicators contains text for A.M and P.M. indicators (in that order); size must be 2. 
1082       */
1083      public String format(String aFormat, List<String> aMonths, List<String> aWeekdays, List<String> aAmPmIndicators) {
1084        DateTimeFormatter format = new DateTimeFormatter(aFormat, aMonths, aWeekdays, aAmPmIndicators);
1085        return format.format(this);
1086      }
1087    
1088      /**
1089       Return the current date-time.
1090       <P>Combines the configured implementation of {@link TimeSource} with the given {@link TimeZone}.
1091       The <tt>TimeZone</tt> will typically come from your implementation of {@link TimeZoneSource}. 
1092       
1093       <P>In an Action, the current date-time date can be referenced using 
1094       <PRE>DateTime.now(getTimeZone())</PRE>
1095       See {@link ActionImpl#getTimeZone()}.
1096       
1097       <P>Only millisecond precision is possible for this method.
1098       */
1099      public static DateTime now(TimeZone aTimeZone) {
1100        TimeSource timesource = BuildImpl.forTimeSource();
1101        return forInstant(timesource.currentTimeMillis(), aTimeZone);
1102      }
1103    
1104      /** 
1105       Return the current date.
1106       <P>As in {@link #now(TimeZone)}, but truncates the time portion, leaving only year-month-day.
1107       <P>In an Action, today's date can be referenced using 
1108       <PRE>DateTime.today(getTimeZone())</PRE>
1109       See {@link ActionImpl#getTimeZone()}. 
1110       */
1111      public static DateTime today(TimeZone aTimeZone) {
1112        DateTime result = now(aTimeZone);
1113        return result.truncate(Unit.DAY);
1114      }
1115    
1116      /** Return <tt>true</tt> only if this date is in the future, with respect to {@link #now(TimeZone)}. */
1117      public boolean isInTheFuture(TimeZone aTimeZone) {
1118        return now(aTimeZone).lt(this);
1119      }
1120    
1121      /** Return <tt>true</tt> only if this date is in the past, with respect to {@link #now(TimeZone)}. */
1122      public boolean isInThePast(TimeZone aTimeZone) {
1123        return now(aTimeZone).gt(this);
1124      }
1125      
1126      /**
1127        Return a <tt>DateTime</tt> corresponding to a change from one {@link TimeZone} to another.
1128        
1129        <P>A <tt>DateTime</tt> object has an implicit and immutable time zone. 
1130        If you need to change the implicit time zone, you can use this method to do so.
1131        
1132        <P>Example :
1133        <PRE>
1134    TimeZone fromUK = TimeZone.getTimeZone("Europe/London");
1135    TimeZone toIndonesia = TimeZone.getTimeZone("Asia/Jakarta");
1136    DateTime newDt = oldDt.changeTimeZone(fromUK, toIndonesia);
1137        </PRE>
1138         
1139       <P>Requires year-month-day-hour to be present; if not, a runtime exception is thrown.
1140       @param aFromTimeZone the implicit time zone of this object.
1141       @param aToTimeZone the implicit time zone of the <tt>DateTime</tt> returned by this method.
1142       @return aDateTime corresponding to the change of time zone implied by the 2 parameters.
1143       */
1144      public DateTime changeTimeZone(TimeZone aFromTimeZone, TimeZone aToTimeZone){
1145        DateTime result = null;
1146        ensureHasYearMonthDay();
1147        if (unitsAllAbsent(Unit.HOUR)){
1148          throw new IllegalArgumentException("DateTime does not include the hour. Cannot change the time zone if no hour is present.");
1149        }
1150        Calendar fromDate = new GregorianCalendar(aFromTimeZone);
1151        fromDate.set(Calendar.YEAR, getYear());
1152        fromDate.set(Calendar.MONTH, getMonth()-1);
1153        fromDate.set(Calendar.DAY_OF_MONTH, getDay());
1154        fromDate.set(Calendar.HOUR_OF_DAY, getHour());
1155        if(getMinute() != null) {
1156          fromDate.set(Calendar.MINUTE, getMinute());
1157        }
1158        else {
1159          fromDate.set(Calendar.MINUTE, 0);
1160        }
1161        //other items zeroed out here, since they don't matter for time zone calculations
1162        fromDate.set(Calendar.SECOND, 0);
1163        fromDate.set(Calendar.MILLISECOND, 0);
1164    
1165        //millisecond precision is OK here, since the seconds/nanoseconds are not part of the calc
1166        Calendar toDate = new GregorianCalendar(aToTimeZone);
1167        toDate.setTimeInMillis(fromDate.getTimeInMillis());
1168        //needed if this date has hour, but no minute (bit of an oddball case) :
1169        Integer minute = getMinute() != null ? toDate.get(Calendar.MINUTE) : null;
1170        result = new DateTime(
1171          toDate.get(Calendar.YEAR), toDate.get(Calendar.MONTH) + 1, toDate.get(Calendar.DAY_OF_MONTH), 
1172          toDate.get(Calendar.HOUR_OF_DAY), minute, getSecond(), getNanoseconds() 
1173        );
1174        return result;
1175      }
1176    
1177      /**
1178       Compare this object to another, for ordering purposes.
1179       <P> Uses the 7 date-time elements (year..nanosecond). The Year is considered the most
1180       significant item, and the Nanosecond the least significant item. Null items are placed first in this comparison.
1181       */
1182      public int compareTo(DateTime aThat) {
1183        if (this == aThat) return EQUAL;
1184        ensureParsed();
1185        aThat.ensureParsed();
1186    
1187        NullsGo nullsGo = NullsGo.FIRST;
1188        int comparison = ModelUtil.comparePossiblyNull(this.fYear, aThat.fYear, nullsGo);
1189        if (comparison != EQUAL)  return comparison;
1190    
1191        comparison = ModelUtil.comparePossiblyNull(this.fMonth, aThat.fMonth, nullsGo);
1192        if (comparison != EQUAL)  return comparison;
1193    
1194        comparison = ModelUtil.comparePossiblyNull(this.fDay, aThat.fDay, nullsGo);
1195        if (comparison != EQUAL)  return comparison;
1196    
1197        comparison = ModelUtil.comparePossiblyNull(this.fHour, aThat.fHour, nullsGo);
1198        if (comparison != EQUAL)  return comparison;
1199    
1200        comparison = ModelUtil.comparePossiblyNull(this.fMinute, aThat.fMinute, nullsGo);
1201        if (comparison != EQUAL)  return comparison;
1202    
1203        comparison = ModelUtil.comparePossiblyNull(this.fSecond, aThat.fSecond, nullsGo);
1204        if (comparison != EQUAL)  return comparison;
1205    
1206        comparison = ModelUtil.comparePossiblyNull(this.fNanosecond, aThat.fNanosecond, nullsGo);
1207        if (comparison != EQUAL)  return comparison;
1208    
1209        return EQUAL;
1210      }
1211    
1212      /**
1213       Equals method for this object.
1214       
1215       <P>Equality is determined by the 7 date-time elements (year..nanosecond).
1216       */
1217      @Override public boolean equals(Object aThat) {
1218        /*
1219         * Implementation note: it was considered branching this method, according to whether
1220         * the objects are already parsed. That was rejected, since maintaining 'synchronicity'
1221         * with hashCode would not then be possible, since hashCode is based only on one object,
1222         * not two.
1223         */
1224        ensureParsed();
1225        Boolean result = ModelUtil.quickEquals(this, aThat);
1226        if (result == null) {
1227          DateTime that = (DateTime)aThat;
1228          that.ensureParsed();
1229          result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
1230        }
1231        return result;
1232      }
1233    
1234      /**
1235       Hash code for this object.
1236       
1237       <P> Uses the same 7 date-time elements (year..nanosecond) as used by
1238       {@link #equals(Object)}.
1239       */
1240      @Override public int hashCode() {
1241        if (fHashCode == 0) {
1242          ensureParsed();
1243          fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
1244        }
1245        return fHashCode;
1246      }
1247    
1248      /**
1249      Intended for <i>debugging and logging</i> only.
1250      
1251      <P><b>To format this <tt>DateTime</tt> for presentation to the user, see the various <tt>format</tt> methods.</b>
1252      
1253      <P>If the {@link #DateTime(String)} constructor was called, then return that String. 
1254      
1255      <P>Otherwise, the return value is constructed from each date-time element, in a fixed format, depending 
1256      on which time units are present. Example values :
1257      <ul>
1258       <li>2011-04-30 13:59:59.123456789
1259       <li>2011-04-30 13:59:59
1260       <li>2011-04-30
1261       <li>2011-04-30 13:59
1262       <li>13:59:59.123456789
1263       <li>13:59:59
1264       <li>and so on...
1265      </ul>
1266      
1267      <P>In the great majority of cases, this will give reasonable output for debugging and logging statements.
1268      
1269      <P>In cases where a bizarre combinations of time units is present, the return value is presented in a verbose form.
1270      For example, if all time units are present <i>except</i> for minutes, the return value has this form:
1271      <PRE>Y:2001 M:1 D:31 h:13 m:null s:59 f:123456789</PRE> 
1272     */
1273      @Override public String toString() {
1274        String result = "";
1275        if (Util.textHasContent(fDateTime)) {
1276          result = fDateTime;
1277        }
1278        else {
1279          String format = calcToStringFormat();
1280          if(format != null){
1281            result = format(calcToStringFormat());
1282          }
1283          else {
1284            StringBuilder builder = new StringBuilder();
1285            addToString("Y", fYear, builder);
1286            addToString("M", fMonth, builder);
1287            addToString("D", fDay, builder);
1288            addToString("h", fHour, builder);
1289            addToString("m", fMinute, builder);
1290            addToString("s", fSecond, builder);
1291            addToString("f", fNanosecond, builder);
1292            result = builder.toString().trim();
1293          }
1294        }
1295        return result;
1296      }
1297    
1298      // PACKAGE-PRIVATE (for unit testing, mostly)
1299    
1300      static final class ItemOutOfRange extends RuntimeException {
1301        ItemOutOfRange(String aMessage) {
1302          super(aMessage);
1303        }
1304      }
1305    
1306      static final class MissingItem extends RuntimeException {
1307        MissingItem(String aMessage) {
1308          super(aMessage);
1309        }
1310      }
1311    
1312      /** Intended as internal tool, for testing only. Not scope is not public! */
1313      void ensureParsed() {
1314        if (!fIsAlreadyParsed) {
1315          parseDateTimeText();
1316        }
1317      }
1318    
1319      /**
1320       Return the number of days in the given month. The returned value depends on the year as
1321       well, because of leap years. Returns <tt>null</tt> if either year or month are
1322       absent. WRONG - should be public??
1323       Package-private, needed for interval calcs.
1324       */
1325      static Integer getNumDaysInMonth(Integer aYear, Integer aMonth) {
1326        Integer result = null;
1327        if (aYear != null && aMonth != null) {
1328          if (aMonth == 1) {
1329            result = 31;
1330          }
1331          else if (aMonth == 2) {
1332            result = isLeapYear(aYear) ? 29 : 28;
1333          }
1334          else if (aMonth == 3) {
1335            result = 31;
1336          }
1337          else if (aMonth == 4) {
1338            result = 30;
1339          }
1340          else if (aMonth == 5) {
1341            result = 31;
1342          }
1343          else if (aMonth == 6) {
1344            result = 30;
1345          }
1346          else if (aMonth == 7) {
1347            result = 31;
1348          }
1349          else if (aMonth == 8) {
1350            result = 31;
1351          }
1352          else if (aMonth == 9) {
1353            result = 30;
1354          }
1355          else if (aMonth == 10) {
1356            result = 31;
1357          }
1358          else if (aMonth == 11) {
1359            result = 30;
1360          }
1361          else if (aMonth == 12) {
1362            result = 31;
1363          }
1364          else {
1365            throw new AssertionError("Month is out of range 1..12:" + aMonth);
1366          }
1367        }
1368        return result;
1369      }
1370    
1371      static DateTime fromJulianDayNumberAtNoon(int aJDAtNoon) {
1372        //http://www.hermetic.ch/cal_stud/jdn.htm
1373        int l = aJDAtNoon + 68569;
1374        int n = (4 * l) / 146097;
1375        l = l - (146097 * n + 3) / 4;
1376        int i = (4000 * (l + 1)) / 1461001;
1377        l = l - (1461 * i) / 4 + 31;
1378        int j = (80 * l) / 2447;
1379        int d = l - (2447 * j) / 80;
1380        l = j / 11;
1381        int m = j + 2 - (12 * l);
1382        int y = 100 * (n - 49) + i + l;
1383        return DateTime.forDateOnly(y, m, d);
1384      }
1385    
1386      // PRIVATE
1387    
1388      /*
1389       There are 2 representations of a date - a text form, and a 'parsed' form, in which all
1390       of the elements of the date are separated out. A DateTime starts out with one of these
1391       forms, and may need to generate the other.
1392       */
1393    
1394      /** The text form of a date. @serial */
1395      private String fDateTime;
1396    
1397      /* The following 7 items represent the parsed form of a DateTime. */
1398      /**  @serial */
1399      private Integer fYear;
1400      /**  @serial */
1401      private Integer fMonth;
1402      /**  @serial */
1403      private Integer fDay;
1404      /**  @serial */
1405      private Integer fHour;
1406      /**  @serial */
1407      private Integer fMinute;
1408      /**  @serial */
1409      private Integer fSecond;
1410      /**  @serial */
1411      private Integer fNanosecond;
1412    
1413      /** Indicates if this DateTime has been parsed into its 7 constituents. @serial */
1414      private boolean fIsAlreadyParsed;
1415    
1416      /** @serial */
1417      private int fHashCode;
1418      
1419      private static final int EQUAL = 0;
1420      
1421      private static int EPOCH_MODIFIED_JD = 2400000;
1422      
1423      private static final long serialVersionUID =  -1300068157085493891L; 
1424        
1425      /**
1426       Return a the whole number, with no fraction.
1427       The JD at noon is 1 more than the JD at midnight. 
1428       */
1429      private int calculateJulianDayNumberAtNoon() {
1430        //http://www.hermetic.ch/cal_stud/jdn.htm
1431        int y = fYear;
1432        int m = fMonth;
1433        int d = fDay;
1434        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;
1435        return result;
1436      }
1437    
1438      private void ensureHasYearMonthDay() {
1439        ensureParsed();
1440        if (!hasYearMonthDay()) {
1441          throw new MissingItem("DateTime does not include year/month/day.");
1442        }
1443      }
1444    
1445      /** Return the number of seconds in any existing time portion of the date. */
1446      private int numSecondsInTimePortion() {
1447        int result = 0;
1448        if (fSecond != null) {
1449          result = result + fSecond;
1450        }
1451        if (fMinute != null) {
1452          result = result + 60 * fMinute;
1453        }
1454        if (fHour != null) {
1455          result = result + 3600 * fHour;
1456        }
1457        return result;
1458      }
1459    
1460      private void validateState() {
1461        checkRange(fYear, 1, 9999, "Year");
1462        checkRange(fMonth, 1, 12, "Month");
1463        checkRange(fDay, 1, 31, "Day");
1464        checkRange(fHour, 0, 23, "Hour");
1465        checkRange(fMinute, 0, 59, "Minute");
1466        checkRange(fSecond, 0, 59, "Second");
1467        checkRange(fNanosecond, 0, 999999999, "Nanosecond");
1468        checkNumDaysInMonth(fYear, fMonth, fDay);
1469      }
1470    
1471      private void checkRange(Integer aValue, int aMin, int aMax, String aName) {
1472        if (!Check.optional(aValue, Check.range(aMin, aMax))) {
1473          throw new ItemOutOfRange(aName + " is not in the range " + aMin + ".." + aMax + ". Value is:" + aValue);
1474        }
1475      }
1476    
1477      private void checkNumDaysInMonth(Integer aYear, Integer aMonth, Integer aDay) {
1478        if (hasYearMonthDay(aYear, aMonth, aDay) && aDay > getNumDaysInMonth(aYear, aMonth)) {
1479          throw new ItemOutOfRange("The day-of-the-month value '" + aDay + "' exceeds the number of days in the month: " + getNumDaysInMonth(aYear, aMonth));
1480        }
1481      }
1482    
1483      private void parseDateTimeText() {
1484        DateTimeParser parser = new DateTimeParser();
1485        DateTime dateTime = parser.parse(fDateTime);
1486        /*
1487         * This is unusual - we essentially copy from one object to another. This could be
1488         * avoided by building another interface, But defining a top-level interface for this
1489         * simple task is too high a price.
1490         */
1491        fYear = dateTime.fYear;
1492        fMonth = dateTime.fMonth;
1493        fDay = dateTime.fDay;
1494        fHour = dateTime.fHour;
1495        fMinute = dateTime.fMinute;
1496        fSecond = dateTime.fSecond;
1497        fNanosecond = dateTime.fNanosecond;
1498        validateState();
1499      }
1500    
1501      private boolean hasYearMonthDay(Integer aYear, Integer aMonth, Integer aDay) {
1502        return isPresent(aYear, aMonth, aDay);
1503      }
1504    
1505      private static boolean isLeapYear(Integer aYear) {
1506        boolean result = false;
1507        if (aYear % 100 == 0) {
1508          // this is a century year
1509          if (aYear % 400 == 0) {
1510            result = true;
1511          }
1512        }
1513        else if (aYear % 4 == 0) {
1514          result = true;
1515        }
1516        return result;
1517      }
1518    
1519      private Object[] getSignificantFields() {
1520        return new Object[]{fYear, fMonth, fDay, fHour, fMinute, fSecond, fNanosecond};
1521      }
1522    
1523      private void addToString(String aName, Object aValue, StringBuilder aBuilder) {
1524        aBuilder.append(aName + ":" + String.valueOf(aValue) + " ");
1525      }
1526    
1527      /** Return true only if all the given arguments are non-null. */
1528      private boolean isPresent(Object... aItems) {
1529        boolean result = true;
1530        for (Object item : aItems) {
1531          if (item == null) {
1532            result = false;
1533            break;
1534          }
1535        }
1536        return result;
1537      }
1538    
1539      private DateTime getStartEndDateTime(Integer aDay, Integer aHour, Integer aMinute, Integer aSecond, Integer aNanosecond) {
1540        ensureHasYearMonthDay();
1541        return new DateTime(fYear, fMonth, aDay, aHour, aMinute, aSecond, aNanosecond);
1542      }
1543      
1544      private String calcToStringFormat(){
1545        String result = null; //caller will check for this; null means the set of units is bizarre
1546        if(unitsAllPresent(Unit.YEAR) && unitsAllAbsent(Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1547          result = "YYYY";
1548        }
1549        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH) && unitsAllAbsent(Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1550          result = "YYYY-MM";
1551        }
1552        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY) && unitsAllAbsent(Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1553          result = "YYYY-MM-DD";
1554        }
1555        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR) && unitsAllAbsent(Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1556          result = "YYYY-MM-DD hh";
1557        }
1558        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE) && unitsAllAbsent(Unit.SECOND, Unit.NANOSECONDS)){
1559          result = "YYYY-MM-DD hh:mm";
1560        }
1561        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND) && unitsAllAbsent(Unit.NANOSECONDS)){
1562          result = "YYYY-MM-DD hh:mm:ss";
1563        }
1564        else if (unitsAllPresent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1565          result = "YYYY-MM-DD hh:mm:ss.fffffffff";
1566        }
1567        else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY) && unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND, Unit.NANOSECONDS)){
1568          result = "hh:mm:ss.fffffffff";
1569        }
1570        else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.NANOSECONDS) && unitsAllPresent(Unit.HOUR, Unit.MINUTE, Unit.SECOND)){
1571          result = "hh:mm:ss";
1572        }
1573        else if (unitsAllAbsent(Unit.YEAR, Unit.MONTH, Unit.DAY, Unit.SECOND, Unit.NANOSECONDS) && unitsAllPresent(Unit.HOUR, Unit.MINUTE)){
1574          result = "hh:mm";
1575        }
1576        return result;
1577      }
1578      
1579      /**
1580        Always treat de-serialization as a full-blown constructor, by
1581        validating the final state of the de-serialized object.
1582      */
1583      private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException {
1584        //always perform the default de-serialization first
1585        aInputStream.defaultReadObject();
1586        //no mutable fields in this case
1587        validateState();
1588      }
1589    
1590       /**
1591        This is the default implementation of writeObject.
1592        Customise if necessary.
1593      */
1594      private void writeObject(ObjectOutputStream aOutputStream) throws IOException {
1595        //perform the default serialization for all non-transient, non-static fields
1596        aOutputStream.defaultWriteObject();
1597      }
1598      
1599    }