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