001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.util;
007:
008: import java.sql.Date;
009: import java.sql.SQLException;
010: import java.sql.Time;
011: import java.sql.Timestamp;
012: import java.util.Calendar;
013: import java.util.GregorianCalendar;
014: import java.util.TimeZone;
015:
016: import org.h2.message.Message;
017: import org.h2.value.Value;
018: import org.h2.value.ValueDate;
019: import org.h2.value.ValueTime;
020: import org.h2.value.ValueTimestamp;
021:
022: /**
023: * This utility class contains time conversion functions.
024: */
025: public class DateTimeUtils {
026:
027: public static Timestamp convertTimestampToCalendar(Timestamp x,
028: Calendar calendar) throws SQLException {
029: if (x != null) {
030: Timestamp y = new Timestamp(getLocalTime(x, calendar));
031: // fix the nano seconds
032: y.setNanos(x.getNanos());
033: x = y;
034: }
035: return x;
036: }
037:
038: public static Time cloneAndNormalizeTime(Time value) {
039: Calendar cal = Calendar.getInstance();
040: cal.setTime(value);
041: cal.set(1970, 0, 1);
042: return new Time(cal.getTime().getTime());
043: }
044:
045: public static Date cloneAndNormalizeDate(Date value) {
046: Calendar cal = Calendar.getInstance();
047: cal.setTime(value);
048: cal.set(Calendar.MILLISECOND, 0);
049: cal.set(Calendar.SECOND, 0);
050: cal.set(Calendar.MINUTE, 0);
051: cal.set(Calendar.HOUR_OF_DAY, 0);
052: return new Date(cal.getTime().getTime());
053: }
054:
055: public static Value convertDateToUniversal(Date x, Calendar source)
056: throws SQLException {
057: return ValueDate.get(new Date(getUniversalTime(source, x)));
058: }
059:
060: public static Value convertTimeToUniversal(Time x, Calendar source)
061: throws SQLException {
062: return ValueTime.get(new Time(getUniversalTime(source, x)));
063: }
064:
065: public static Value convertTimestampToUniversal(Timestamp x,
066: Calendar source) throws SQLException {
067: Timestamp y = new Timestamp(getUniversalTime(source, x));
068: // fix the nano seconds
069: y.setNanos(x.getNanos());
070: return ValueTimestamp.getNoCopy(y);
071: }
072:
073: private static long getUniversalTime(Calendar source,
074: java.util.Date x) throws SQLException {
075: if (source == null) {
076: throw Message.getInvalidValueException("calendar", null);
077: }
078: source = (Calendar) source.clone();
079: Calendar universal = Calendar.getInstance();
080: source.setTime(x);
081: convertTime(source, universal);
082: return universal.getTime().getTime();
083: }
084:
085: private static long getLocalTime(java.util.Date x, Calendar target)
086: throws SQLException {
087: if (target == null) {
088: throw Message.getInvalidValueException("calendar", null);
089: }
090: target = (Calendar) target.clone();
091: Calendar local = Calendar.getInstance();
092: local.setTime(x);
093: convertTime(local, target);
094: return target.getTime().getTime();
095: }
096:
097: private static void convertTime(Calendar from, Calendar to) {
098: to.set(Calendar.YEAR, from.get(Calendar.YEAR));
099: to.set(Calendar.MONTH, from.get(Calendar.MONTH));
100: to.set(Calendar.DAY_OF_MONTH, from.get(Calendar.DAY_OF_MONTH));
101: to.set(Calendar.HOUR_OF_DAY, from.get(Calendar.HOUR_OF_DAY));
102: to.set(Calendar.MINUTE, from.get(Calendar.MINUTE));
103: to.set(Calendar.SECOND, from.get(Calendar.SECOND));
104: to.set(Calendar.MILLISECOND, from.get(Calendar.MILLISECOND));
105: }
106:
107: public static Date convertDateToCalendar(Date x, Calendar calendar)
108: throws SQLException {
109: return x == null ? null : new Date(getLocalTime(x, calendar));
110: }
111:
112: public static Time convertTimeToCalendar(Time x, Calendar calendar)
113: throws SQLException {
114: return x == null ? null : new Time(getLocalTime(x, calendar));
115: }
116:
117: public static java.util.Date parseDateTime(String original,
118: int type, int errorCode) throws SQLException {
119: String s = original;
120: if (s == null) {
121: return null;
122: }
123: try {
124: int timeStart = 0;
125: TimeZone tz = null;
126: if (type == Value.TIME) {
127: timeStart = 0;
128: } else {
129: timeStart = s.indexOf(' ') + 1;
130: if (timeStart <= 0) {
131: // ISO 8601 compatibility
132: timeStart = s.indexOf('T') + 1;
133: }
134: }
135:
136: int year = 1970, month = 1, day = 1;
137: if (type != Value.TIME) {
138: // support +year
139: if (s.startsWith("+")) {
140: s = s.substring(1);
141: }
142: // start at position 1 to support -year
143: int s1 = s.indexOf('-', 1);
144: int s2 = s.indexOf('-', s1 + 1);
145: if (s1 <= 0 || s2 <= s1) {
146: throw Message.getSQLException(errorCode,
147: new String[] { s, "format yyyy-mm-dd" });
148: }
149: year = Integer.parseInt(s.substring(0, s1));
150: month = Integer.parseInt(s.substring(s1 + 1, s2));
151: int end = timeStart == 0 ? s.length() : timeStart - 1;
152: day = Integer.parseInt(s.substring(s2 + 1, end));
153: }
154: int hour = 0, minute = 0, second = 0, nano = 0;
155: if (type != Value.DATE) {
156: int s1 = s.indexOf(':', timeStart);
157: int s2 = s.indexOf(':', s1 + 1);
158: int s3 = s.indexOf('.', s2 + 1);
159: if (s1 <= 0 || s2 <= s1) {
160: throw Message.getSQLException(errorCode,
161: new String[] { s, "format hh:mm:ss" });
162: }
163:
164: if (s.endsWith("Z")) {
165: s = s.substring(0, s.length() - 1);
166: tz = TimeZone.getTimeZone("UTC");
167: } else {
168: int timezoneStart = s.indexOf('+', s2 + 1);
169: if (timezoneStart < 0) {
170: timezoneStart = s.indexOf('-', s2 + 1);
171: }
172: if (timezoneStart >= 0) {
173: String tzName = "GMT"
174: + s.substring(timezoneStart);
175: tz = TimeZone.getTimeZone(tzName);
176: if (!tz.getID().equals(tzName)) {
177: throw Message
178: .getSQLException(errorCode,
179: new String[] {
180: s,
181: tz.getID() + " <>"
182: + tzName });
183: }
184: s = s.substring(0, timezoneStart).trim();
185: }
186: }
187:
188: hour = Integer.parseInt(s.substring(timeStart, s1));
189: minute = Integer.parseInt(s.substring(s1 + 1, s2));
190: if (s3 < 0) {
191: second = Integer.parseInt(s.substring(s2 + 1));
192: } else {
193: second = Integer.parseInt(s.substring(s2 + 1, s3));
194: String n = (s + "000000000").substring(s3 + 1,
195: s3 + 10);
196: nano = Integer.parseInt(n);
197: }
198: }
199: if (hour < 0 || hour > 23) {
200: throw new IllegalArgumentException("hour: " + hour);
201: }
202: long time;
203: try {
204: time = getTime(false, tz, year, month, day, hour,
205: minute, second, type != Value.TIMESTAMP, nano);
206: } catch (IllegalArgumentException e) {
207: // special case: if the time simply doesn't exist, use the lenient version
208: if (e.toString().indexOf("HOUR_OF_DAY") > 0) {
209: time = getTime(true, tz, year, month, day, hour,
210: minute, second, type != Value.TIMESTAMP,
211: nano);
212: } else {
213: throw e;
214: }
215: }
216: switch (type) {
217: case Value.DATE:
218: return new java.sql.Date(time);
219: case Value.TIME:
220: return new java.sql.Time(time);
221: case Value.TIMESTAMP: {
222: Timestamp ts = new Timestamp(time);
223: ts.setNanos(nano);
224: return ts;
225: }
226: default:
227: throw Message.getInternalError("type:" + type);
228: }
229: } catch (IllegalArgumentException e) {
230: throw Message.getSQLException(errorCode, new String[] {
231: original, e.toString() }, e);
232: }
233: }
234:
235: private static long getTime(boolean lenient, TimeZone tz, int year,
236: int month, int day, int hour, int minute, int second,
237: boolean setMillis, int nano) {
238: Calendar c;
239: if (tz == null) {
240: c = Calendar.getInstance();
241: } else {
242: c = Calendar.getInstance(tz);
243: }
244: c.setLenient(lenient);
245: if (year <= 0) {
246: c.set(Calendar.ERA, GregorianCalendar.BC);
247: c.set(Calendar.YEAR, 1 - year);
248: } else {
249: c.set(Calendar.YEAR, year);
250: }
251: c.set(Calendar.MONTH, month - 1); // january is 0
252: c.set(Calendar.DAY_OF_MONTH, day);
253: c.set(Calendar.HOUR_OF_DAY, hour);
254: c.set(Calendar.MINUTE, minute);
255: c.set(Calendar.SECOND, second);
256: if (setMillis) {
257: c.set(Calendar.MILLISECOND, nano / 1000000);
258: }
259: return c.getTime().getTime();
260: }
261:
262: }
|