001: //jTDS JDBC Driver for Microsoft SQL Server and Sybase
002: //Copyright (C) 2004 The jTDS Project
003: //
004: //This library is free software; you can redistribute it and/or
005: //modify it under the terms of the GNU Lesser General Public
006: //License as published by the Free Software Foundation; either
007: //version 2.1 of the License, or (at your option) any later version.
008: //
009: //This library is distributed in the hope that it will be useful,
010: //but WITHOUT ANY WARRANTY; without even the implied warranty of
011: //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: //Lesser General Public License for more details.
013: //
014: //You should have received a copy of the GNU Lesser General Public
015: //License along with this library; if not, write to the Free Software
016: //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: //
018: package net.sourceforge.jtds.jdbc;
019:
020: import java.sql.Date;
021: import java.sql.SQLException;
022: import java.sql.Time;
023: import java.sql.Timestamp;
024: import java.util.Calendar;
025: import java.util.GregorianCalendar;
026:
027: /**
028: * Encapsulates Sybase date/time values and provides conversions to and from
029: * Java classes.
030: *
031: * @author Mike Hutchinson
032: * @version $Id: DateTime.java,v 1.4 2005/07/11 13:33:30 alin_sinpalean Exp $
033: */
034: public class DateTime {
035: /** Per thread instance of Calendar used for conversions. */
036: private static ThreadLocal calendar = new ThreadLocal() {
037: protected synchronized Object initialValue() {
038: return new GregorianCalendar();
039: }
040: };
041: /** Indicates date value not used. */
042: static final int DATE_NOT_USED = Integer.MIN_VALUE;
043: /** Indicates time value not used. */
044: static final int TIME_NOT_USED = Integer.MIN_VALUE;
045: /** The date component of the server datetime value. */
046: private int date;
047: /** The time component of the server datetime value. */
048: private int time;
049: /** Unpacked year value. */
050: private short year;
051: /** Unpacked month value. */
052: private short month;
053: /** Unpacked day value. */
054: private short day;
055: /** Unpacked hour value. */
056: private short hour;
057: /** Unpacked minute value. */
058: private short minute;
059: /** Unpacked second value. */
060: private short second;
061: /** Unpacked millisecond value. */
062: private short millis;
063: /** Indicates server datetime values have been unpacked. */
064: private boolean unpacked;
065: /** Cached value of the datetime as a <code>String</code>. */
066: private String stringValue;
067: /** Cached value of the datetime as a <code>java.sql.Timestamp</code>. */
068: private Timestamp tsValue;
069: /** Cached value of the datetime as a <code>java.sql.Date</code>. */
070: private Date dateValue;
071: /** Cached value of the datetime as a <code>java.sql.Time</code>. */
072: private Time timeValue;
073:
074: /**
075: * Constructs a DateTime object from the two integer components of a
076: * datetime.
077: *
078: * @param date server date field
079: * @param time server time field
080: */
081: DateTime(int date, int time) {
082: this .date = date;
083: this .time = time;
084: }
085:
086: /**
087: * Constructs a DateTime object from the two short components of a
088: * smalldatetime.
089: *
090: * @param date server date field
091: * @param time server time field
092: */
093: DateTime(short date, short time) {
094: this .date = (int) date & 0xFFFF;
095: this .time = (int) time * 60 * 300;
096: }
097:
098: /**
099: * Constructs a DateTime object from a <code>java.sql.Timestamp</code>.
100: *
101: * @param ts <code>Timestamp</code> object representing the datetime
102: * @throws SQLException if the date is out of range
103: */
104: DateTime(Timestamp ts) throws SQLException {
105: tsValue = ts;
106: GregorianCalendar cal = (GregorianCalendar) calendar.get();
107: cal.setTime((java.util.Date) ts);
108:
109: if (!Driver.JDBC3) {
110: // Not Running under 1.4 so need to add milliseconds
111: cal.set(Calendar.MILLISECOND,
112: ((Timestamp) ts).getNanos() / 1000000);
113: }
114: this .year = (short) cal.get(Calendar.YEAR);
115: this .month = (short) (cal.get(Calendar.MONTH) + 1);
116: this .day = (short) cal.get(Calendar.DAY_OF_MONTH);
117: this .hour = (short) cal.get(Calendar.HOUR_OF_DAY);
118: this .minute = (short) cal.get(Calendar.MINUTE);
119: this .second = (short) cal.get(Calendar.SECOND);
120: this .millis = (short) cal.get(Calendar.MILLISECOND);
121: packDate();
122: packTime();
123: unpacked = true;
124: }
125:
126: /**
127: * Constructs a DateTime object from a <code>java.sql.Time</code>.
128: *
129: * @param t <code>Time</code> object representing the datetime
130: */
131: DateTime(Time t) {
132: timeValue = t;
133: GregorianCalendar cal = (GregorianCalendar) calendar.get();
134: cal.setTime((java.util.Date) t);
135: this .date = DATE_NOT_USED;
136: this .year = 1900;
137: this .month = 1;
138: this .day = 1;
139: this .hour = (short) cal.get(Calendar.HOUR_OF_DAY);
140: this .minute = (short) cal.get(Calendar.MINUTE);
141: this .second = (short) cal.get(Calendar.SECOND);
142: this .millis = (short) cal.get(Calendar.MILLISECOND);
143: packTime();
144: this .year = 1970;
145: this .month = 1;
146: this .day = 1;
147: unpacked = true;
148: }
149:
150: /**
151: * Constructs a DateTime object from a <code>java.sql.Date</code>.
152: *
153: * @param d <code>Date</code> object representing the datetime
154: * @throws SQLException if the Date is out of range
155: */
156: DateTime(Date d) throws SQLException {
157: dateValue = d;
158: GregorianCalendar cal = (GregorianCalendar) calendar.get();
159: cal.setTime((java.util.Date) d);
160: this .year = (short) cal.get(Calendar.YEAR);
161: this .month = (short) (cal.get(Calendar.MONTH) + 1);
162: this .day = (short) cal.get(Calendar.DAY_OF_MONTH);
163: this .hour = 0;
164: this .minute = 0;
165: this .second = 0;
166: this .millis = 0;
167: packDate();
168: this .time = TIME_NOT_USED;
169: unpacked = true;
170: }
171:
172: /**
173: * Retrieves the date component of a datetime value.
174: *
175: * @return the datetime date component as an <code>int</code>
176: */
177: int getDate() {
178: return (date == DATE_NOT_USED) ? 0 : date;
179: }
180:
181: /**
182: * Retrieves the time component of a datetime value.
183: *
184: * @return the datetime time component as an <code>int</code>
185: */
186: int getTime() {
187: return (time == TIME_NOT_USED) ? 0 : time;
188: }
189:
190: /**
191: * Converts a Julian datetime from the Sybase epoch of 1900-01-01 to the
192: * equivalent unpacked year/month/day etc.
193: *
194: * Algorithm from Fliegel, H F and van Flandern, T C (1968).
195: * Communications of the ACM, Vol 11, No 10 (October, 1968).
196: * <pre>
197: * SUBROUTINE GDATE (JD, YEAR,MONTH,DAY)
198: * C
199: * C---COMPUTES THE GREGORIAN CALENDAR DATE (YEAR,MONTH,DAY)
200: * C GIVEN THE JULIAN DATE (JD).
201: * C
202: * INTEGER JD,YEAR,MONTH,DAY,I,J,K
203: * C
204: * L= JD+68569
205: * N= 4*L/146097
206: * L= L-(146097*N+3)/4
207: * I= 4000*(L+1)/1461001
208: * L= L-1461*I/4+31
209: * J= 80*L/2447
210: * K= L-2447*J/80
211: * L= J/11
212: * J= J+2-12*L
213: * I= 100*(N-49)+I+L
214: * C
215: * YEAR= I
216: * MONTH= J
217: * DAY= K
218: * C
219: * RETURN
220: * END
221: * </pre>
222: */
223: private void unpackDateTime() {
224: if (this .date == DATE_NOT_USED) {
225: this .year = 1970;
226: this .month = 1;
227: this .day = 1;
228: } else {
229: if (date == 0) {
230: // Optimize common case of 1900-01-01 which is used as
231: // the default date for datetimes where only the time is set.
232: this .year = 1900;
233: this .month = 1;
234: this .day = 1;
235: } else {
236: int l = date + 68569 + 2415021;
237: int n = 4 * l / 146097;
238: l = l - (146097 * n + 3) / 4;
239: int i = 4000 * (l + 1) / 1461001;
240: l = l - 1461 * i / 4 + 31;
241: int j = 80 * l / 2447;
242: int k = l - 2447 * j / 80;
243: l = j / 11;
244: j = j + 2 - 12 * l;
245: i = 100 * (n - 49) + i + l;
246: this .year = (short) i;
247: this .month = (short) j;
248: this .day = (short) k;
249: }
250: }
251: if (time == TIME_NOT_USED) {
252: this .hour = 0;
253: this .minute = 0;
254: this .second = 0;
255: } else {
256: int hours = time / 1080000;
257: time = time - hours * 1080000;
258: int minutes = time / 18000;
259: time = time - (minutes * 18000);
260: int seconds = time / 300;
261: time = time - seconds * 300;
262: time = (int) Math.round(time * 1000 / 300f);
263: this .hour = (short) hours;
264: this .minute = (short) minutes;
265: this .second = (short) seconds;
266: this .millis = (short) time;
267: }
268: unpacked = true;
269: }
270:
271: /**
272: * Converts a calendar date into days since 1900 (Sybase epoch).
273: * <p>
274: * Algorithm from Fliegel, H F and van Flandern, T C (1968).
275: * Communications of the ACM, Vol 11, No 10 (October, 1968).
276: *
277: * <pre>
278: * INTEGER FUNCTION JD (YEAR,MONTH,DAY)
279: * C
280: * C---COMPUTES THE JULIAN DATE (JD) GIVEN A GREGORIAN CALENDAR
281: * C DATE (YEAR,MONTH,DAY).
282: * C
283: * INTEGER YEAR,MONTH,DAY,I,J,K
284: * C
285: * I= YEAR
286: * J= MONTH
287: * K= DAY
288: * C
289: * JD= K-32075+1461*(I+4800+(J-14)/12)/4+367*(J-2-(J-14)/12*12)
290: * 2 /12-3*((I+4900+(J-14)/12)/100)/4
291: * C
292: * RETURN
293: * END
294: * </pre>
295: *
296: * @throws java.sql.SQLException if the date is outside the accepted range, 1753-9999
297: */
298: public void packDate() throws SQLException {
299: if (year < 1753 || year > 9999) {
300: throw new SQLException(
301: Messages.get("error.datetime.range"), "22003");
302: }
303: date = day - 32075 + 1461 * (year + 4800 + (month - 14) / 12)
304: / 4 + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
305: - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
306: - 2415021;
307: }
308:
309: /**
310: * Converts separate time components into a datetime time value.
311: */
312: public void packTime() {
313: time = hour * 1080000;
314: time += minute * 18000;
315: time += second * 300;
316: time += Math.round(millis * 300f / 1000);
317: if (time > 25919999) {
318: // Time field has overflowed need to increment days
319: // Sybase does not allow invalid time component
320: time = 0;
321: hour = 0;
322: minute = 0;
323: second = 0;
324: millis = 0;
325: if (date != DATE_NOT_USED) {
326: GregorianCalendar cal = (GregorianCalendar) calendar
327: .get();
328: cal.set(Calendar.YEAR, this .year);
329: cal.set(Calendar.MONTH, this .month - 1);
330: cal.set(Calendar.DAY_OF_MONTH, this .day);
331: cal.add(Calendar.DATE, 1);
332: year = (short) cal.get(Calendar.YEAR);
333: month = (short) (cal.get(Calendar.MONTH) + 1);
334: day = (short) cal.get(Calendar.DAY_OF_MONTH);
335: date++;
336: }
337: }
338: }
339:
340: /**
341: * Retrieves the current datetime value as a Timestamp.
342: *
343: * @return the current datetime value as a <code>java.sql.Timestamp</code>
344: */
345: public Timestamp toTimestamp() {
346: if (tsValue == null) {
347: if (!unpacked) {
348: unpackDateTime();
349: }
350: GregorianCalendar cal = (GregorianCalendar) calendar.get();
351: cal.set(Calendar.YEAR, this .year);
352: cal.set(Calendar.MONTH, this .month - 1);
353: cal.set(Calendar.DAY_OF_MONTH, this .day);
354: cal.set(Calendar.HOUR_OF_DAY, this .hour);
355: cal.set(Calendar.MINUTE, this .minute);
356: cal.set(Calendar.SECOND, this .second);
357: cal.set(Calendar.MILLISECOND, this .millis);
358: tsValue = new Timestamp(cal.getTime().getTime());
359: }
360: return tsValue;
361: }
362:
363: /**
364: * Retrieves the current datetime value as a Date.
365: *
366: * @return the current datetime value as a <code>java.sql.Date</code>
367: */
368: public Date toDate() {
369: if (dateValue == null) {
370: if (!unpacked) {
371: unpackDateTime();
372: }
373: GregorianCalendar cal = (GregorianCalendar) calendar.get();
374: cal.set(Calendar.YEAR, this .year);
375: cal.set(Calendar.MONTH, this .month - 1);
376: cal.set(Calendar.DAY_OF_MONTH, this .day);
377: cal.set(Calendar.HOUR_OF_DAY, 0);
378: cal.set(Calendar.MINUTE, 0);
379: cal.set(Calendar.SECOND, 0);
380: cal.set(Calendar.MILLISECOND, 0);
381: dateValue = new Date(cal.getTime().getTime());
382: }
383: return dateValue;
384: }
385:
386: /**
387: * Retrieves the current datetime value as a Time.
388: *
389: * @return the current datetime value as a <code>java.sql.Time</code>
390: */
391: public Time toTime() {
392: if (timeValue == null) {
393: if (!unpacked) {
394: unpackDateTime();
395: }
396: GregorianCalendar cal = (GregorianCalendar) calendar.get();
397: cal.set(Calendar.YEAR, 1970);
398: cal.set(Calendar.MONTH, 0);
399: cal.set(Calendar.DAY_OF_MONTH, 1);
400: cal.set(Calendar.HOUR_OF_DAY, this .hour);
401: cal.set(Calendar.MINUTE, this .minute);
402: cal.set(Calendar.SECOND, this .second);
403: cal.set(Calendar.MILLISECOND, this .millis);
404: timeValue = new Time(cal.getTime().getTime());
405: }
406: return timeValue;
407: }
408:
409: /**
410: * Retrieves the current datetime value as a Time, Date or Timestamp.
411: *
412: * @return the current datetime value as an <code>java.lang.Object</code>
413: */
414: public Object toObject() {
415: if (date == DATE_NOT_USED) {
416: return toTime();
417: }
418: if (time == TIME_NOT_USED) {
419: return toDate();
420: }
421: return toTimestamp();
422: }
423:
424: /**
425: * Retrieves the current datetime value as a String.
426: *
427: * @return the current datetime value as a <code>String</code>
428: */
429: public String toString() {
430: if (stringValue == null) {
431: if (!unpacked) {
432: unpackDateTime();
433: }
434: //
435: // Make local copies to avoid corrupting unpacked
436: // components.
437: //
438: int day = this .day;
439: int month = this .month;
440: int year = this .year;
441: int millis = this .millis;
442: int second = this .second;
443: int minute = this .minute;
444: int hour = this .hour;
445: char buf[] = new char[23];
446: int p = 0;
447: if (date != DATE_NOT_USED) {
448: p = 10;
449: buf[--p] = (char) ('0' + day % 10);
450: day /= 10;
451: buf[--p] = (char) ('0' + day % 10);
452: buf[--p] = '-';
453: buf[--p] = (char) ('0' + month % 10);
454: month /= 10;
455: buf[--p] = (char) ('0' + month % 10);
456: buf[--p] = '-';
457: buf[--p] = (char) ('0' + year % 10);
458: year /= 10;
459: buf[--p] = (char) ('0' + year % 10);
460: year /= 10;
461: buf[--p] = (char) ('0' + year % 10);
462: year /= 10;
463: buf[--p] = (char) ('0' + year % 10);
464: p += 10;
465: if (time != TIME_NOT_USED) {
466: buf[p++] = ' ';
467: }
468: }
469: if (time != TIME_NOT_USED) {
470: p += 12;
471: buf[--p] = (char) ('0' + millis % 10);
472: millis /= 10;
473: buf[--p] = (char) ('0' + millis % 10);
474: millis /= 10;
475: buf[--p] = (char) ('0' + millis % 10);
476: buf[--p] = '.';
477: buf[--p] = (char) ('0' + second % 10);
478: second /= 10;
479: buf[--p] = (char) ('0' + second % 10);
480: buf[--p] = ':';
481: buf[--p] = (char) ('0' + minute % 10);
482: minute /= 10;
483: buf[--p] = (char) ('0' + minute % 10);
484: buf[--p] = ':';
485: buf[--p] = (char) ('0' + hour % 10);
486: hour /= 10;
487: buf[--p] = (char) ('0' + hour % 10);
488: p += 12;
489: if (buf[p - 1] == '0') {
490: p--;
491: }
492: if (buf[p - 1] == '0') {
493: p--;
494: }
495: }
496: stringValue = String.valueOf(buf, 0, p);
497: }
498: return stringValue;
499: }
500: }
|