KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > value > DateTimeValue


1 package net.sf.saxon.value;
2 import net.sf.saxon.Controller;
3 import net.sf.saxon.ConversionContext;
4 import net.sf.saxon.expr.XPathContext;
5 import net.sf.saxon.functions.Component;
6 import net.sf.saxon.om.FastStringBuffer;
7 import net.sf.saxon.trans.DynamicError;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.type.BuiltInAtomicType;
10 import net.sf.saxon.type.ItemType;
11 import net.sf.saxon.type.Type;
12 import net.sf.saxon.type.ValidationException;
13
14 import java.math.BigDecimal JavaDoc;
15 import java.util.*;
16
17 /**
18 * A value of type DateTime
19 */

20
21 public final class DateTimeValue extends CalendarValue {
22
23     // UTCDate is held as a redundant representation of the
24
// normalized (UTC) value.
25
private Date UTCDate = null; // Always read this using getUTCDate
26

27
28     /**
29     * get the dateTime value representing the nominal
30     * date/time of this transformation run. Two calls within the same
31     * query or transformation will always return the same answer.
32     */

33
34     public static DateTimeValue getCurrentDateTime(XPathContext context) {
35         Controller c;
36         if (context==null || (c = context.getController()) == null) {
37             // non-XSLT/XQuery environment
38
// We also take this path when evaluating compile-time expressions that require an implicit timezone.
39
return new DateTimeValue(new GregorianCalendar(), true);
40         } else {
41             return c.getCurrentDateTime();
42         }
43     }
44
45     /**
46     * Constructor: create a dateTime value given a Java calendar object
47     * @param calendar holds the date and time
48     * @param tzSpecified indicates whether the timezone is specified
49     */

50
51     public DateTimeValue(GregorianCalendar calendar, boolean tzSpecified) {
52         this.calendar = calendar;
53         zoneSpecified = tzSpecified;
54     }
55
56     /**
57      * Constructor: create a dateTime value given a date and a time.
58      * @param date the date
59      * @param time the time
60      * @throws XPathException if the timezones are both present and inconsistent
61      */

62
63     public DateTimeValue(DateValue date, TimeValue time) throws XPathException {
64         SecondsDurationValue tz1 = (SecondsDurationValue)date.getComponent(Component.TIMEZONE);
65         SecondsDurationValue tz2 = (SecondsDurationValue)time.getComponent(Component.TIMEZONE);
66         zoneSpecified = (tz1 != null || tz2 != null);
67         if (tz1 != null && tz2 != null && !tz1.equals(tz2)) {
68             DynamicError err = new DynamicError("Supplied date and time are in different timezones");
69             err.setErrorCode("FORG0008");
70             throw err;
71         }
72         // create a calendar that uses the timezone actually specified, or GMT otherwise
73
//calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
74
TimeZone zone;
75         SecondsDurationValue tz = (tz1 == null ? tz2 : tz1);
76         int zoneOffset = (tz == null ? 0 : (int)tz.getLengthInMilliseconds());
77         zone = (tz == null ? TimeZone.getTimeZone("GMT") : new SimpleTimeZone(zoneOffset, "LLL"));
78         calendar = new GregorianCalendar(zone);
79         calendar.setLenient(false);
80         final int year = (int)((IntegerValue)date.getComponent(Component.YEAR)).longValue();
81         final int month = (int)((IntegerValue)date.getComponent(Component.MONTH)).longValue();
82         final int day = (int)((IntegerValue)date.getComponent(Component.DAY)).longValue();
83         final int hour = (int)((IntegerValue)time.getComponent(Component.HOURS)).longValue();
84         final int minute = (int)((IntegerValue)time.getComponent(Component.MINUTES)).longValue();
85         final BigDecimal JavaDoc secs = ((DecimalValue)time.getComponent(Component.SECONDS)).getValue();
86         final int second = secs.intValue();
87         final int millisec = secs.multiply(BigDecimal.valueOf(1000)).intValue() % 1000;
88
89         calendar.set(Math.abs(year), month-1, day, hour, minute, second);
90         if (year < 0) {
91             calendar.set(Calendar.ERA, GregorianCalendar.BC);
92         }
93         calendar.set(Calendar.MILLISECOND, millisec);
94         calendar.set(Calendar.ZONE_OFFSET, zoneOffset);
95         calendar.set(Calendar.DST_OFFSET, 0);
96     }
97     /**
98     * Constructor: create a dateTime value from a supplied string, in
99     * ISO 8601 format
100     */

101
102     public DateTimeValue(CharSequence JavaDoc s) throws XPathException {
103         // input must have format yyyy-mm-ddThh:mm:ss[.fff*][([+|-]hh:mm | Z)]
104
zoneSpecified = false;
105         StringTokenizer tok = new StringTokenizer(trimWhitespace(s).toString(), "-:.+TZ", true);
106         try {
107             if (!tok.hasMoreElements()) badDate("too short");
108             String JavaDoc part = (String JavaDoc)tok.nextElement();
109             int era = +1;
110             if ("+".equals(part)) {
111                 part = (String JavaDoc)tok.nextElement();
112             } else if ("-".equals(part)) {
113                 era = -1;
114                 part = (String JavaDoc)tok.nextElement();
115             }
116             int year = Integer.parseInt(part) * era;
117             if (part.length() < 4) badDate("Year is less than four digits");
118             if (year==0) badDate("Year zero is not allowed");
119             if (!tok.hasMoreElements()) badDate("Too short");
120             if (!"-".equals(tok.nextElement())) badDate("Wrong delimiter after year");
121
122             if (!tok.hasMoreElements()) badDate("Too short");
123             part = (String JavaDoc)tok.nextElement();
124             int month = Integer.parseInt(part);
125             if (part.length() != 2) badDate("Month must be two digits");
126             if (month < 1 || month > 12) badDate("Month is out of range");
127             if (!tok.hasMoreElements()) badDate("Too short");
128             if (!"-".equals(tok.nextElement())) badDate("Wrong delimiter after month");
129
130             if (!tok.hasMoreElements()) badDate("Too short");
131             part = (String JavaDoc)tok.nextElement();
132             int day = Integer.parseInt(part);
133             if (part.length() != 2) badDate("Day must be two digits");
134             if (day < 1 || day > 31) badDate("Day is out of range");
135             if (!tok.hasMoreElements()) badDate("Too short");
136             if (!"T".equals(tok.nextElement())) badDate("Wrong delimiter after day");
137
138             if (!tok.hasMoreElements()) badDate("Too short");
139             part = (String JavaDoc)tok.nextElement();
140             int hour = Integer.parseInt(part);
141             if (part.length() != 2) badDate("Hour must be two digits");
142             if (hour > 24) badDate("Hour is out of range");
143             if (!tok.hasMoreElements()) badDate("Too short");
144             if (!":".equals(tok.nextElement())) badDate("Wrong delimiter after hour");
145
146             if (!tok.hasMoreElements()) badDate("Too short");
147             part = (String JavaDoc)tok.nextElement();
148             int minute = Integer.parseInt(part);
149             if (part.length() != 2) badDate("Minute must be two digits");
150             if (minute > 59) badDate("Minute is out of range");
151             if (hour == 24 && minute != 0) badDate("If hour is 24, minute must be 00");
152             if (!tok.hasMoreElements()) badDate("Too short");
153             if (!":".equals(tok.nextElement())) badDate("Wrong delimiter after minute");
154
155             if (!tok.hasMoreElements()) badDate("Too short");
156             part = (String JavaDoc)tok.nextElement();
157             int second = Integer.parseInt(part);
158             if (part.length() != 2) badDate("Second must be two digits");
159             if (second > 61) badDate("Second is out of range");
160             if (hour == 24 && second != 0) badDate("If hour is 24, second must be 00");
161
162             int millisecond = 0;
163             int tz = 0;
164
165             int state = 0;
166             while (tok.hasMoreElements()) {
167                 if (state==9) {
168                     badDate("Characters after the end");
169                 }
170                 String JavaDoc delim = (String JavaDoc)tok.nextElement();
171                 if (".".equals(delim)) {
172                     if (state != 0) {
173                         badDate("Decimal separator occurs twice");
174                     }
175                     part = (String JavaDoc)tok.nextElement();
176                     double fractionalSeconds = Double.parseDouble('.' + part);
177                     millisecond = (int)(Math.round(fractionalSeconds * 1000));
178                     if (hour == 24 && millisecond != 0) {
179                         badDate("If hour is 24, milliseconds must be 0");
180                     }
181                     state = 1;
182                 } else if ("Z".equals(delim)) {
183                     if (state > 1) {
184                         badDate("Z cannot occur here");
185                     }
186                     zoneSpecified = true;
187                     tz = 0;
188                     state = 9; // we've finished
189
} else if ("+".equals(delim) || "-".equals(delim)) {
190                     if (state > 1) {
191                         badDate(delim + " cannot occur here");
192                     }
193                     state = 2;
194                     zoneSpecified = true;
195                     if (!tok.hasMoreElements()) badDate("Missing timezone");
196                     part = (String JavaDoc)tok.nextElement();
197                     if (part.length() != 2) badDate("Timezone hour must be two digits");
198                     tz = Integer.parseInt(part) * 60;
199                     if (tz > 14*60) badDate("Timezone hour is out of range");
200                     if (tz > 12*60) badDate("Because of Java limitations, Saxon currently limits the timezone to +/- 12 hours");
201                     if ("-".equals(delim)) tz = -tz;
202
203                 } else if (":".equals(delim)) {
204                     if (state != 2) {
205                         badDate("Misplaced ':'");
206                     }
207                     state = 9;
208                     part = (String JavaDoc)tok.nextElement();
209                     int tzminute = Integer.parseInt(part);
210                     if (part.length() != 2) badDate("Timezone minute must be two digits");
211                     if (tzminute > 59) badDate("Timezone minute is out of range");
212                     if (tz<0) tzminute = -tzminute;
213                     tz += tzminute;
214                 } else {
215                     badDate("Timezone format is incorrect");
216                 }
217             }
218
219             if (state == 2 || state == 3) {
220                 badDate("Timezone incomplete");
221             }
222
223             boolean adjust = false;
224             if (hour == 24) {
225                 hour = 0;
226                 adjust = true;
227             }
228             // create a calendar that uses the timezone actually specified, or GMT otherwise
229
//calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
230
TimeZone zone = new SimpleTimeZone(tz*60000, "LLL");
231             calendar = new GregorianCalendar(zone);
232             calendar.setLenient(false);
233             calendar.set(Math.abs(year), month-1, day, hour, minute, second);
234             if (year < 0) {
235                 calendar.set(Calendar.ERA, GregorianCalendar.BC);
236             }
237             calendar.set(Calendar.MILLISECOND, millisecond);
238             calendar.set(Calendar.ZONE_OFFSET, tz*60000);
239             calendar.set(Calendar.DST_OFFSET, 0);
240
241             try {
242                 calendar.getTime();
243             } catch (IllegalArgumentException JavaDoc err) {
244                 badDate("Non-existent date");
245             }
246
247             if (adjust) {
248                 calendar.add(Calendar.DAY_OF_MONTH, 1);
249             }
250
251
252         } catch (NumberFormatException JavaDoc err) {
253             badDate("Non-numeric component");
254         }
255     }
256
257     private void badDate(String JavaDoc msg) throws XPathException {
258         DynamicError err = new DynamicError("Invalid dateTime value. " + msg);
259         err.setErrorCode("FORG0001");
260         throw err;
261     }
262
263
264     /**
265     * Get the UTC date/time corresponding to this dateTime. This normalizes
266     * the value to incorporate the timezone information, for example
267     * 2002-01-01T07:00:00-05:00 gives the same answer as 2002-01-01T12:00:00Z
268     */

269
270     public Date getUTCDate() {
271         // implement this as a memo function
272
if (UTCDate==null) {
273             UTCDate = calendar.getTime();
274         }
275         return UTCDate;
276     }
277
278     /**
279      * Get the Calendar object representing the value of this DateTime
280      */

281
282     public Calendar getCalendar() {
283         return calendar;
284     }
285
286     /**
287     * Convert to target data type
288     * @param requiredType an integer identifying the required atomic type
289     * @param conversion
290      * @return an AtomicValue, a value of the required type; or an ErrorValue
291     */

292
293     public AtomicValue convertPrimitive(BuiltInAtomicType requiredType, boolean validate, ConversionContext conversion) {
294         try {
295             switch(requiredType.getPrimitiveType()) {
296             case Type.DATE_TIME:
297             case Type.ATOMIC:
298             case Type.ITEM:
299                 return this;
300             case Type.DATE:
301                 String JavaDoc ds = getStringValue();
302                 int sep = ds.indexOf('T');
303                 if (zoneSpecified) {
304                     int z = ds.indexOf('Z', sep);
305                     if (z < 0) {
306                         z = ds.indexOf('+', sep);
307                     }
308                     if (z < 0) {
309                         z = ds.indexOf('-', sep);
310                     }
311                     if (z < 0) {
312                         // something's gone wrong
313
throw new IllegalArgumentException JavaDoc("Internal date formatting error " + ds);
314                     }
315                     return new DateValue(ds.substring(0, sep) + ds.substring(z));
316                 } else {
317                     return new DateValue(ds.substring(0, sep));
318                 }
319             case Type.TIME:
320                 ds = getStringValue();
321                 sep = ds.indexOf('T');
322                 return new TimeValue(ds.substring(sep+1));
323
324             case Type.G_YEAR:
325                 return(convertPrimitive(Type.DATE_TYPE, validate, conversion)
326                         .convertPrimitive(Type.G_YEAR_TYPE, validate, conversion));
327
328             case Type.G_YEAR_MONTH:
329                 return(convertPrimitive(Type.DATE_TYPE, validate, conversion)
330                         .convertPrimitive(Type.G_YEAR_MONTH_TYPE, validate, conversion));
331
332             case Type.G_MONTH:
333                 return(convertPrimitive(Type.DATE_TYPE, validate, conversion)
334                         .convertPrimitive(Type.G_MONTH_TYPE, validate, conversion));
335
336             case Type.G_MONTH_DAY:
337                 return(convertPrimitive(Type.DATE_TYPE, validate, conversion)
338                         .convertPrimitive(Type.G_MONTH_DAY_TYPE, validate, conversion));
339
340             case Type.G_DAY:
341                 return(convertPrimitive(Type.DATE_TYPE, validate, conversion)
342                         .convertPrimitive(Type.G_DAY_TYPE, validate, conversion));
343
344             case Type.STRING:
345                 return new StringValue(getStringValueCS());
346
347             case Type.UNTYPED_ATOMIC:
348                 return new UntypedAtomicValue(getStringValueCS());
349
350             default:
351                 ValidationException err = new ValidationException("Cannot convert dateTime to " +
352                                          requiredType.getDisplayName());
353                 //err.setXPathContext(context);
354
err.setErrorCode("FORG0001");
355                 return new ValidationErrorValue(err);
356             }
357         } catch (ValidationException e) {
358             return new ValidationErrorValue(e);
359         } catch (XPathException e) {
360             return new ValidationErrorValue(new ValidationException(e));
361         }
362     }
363
364     /**
365     * Convert to string
366     * @return ISO 8601 representation. The value returned is the localized representation,
367      * that is it uses the timezone contained within the value itself.
368     */

369
370     public String JavaDoc getStringValue() {
371
372         FastStringBuffer sb = new FastStringBuffer(30);
373         int era = calendar.get(GregorianCalendar.ERA);
374         int year = calendar.get(Calendar.YEAR);
375         if (era == GregorianCalendar.BC) {
376             sb.append('-');
377         }
378         if (year<0) {
379             sb.append('-');
380             year = -year;
381         }
382         appendString(sb, year, (year>9999 ? (calendar.get(Calendar.YEAR)+"").length() : 4));
383         sb.append('-');
384         appendString(sb, calendar.get(Calendar.MONTH)+1, 2);
385         sb.append('-');
386         appendString(sb, calendar.get(Calendar.DATE), 2);
387         sb.append('T');
388         appendString(sb, calendar.get(Calendar.HOUR_OF_DAY), 2);
389         sb.append(':');
390         appendString(sb, calendar.get(Calendar.MINUTE), 2);
391         sb.append(':');
392         appendSeconds(calendar, sb);
393
394         if (zoneSpecified) {
395             appendTimezone(calendar, sb);
396         }
397
398         return sb.toString();
399
400     }
401
402     static void appendString(FastStringBuffer sb, int value, int size) {
403         String JavaDoc s = "000000"+value;
404         sb.append( s.substring(s.length()-size) );
405     }
406
407     static void appendSeconds(Calendar calendar, FastStringBuffer sb) {
408         appendString(sb, calendar.get(Calendar.SECOND), 2);
409         int millis = calendar.get(Calendar.MILLISECOND);
410         if (millis != 0) {
411             sb.append('.');
412             String JavaDoc m = calendar.get(Calendar.MILLISECOND) + "";
413             while (m.length() < 3) m = '0' + m;
414             while (m.endsWith("0")) m = m.substring(0, m.length()-1);
415             sb.append(m);
416         }
417         return;
418     }
419
420     /**
421      * Add a string representation of the timezone, typically
422      * formatted as "Z" or "+03:00" or "-10:00", to the supplied
423      * string buffer
424      * @param calendar The Calendar whose timezone value is required
425      * @param sb The StringBuffer that will be updated with the resulting string
426      * representation
427      */

428
429     public static void appendTimezone(Calendar calendar, FastStringBuffer sb) {
430         int timeZoneOffset = (calendar.get(Calendar.ZONE_OFFSET) +
431                               calendar.get(Calendar.DST_OFFSET)) / 60000;
432         appendTimezone(timeZoneOffset, sb);
433     }
434
435      /**
436      * Add a string representation of the timezone, typically
437      * formatted as "Z" or "+03:00" or "-10:00", to the supplied
438      * string buffer
439      * @param timeZoneOffset The timezone offset in minutes
440      * @param sb The StringBuffer that will be updated with the resulting string
441      * representation
442      */

443
444     static void appendTimezone(int timeZoneOffset, FastStringBuffer sb) {
445         if (timeZoneOffset == 0) {
446             sb.append('Z');
447         } else {
448             sb.append((timeZoneOffset<0 ? "-" : "+"));
449             int tzo = timeZoneOffset;
450             if (tzo < 0) tzo = -tzo;
451             int tzhours = tzo / 60;
452             appendString(sb, tzhours, 2);
453             sb.append(':');
454             int tzminutes = tzo % 60;
455             appendString(sb, tzminutes, 2);
456         }
457         return;
458     }
459
460     /**
461     * Determine the data type of the exprssion
462     * @return Type.DATE_TIME,
463     */

464
465     public ItemType getItemType() {
466         return Type.DATE_TIME_TYPE;
467     }
468
469     /**
470      * Return a dateTime with the same localized value, but
471      * without the timezone component
472      * @return the result of removing the timezone
473      */

474
475     public CalendarValue removeTimezone() {
476         return new DateTimeValue(calendar, false);
477     }
478
479     /**
480      * Return a date, time, or dateTime with the same normalized value, but
481      * in a different timezone
482      * @return the date/time in the new timezone
483      * @throws XPathException
484      */

485
486     public CalendarValue setTimezone(SecondsDurationValue tz) throws XPathException {
487         if (zoneSpecified) {
488             SimpleTimeZone stz = new SimpleTimeZone((int)(tz.getLengthInSeconds()*1000), "xxz");
489             GregorianCalendar cal = new GregorianCalendar(stz);
490             cal.setTime(getUTCDate());
491             return new DateTimeValue(cal, true);
492         } else {
493             FastStringBuffer sb = new FastStringBuffer(10);
494             sb.append(getStringValue());
495             appendTimezone((int)(tz.getLengthInSeconds()/60.0), sb);
496             return new DateTimeValue(sb);
497         }
498     }
499
500     /**
501      * Add a duration to a dateTime
502      * @param duration the duration to be added (may be negative)
503      * @return the new date
504      * @throws XPathException if the duration is an xs:duration, as distinct from
505      * a subclass thereof
506      */

507
508     public CalendarValue add(DurationValue duration) throws XPathException {
509         if (duration instanceof SecondsDurationValue) {
510             double seconds = duration.getLengthInSeconds();
511             GregorianCalendar cal2 = (GregorianCalendar)calendar.clone();
512             cal2.add(Calendar.SECOND, (int)seconds);
513             cal2.add(Calendar.MILLISECOND, (int)((seconds % 1) * 1000));
514             return new DateTimeValue(cal2, zoneSpecified);
515         } else if (duration instanceof MonthDurationValue) {
516             int months = ((MonthDurationValue)duration).getLengthInMonths();
517             GregorianCalendar cal2 = (GregorianCalendar)calendar.clone();
518             cal2.add(Calendar.MONTH, months);
519             return new DateTimeValue(cal2, zoneSpecified);
520         } else {
521             DynamicError err = new DynamicError(
522                     "DateTime arithmetic is not supported on xs:duration, only on its subtypes");
523             err.setIsTypeError(true);
524             throw err;
525         }
526     }
527
528   /**
529      * Determine the difference between two points in time, as a duration
530      * @param other the other point in time
531      * @param context
532    * @return the duration as an xdt:dayTimeDuration
533      * @throws XPathException for example if one value is a date and the other is a time
534      */

535
536     public SecondsDurationValue subtract(CalendarValue other, ConversionContext context) throws XPathException {
537         if (!(other instanceof DateTimeValue)) {
538             DynamicError err = new DynamicError(
539                     "First operand of '-' is a dateTime, but the second is not");
540             err.setIsTypeError(true);
541             throw err;
542         }
543         return super.subtract(other, context);
544     }
545
546     /**
547     * Convert to Java object (for passing to external functions)
548     */

549
550     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
551         if (target.isAssignableFrom(Date.class)) {
552             return getUTCDate();
553         } else if (target.isAssignableFrom(DateTimeValue.class)) {
554             return this;
555         } else if (target==String JavaDoc.class || target==CharSequence JavaDoc.class) {
556             return getStringValue();
557         } else if (target==Object JavaDoc.class) {
558             return getStringValue();
559         } else {
560             Object JavaDoc o = super.convertToJava(target, context);
561             if (o == null) {
562                 throw new DynamicError("Conversion of dateTime to " + target.getName() +
563                         " is not supported");
564             }
565             return o;
566         }
567     }
568
569     /**
570     * Get a component of the value. Returns null if the timezone component is
571     * requested and is not present.
572     */

573
574     public AtomicValue getComponent(int component) throws XPathException {
575         switch (component) {
576         case Component.YEAR:
577             return new IntegerValue(calendar.get(Calendar.YEAR));
578         case Component.MONTH:
579             return new IntegerValue(calendar.get(Calendar.MONTH) + 1);
580         case Component.DAY:
581             return new IntegerValue(calendar.get(Calendar.DATE));
582         case Component.HOURS:
583             return new IntegerValue(calendar.get(Calendar.HOUR_OF_DAY));
584         case Component.MINUTES:
585             return new IntegerValue(calendar.get(Calendar.MINUTE));
586         case Component.SECONDS:
587             FastStringBuffer sb = new FastStringBuffer(10);
588             appendSeconds(calendar, sb);
589             return DecimalValue.makeDecimalValue(sb, false);
590         case Component.TIMEZONE:
591             if (zoneSpecified) {
592                 int tzmsecs = (calendar.get(Calendar.ZONE_OFFSET) +
593                               calendar.get(Calendar.DST_OFFSET));
594                 return SecondsDurationValue.fromMilliseconds(tzmsecs);
595             } else {
596                 return null;
597             }
598         default:
599             throw new IllegalArgumentException JavaDoc("Unknown component for dateTime: " + component);
600         }
601     }
602
603
604     /**
605     * Compare the value to another dateTime value.
606      * <p>
607      * This method is not used for XPath comparisons because it does not have access to the implicitTimezone
608      * from the dynamic context. It is available for schema comparisons, although it does not currently
609      * implement the XML Schema semantics for timezone comparison (which involve partial ordering)
610     * @param other The other dateTime value
611     * @return negative value if this one is the earler, 0 if they are chronologically equal,
612     * positive value if this one is the later. For this purpose, dateTime values with an unknown
613     * timezone are considered to be values in the implicit timezone (the Comparable interface requires
614     * a total ordering).
615     * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
616     * is declared as Object to satisfy the Comparable interface)
617     */

618
619     public int compareTo(Object JavaDoc other) {
620         // TODO: implement the XML Schema comparison semantics
621
if (!(other instanceof DateTimeValue)) {
622             throw new ClassCastException JavaDoc("DateTime values are not comparable to " + other.getClass());
623         }
624         DateTimeValue v1 = this;
625         DateTimeValue v2 = (DateTimeValue)other;
626         if (v1.zoneSpecified == v2.zoneSpecified) {
627             // both have a timezone, or neither has a timezone
628
return getUTCDate().compareTo(((DateTimeValue)other).getUTCDate());
629         } else {
630             // one has a timezone and the other doesn't
631
try {
632                 GregorianCalendar cal = new GregorianCalendar();
633                 int tz = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 1000;
634                 SecondsDurationValue tzd = SecondsDurationValue.fromSeconds(tz);
635                 if (!v1.zoneSpecified) {
636                     v1 = (DateTimeValue)v1.setTimezone(tzd);
637                 }
638                 if (!v2.zoneSpecified) {
639                     v2 = (DateTimeValue)v2.setTimezone(tzd);
640                 }
641                 return v1.getUTCDate().compareTo(v2.getUTCDate());
642             } catch (XPathException e) {
643                 throw new AssertionError JavaDoc("Java timezone is out of range");
644             }
645         }
646     }
647
648     /**
649     * Compare the value to another dateTime value
650     * @param other The other dateTime value
651     * @param conversion A ConversionContext used to supply the implicit timezone
652     * @return negative value if this one is the earler, 0 if they are chronologically equal,
653     * positive value if this one is the later. For this purpose, dateTime values with an unknown
654     * timezone are considered to be values in the implicit timezone (the Comparable interface requires
655     * a total ordering).
656     * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
657     * is declared as Object to satisfy the Comparable interface)
658     */

659
660     public int compareTo(CalendarValue other, ConversionContext conversion) {
661         if (!(other instanceof DateTimeValue)) {
662             throw new ClassCastException JavaDoc("DateTime values are not comparable to " + other.getClass());
663         }
664         DateTimeValue v1 = this;
665         DateTimeValue v2 = (DateTimeValue)other;
666         if (v1.zoneSpecified == v2.zoneSpecified) {
667             // both have a timezone, or neither has a timezone
668
return getUTCDate().compareTo(((DateTimeValue)other).getUTCDate());
669         } else {
670             // one has a timezone and the other doesn't
671
try {
672                 int tz = conversion.getImplicitTimezone() * 60000;
673                 SecondsDurationValue tzd = SecondsDurationValue.fromMilliseconds(tz);
674                 if (!v1.zoneSpecified) {
675                     v1 = (DateTimeValue)v1.setTimezone(tzd);
676                 }
677                 if (!v2.zoneSpecified) {
678                     v2 = (DateTimeValue)v2.setTimezone(tzd);
679                 }
680                 return v1.getUTCDate().compareTo(v2.getUTCDate());
681             } catch (XPathException e) {
682                 throw new AssertionError JavaDoc("Java timezone is out of range");
683             }
684         }
685     }
686
687
688
689     public boolean equals(Object JavaDoc other) {
690         return compareTo(other) == 0;
691     }
692
693     public int hashCode() {
694         if (zoneSpecified) {
695             return getUTCDate().hashCode();
696         } else {
697             try {
698                 GregorianCalendar cal = new GregorianCalendar();
699                 int tz = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 60000;
700                 SecondsDurationValue tzd = SecondsDurationValue.fromSeconds(tz);
701                 DateTimeValue v1 = (DateTimeValue)setTimezone(tzd);
702                 return v1.getUTCDate().hashCode();
703             } catch (XPathException e) {
704                 throw new AssertionError JavaDoc("Java timezone is out of range");
705             }
706         }
707     }
708
709 }
710
711 //
712
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
713
// you may not use this file except in compliance with the License. You may obtain a copy of the
714
// License at http://www.mozilla.org/MPL/
715
//
716
// Software distributed under the License is distributed on an "AS IS" basis,
717
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
718
// See the License for the specific language governing rights and limitations under the License.
719
//
720
// The Original Code is: all this file.
721
//
722
// The Initial Developer of the Original Code is Michael H. Kay
723
//
724
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
725
//
726
// Contributor(s): none.
727
//
728

729
Popular Tags