001: package net.sf.saxon.value;
002:
003: import net.sf.saxon.expr.XPathContext;
004: import net.sf.saxon.om.FastStringBuffer;
005: import net.sf.saxon.trans.XPathException;
006:
007: import java.math.BigDecimal;
008: import java.util.GregorianCalendar;
009:
010: /**
011: * Abstract superclass for Date, Time, and DateTime.
012: */
013:
014: public abstract class CalendarValue extends AtomicValue implements
015: Comparable {
016:
017: // This is a reimplementation that makes no use of the Java Calendar/Date types except for computations.
018:
019: private int tzMinutes = NO_TIMEZONE; // timezone offset in minutes: or the special value NO_TIMEZONE
020: public static final int NO_TIMEZONE = 9999999;
021:
022: /**
023: * Determine whether this value includes a timezone
024: * @return true if there is a timezone in the value, false if not
025: */
026:
027: public final boolean hasTimezone() {
028: return tzMinutes != NO_TIMEZONE;
029: }
030:
031: /**
032: * Modify the timezone value held in this object. This must be done only while the value is being
033: * constructed.
034: * @param minutes The timezone offset from GMT in minutes, positive or negative; or the special
035: * value NO_TIMEZONE indicating that the value is not in a timezone (this is the default)
036: */
037:
038: public final void setTimezoneInMinutes(int minutes) {
039: tzMinutes = minutes;
040: }
041:
042: /**
043: * Convert the value to a DateTime, retaining all the components that are actually present, and
044: * substituting conventional values for components that are missing
045: */
046:
047: public abstract DateTimeValue toDateTime();
048:
049: /**
050: * Get the timezone value held in this object.
051: * @return The timezone offset from GMT in minutes, positive or negative; or the special
052: * value NO_TIMEZONE indicating that the value is not in a timezone
053: */
054:
055: public final int getTimezoneInMinutes() {
056: return tzMinutes;
057: }
058:
059: /**
060: * Convert the value to a string
061: */
062:
063: public final String getStringValue() {
064: return getStringValueCS().toString();
065: }
066:
067: /**
068: * Get a Java Calendar object that represents this date/time value. The Calendar
069: * object will be newly created for the purpose
070: * @return A Calendar object representing the date and time. Note that Java can only
071: * represent the time to millisecond precision, and that it does not support the full
072: * range of timezones required by XPath (-14:00 to +14:00)
073: */
074:
075: public abstract GregorianCalendar getCalendar();
076:
077: /**
078: * Add a duration to this date/time value
079: * @param duration the duration to be added (which might be negative)
080: * @return a new date/time value representing the result of adding the duration. The original
081: * object is not modified.
082: * @throws XPathException
083: */
084:
085: public abstract CalendarValue add(DurationValue duration)
086: throws XPathException;
087:
088: /**
089: * Determine the difference between two points in time, as a duration
090: * @param other the other point in time
091: * @param context the dynamic context, used to obtain timezone information. May be set to null
092: * only if both values contain an explicit timezone, or if neither does so.
093: * @return the duration as an xdt:dayTimeDuration
094: * @throws net.sf.saxon.trans.XPathException for example if one value is a date and the other is a time
095: */
096:
097: public SecondsDurationValue subtract(CalendarValue other,
098: XPathContext context) throws XPathException {
099: DateTimeValue dt1 = this .toDateTime();
100: DateTimeValue dt2 = other.toDateTime();
101: if (dt1.getTimezoneInMinutes() != dt2.getTimezoneInMinutes()) {
102: dt1 = dt1.normalize(context);
103: dt2 = dt2.normalize(context);
104: }
105: BigDecimal d1 = dt1.toJulianInstant();
106: BigDecimal d2 = dt2.toJulianInstant();
107: BigDecimal difference = d1.subtract(d2);
108: return SecondsDurationValue.fromSeconds(difference);
109: }
110:
111: /**
112: * Return a date, time, or dateTime with the same localized value, but
113: * without the timezone component
114: * @return the result of removing the timezone
115: */
116:
117: public final CalendarValue removeTimezone() {
118: CalendarValue c = copy();
119: c.tzMinutes = NO_TIMEZONE;
120: return c;
121: }
122:
123: /**
124: * Return a new date, time, or dateTime with the same normalized value, but
125: * in a different timezone
126: * @param tz the new timezone, in minutes
127: * @return the date/time in the new timezone
128: */
129:
130: public abstract CalendarValue adjustTimezone(int tz);
131:
132: /**
133: * Make a copy of this date, time, or dateTime value
134: */
135:
136: public abstract CalendarValue copy();
137:
138: /**
139: * Compare this value to another value of the same type, using the supplied ConversionContext
140: * to get the implicit timezone if required.
141: */
142:
143: public abstract int compareTo(CalendarValue other,
144: XPathContext context);
145:
146: /**
147: * Add a string representation of the timezone, typically
148: * formatted as "Z" or "+03:00" or "-10:00", to a supplied
149: * string buffer
150: * @param sb The StringBuffer that will be updated with the resulting string
151: * representation
152: */
153:
154: public final void appendTimezone(FastStringBuffer sb) {
155: if (hasTimezone()) {
156: appendTimezone(getTimezoneInMinutes(), sb);
157: }
158: }
159:
160: public static final void appendTimezone(int tz, FastStringBuffer sb) {
161: if (tz == 0) {
162: sb.append("Z");
163: } else {
164: sb.append(tz > 0 ? "+" : "-");
165: tz = Math.abs(tz);
166: appendTwoDigits(sb, tz / 60);
167: sb.append(':');
168: appendTwoDigits(sb, tz % 60);
169: }
170: }
171:
172: /**
173: * Append an integer, formatted with leading zeros to a fixed size, to a string buffer
174: * @param sb the string buffer
175: * @param value the integer to be formatted
176: * @param size the number of digits required (max 9)
177: */
178:
179: static void appendString(FastStringBuffer sb, int value, int size) {
180: String s = "000000000" + value;
181: sb.append(s.substring(s.length() - size));
182: }
183:
184: /**
185: * Append an integer, formatted as two digits, to a string buffer
186: * @param sb the string buffer
187: * @param value the integer to be formatted (must be in the range 0..99
188: */
189:
190: static void appendTwoDigits(FastStringBuffer sb, int value) {
191: sb.append((char) (value / 10 + '0'));
192: sb.append((char) (value % 10 + '0'));
193: }
194: }
195:
196: //
197: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
198: // you may not use this file except in compliance with the License. You may obtain a copy of the
199: // License at http://www.mozilla.org/MPL/
200: //
201: // Software distributed under the License is distributed on an "AS IS" basis,
202: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
203: // See the License for the specific language governing rights and limitations under the License.
204: //
205: // The Original Code is: all this file.
206: //
207: // The Initial Developer of the Original Code is Michael H. Kay
208: //
209: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
210: //
211: // Contributor(s): none.
212: //
|