All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.HsqlDateTime Maven / Gradle / Ivy

There is a newer version: 2.7.4
Show newest version
/* Copyright (c) 2001-2021, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.StringUtil;
import org.hsqldb.types.TimestampData;
import org.hsqldb.types.Types;

/**
 * collection of static methods to convert Date and Timestamp strings
 * into corresponding Java objects and perform other Calendar related
 * operation.

* * Was reviewed for 1.7.2 resulting in centralising all DATETIME related * operations.

* * From version 2.0.0, HSQLDB supports TIME ZONE with datetime types. The * values are stored internally as UTC seconds from 1970, regardless of the * time zone of the JVM, and converted as and when required, to the local * timezone. * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.6.0 * @since 1.7.0 */ public class HsqlDateTime { public static final Locale defaultLocale = Locale.UK; private static final Calendar tempCalDefault = new GregorianCalendar(); private static final Calendar tempCalGMT = new GregorianCalendar(TimeZone.getTimeZone("GMT"), defaultLocale); private static final String sdfdPattern = "yyyy-MM-dd"; private static final SimpleDateFormat sdfd = new SimpleDateFormat(sdfdPattern); private static final String sdftPattern = "HH:mm:ss"; private static final SimpleDateFormat sdft = new SimpleDateFormat(sdftPattern); private static final String sdftsPattern = "yyyy-MM-dd HH:mm:ss"; private static final SimpleDateFormat sdfts = new SimpleDateFormat(sdftsPattern); private static final String sdftsSysPattern = "yyyy-MM-dd HH:mm:ss.SSS"; private static final SimpleDateFormat sdftsSys = new SimpleDateFormat(sdftsSysPattern); private static final Date sysDate = new java.util.Date(); static { TimeZone.getDefault(); tempCalGMT.setLenient(false); sdfd.setCalendar(new GregorianCalendar(TimeZone.getTimeZone("GMT"), defaultLocale)); sdfd.setLenient(false); sdft.setCalendar(new GregorianCalendar(TimeZone.getTimeZone("GMT"), defaultLocale)); sdft.setLenient(false); sdfts.setCalendar(new GregorianCalendar(TimeZone.getTimeZone("GMT"), defaultLocale)); sdfts.setLenient(false); } public static long getDateSeconds(String s) { try { synchronized (sdfd) { java.util.Date d = sdfd.parse(s); return d.getTime() / 1000; } } catch (Exception e) { throw Error.error(ErrorCode.X_22007); } } public static String getDateString(long seconds) { synchronized (sdfd) { sysDate.setTime(seconds * 1000); return sdfd.format(sysDate); } } public static long getTimestampSeconds(String s) { try { synchronized (sdfts) { java.util.Date d = sdfts.parse(s); return d.getTime() / 1000; } } catch (Exception e) { throw Error.error(ErrorCode.X_22007); } } public static String getTimestampString(long seconds, int nanos, int scale) { synchronized (sdfts) { sysDate.setTime(seconds * 1000); String ts = sdfts.format(sysDate); if (scale > 0) { ts += '.' + StringUtil.toZeroPaddedString(nanos, 9, scale); } return ts; } } public static String getTimestampString(long millis) { synchronized (sdfts) { sysDate.setTime(millis); return sdfts.format(sysDate); } } private static void resetToDate(Calendar cal) { cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); } private static void resetToTime(Calendar cal) { cal.set(Calendar.YEAR, 1970); cal.set(Calendar.MONTH, 0); cal.set(Calendar.DATE, 1); cal.set(Calendar.MILLISECOND, 0); } public static long convertMillisToCalendar(Calendar calendar, long millis) { synchronized (tempCalGMT) { synchronized (calendar) { calendar.clear(); tempCalGMT.setTimeInMillis(millis); calendar.set(tempCalGMT.get(Calendar.YEAR), tempCalGMT.get(Calendar.MONTH), tempCalGMT.get(Calendar.DAY_OF_MONTH), tempCalGMT.get(Calendar.HOUR_OF_DAY), tempCalGMT.get(Calendar.MINUTE), tempCalGMT.get(Calendar.SECOND)); return calendar.getTimeInMillis(); } } } public static long convertMillisFromCalendar(Calendar calendar, long millis) { return convertMillisFromCalendar(tempCalGMT, calendar, millis); } public static long convertMillisFromCalendar(Calendar clendarGMT, Calendar calendar, long millis) { synchronized (clendarGMT) { synchronized (calendar) { clendarGMT.clear(); calendar.setTimeInMillis(millis); clendarGMT.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND)); return clendarGMT.getTimeInMillis(); } } } /** * Sets the time in the given Calendar using the given milliseconds value; wrapper method to * allow use of more efficient JDK1.4 method on JDK1.4 (was protected in earlier versions). * * @param cal the Calendar * @param millis the time value in milliseconds */ public static void setTimeInMillis(Calendar cal, long millis) { cal.setTimeInMillis(millis); } public static long convertToNormalisedTime(long t) { return convertToNormalisedTime(tempCalGMT, t); } public static long convertToNormalisedTime(Calendar cal, long t) { synchronized (cal) { setTimeInMillis(cal, t); resetToDate(cal); long t1 = cal.getTimeInMillis(); return t - t1; } } public static long getNormalisedTime(long t) { return getNormalisedTime(tempCalGMT, t); } public static long getNormalisedTime(Calendar calendar, long t) { synchronized (calendar) { setTimeInMillis(calendar, t); resetToTime(calendar); return calendar.getTimeInMillis(); } } public static long getNormalisedDate(long d) { return getNormalisedDate(tempCalGMT, d); } public static long getNormalisedDate(Calendar calendar, long t) { synchronized (calendar) { setTimeInMillis(calendar, t); resetToDate(calendar); return calendar.getTimeInMillis(); } } public static int getZoneSeconds() { return getZoneSeconds(tempCalDefault); } public static int getZoneSeconds(Calendar calendar) { return (calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)) / 1000; } public static int getZoneMillis(Calendar calendar, long millis) { return calendar.getTimeZone().getOffset(millis); } /** * Returns the indicated part of the given millisecond date object. * @param m the millisecond time value from which to extract the indicated part * @param part an integer code corresponding to the desired date part * @return the indicated part of the given java.util.Date object */ public static int getDateTimePart(Calendar calendar, long m, int part) { synchronized (calendar) { calendar.setTimeInMillis(m); return calendar.get(part); } } /** * truncates millisecond date object */ public static long getTruncatedPart(Calendar calendar, long m, int part) { synchronized (calendar) { calendar.setTimeInMillis(m); switch (part) { case Types.DTI_ISO_WEEK_OF_YEAR : { int dayWeek = calendar.get(Calendar.DAY_OF_WEEK); if (dayWeek == 1) { dayWeek = 8; } calendar.add(Calendar.DAY_OF_YEAR, 2 - dayWeek); resetToDate(calendar); break; } case Types.DTI_WEEK_OF_YEAR : { int dayWeek = calendar.get(Calendar.DAY_OF_WEEK); calendar.add(Calendar.DAY_OF_YEAR, 1 - dayWeek); resetToDate(calendar); break; } default : { zeroFromPart(calendar, part); break; } } return calendar.getTimeInMillis(); } } /** * rounded millisecond date object */ public static long getRoundedPart(Calendar calendar, long m, int part) { synchronized (calendar) { calendar.setTimeInMillis(m); switch (part) { case Types.SQL_INTERVAL_YEAR : if (calendar.get(Calendar.MONTH) > 6) { calendar.add(Calendar.YEAR, 1); } break; case Types.SQL_INTERVAL_MONTH : if (calendar.get(Calendar.DAY_OF_MONTH) > 15) { calendar.add(Calendar.MONTH, 1); } break; case Types.SQL_INTERVAL_DAY : if (calendar.get(Calendar.HOUR_OF_DAY) > 11) { calendar.add(Calendar.DAY_OF_MONTH, 1); } break; case Types.SQL_INTERVAL_HOUR : if (calendar.get(Calendar.MINUTE) > 29) { calendar.add(Calendar.HOUR_OF_DAY, 1); } break; case Types.SQL_INTERVAL_MINUTE : if (calendar.get(Calendar.SECOND) > 29) { calendar.add(Calendar.MINUTE, 1); } break; case Types.SQL_INTERVAL_SECOND : if (calendar.get(Calendar.MILLISECOND) > 499) { calendar.add(Calendar.SECOND, 1); } break; case Types.DTI_WEEK_OF_YEAR : { int dayYear = calendar.get(Calendar.DAY_OF_YEAR); int year = calendar.get(Calendar.YEAR); int week = calendar.get(Calendar.WEEK_OF_YEAR); int day = calendar.get(Calendar.DAY_OF_WEEK); calendar.clear(); calendar.set(Calendar.YEAR, year); if (day > 3) { week++; } if (week == 1 && (dayYear > 356 || dayYear < 7)) { calendar.set(Calendar.DAY_OF_YEAR, dayYear); while (true) { if (calendar.get(Calendar.DAY_OF_WEEK) == 1) { return calendar.getTimeInMillis(); } calendar.add(Calendar.DAY_OF_YEAR, -1); } } calendar.set(Calendar.WEEK_OF_YEAR, week); return calendar.getTimeInMillis(); } } zeroFromPart(calendar, part); return calendar.getTimeInMillis(); } } static void zeroFromPart(Calendar cal, int part) { switch (part) { case Types.SQL_INTERVAL_YEAR : cal.set(Calendar.MONTH, 0); case Types.SQL_INTERVAL_MONTH : cal.set(Calendar.DAY_OF_MONTH, 1); case Types.SQL_INTERVAL_DAY : cal.set(Calendar.HOUR_OF_DAY, 0); case Types.SQL_INTERVAL_HOUR : cal.set(Calendar.MINUTE, 0); case Types.SQL_INTERVAL_MINUTE : cal.set(Calendar.SECOND, 0); case Types.SQL_INTERVAL_SECOND : cal.set(Calendar.MILLISECOND, 0); default : } } //J- private static final char[][] dateTokens = { { 'R', 'R', 'R', 'R' }, { 'I', 'Y', 'Y', 'Y' }, { 'Y', 'Y', 'Y', 'Y' }, { 'I', 'Y' }, { 'Y', 'Y' }, { 'B', 'C' }, { 'B', '.', 'C', '.' }, { 'A', 'D' }, { 'A', '.', 'D', '.' }, { 'M', 'O', 'N' }, { 'M', 'O', 'N', 'T', 'H' }, { 'M', 'M' }, { 'D', 'A', 'Y' }, { 'D', 'Y' }, { 'W', 'W' }, { 'I', 'W' }, { 'D', 'D' }, { 'D', 'D', 'D' }, { 'W' }, { 'H', 'H', '2', '4' }, { 'H', 'H', '1', '2' }, { 'H', 'H' }, { 'M', 'I' }, { 'S', 'S' }, { 'A', 'M' }, { 'P', 'M' }, { 'A', '.', 'M', '.' }, { 'P', '.', 'M', '.' }, { 'F', 'F' } }; private static final String[] javaDateTokens = { "yyyy", "'*IYYY'", "yyyy", "'*IY'", "yy", "G", "G", "G", "G", "MMM", "MMMMM", "MM", "EEEE", "EE", "'*WW'", "'*IW'", "dd", "D", "'*W'", "HH", "KK", "KK", "mm", "ss", "aaa", "aaa", "aaa", "aaa", "SSS" }; private static final int[] sqlIntervalCodes = { -1, -1, Types.SQL_INTERVAL_YEAR, -1, Types.SQL_INTERVAL_YEAR, -1, -1, -1, -1, Types.SQL_INTERVAL_MONTH, Types.SQL_INTERVAL_MONTH, Types.SQL_INTERVAL_MONTH, -1, -1, Types.DTI_WEEK_OF_YEAR, Types.DTI_ISO_WEEK_OF_YEAR, Types.SQL_INTERVAL_DAY, Types.SQL_INTERVAL_DAY, -1, Types.SQL_INTERVAL_HOUR, -1, Types.SQL_INTERVAL_HOUR, Types.SQL_INTERVAL_MINUTE, Types.SQL_INTERVAL_SECOND, -1,-1,-1,-1, -1 }; //J+ /** Indicates end-of-input */ private static final char e = 0xffff; public static TimestampData toDate(String string, String pattern, SimpleDateFormat format, boolean fraction) { long millis; int nanos = 0; String javaPattern = HsqlDateTime.toJavaDatePattern(pattern); String tempPattern = null; int matchIndex = javaPattern.indexOf("*IY"); if (matchIndex >= 0) { throw Error.error(ErrorCode.X_22511); } matchIndex = javaPattern.indexOf("*WW"); if (matchIndex >= 0) { throw Error.error(ErrorCode.X_22511); } matchIndex = javaPattern.indexOf("*W"); if (matchIndex >= 0) { throw Error.error(ErrorCode.X_22511); } matchIndex = javaPattern.indexOf("SSS"); if (matchIndex >= 0) { tempPattern = javaPattern; javaPattern = javaPattern.substring(0, matchIndex) + javaPattern.substring(matchIndex + 3); } try { format.applyPattern(javaPattern); millis = format.parse(string).getTime(); } catch (Exception e) { throw Error.error(ErrorCode.X_22007, e.toString()); } if (matchIndex >= 0 && fraction) { javaPattern = tempPattern; try { format.applyPattern(javaPattern); long tempMillis = format.parse(string).getTime(); int factor = 1; tempMillis -= millis; nanos = (int) tempMillis; while (tempMillis > 1000) { tempMillis /= 10; factor *= 10; } nanos *= (1000000 / factor); } catch (Exception e) { throw Error.error(ErrorCode.X_22007, e.toString()); } } return new TimestampData(millis / 1000, nanos, 0); } public static String toFormattedDate(Date date, String pattern, SimpleDateFormat format) { String javaPattern = HsqlDateTime.toJavaDatePattern(pattern); try { format.applyPattern(javaPattern); } catch (Exception e) { throw Error.error(ErrorCode.X_22511); } String result = format.format(date); int matchIndex = result.indexOf("*IY"); if (matchIndex >= 0) { Calendar cal = format.getCalendar(); int matchLength = 3; int temp = result.indexOf("*IYYY"); if (temp >= 0) { matchLength = 5; matchIndex = temp; } int year = cal.get(Calendar.YEAR); int weekOfYear = cal.get(Calendar.WEEK_OF_YEAR); if (weekOfYear == 1 && cal.get(Calendar.DAY_OF_YEAR) > 360) { year++; } else if (weekOfYear > 51 && cal.get(Calendar.DAY_OF_YEAR) < 4) { year--; } String yearString = String.valueOf(year); if (matchLength == 3) { yearString = yearString.substring(yearString.length() - 2); } StringBuilder sb = new StringBuilder(result); sb.replace(matchIndex, matchIndex + matchLength, yearString); result = sb.toString(); } matchIndex = result.indexOf("*WW"); if (matchIndex >= 0) { Calendar cal = format.getCalendar(); int matchLength = 3; int dayOfYear = cal.get(Calendar.DAY_OF_YEAR); int weekOfYear = ((dayOfYear - 1) / 7) + 1; String week = String.valueOf(weekOfYear); if (week.length() == 1) { week = "0" + week; } StringBuilder sb = new StringBuilder(result); sb.replace(matchIndex, matchIndex + matchLength, week); result = sb.toString(); } matchIndex = result.indexOf("*IW"); if (matchIndex >= 0) { Calendar cal = format.getCalendar(); int matchLength = 3; int weekOfYear = cal.get(Calendar.WEEK_OF_YEAR); String week = String.valueOf(weekOfYear); if (week.length() == 1) { week = "0" + week; } StringBuilder sb = new StringBuilder(result); sb.replace(matchIndex, matchIndex + matchLength, week); result = sb.toString(); } matchIndex = result.indexOf("*W"); if (matchIndex >= 0) { Calendar cal = format.getCalendar(); int matchLength = 2; int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); int weekOfMonth = ((dayOfMonth - 1) / 7) + 1; StringBuilder sb = new StringBuilder(result); sb.replace(matchIndex, matchIndex + matchLength, String.valueOf(weekOfMonth)); result = sb.toString(); } return result; } /** * Converts the given format into a pattern accepted by java.text.SimpleDataFormat * * @param format date format */ public static String toJavaDatePattern(String format) { int len = format.length(); char ch; StringBuilder sb = new StringBuilder(len); Tokenizer tokenizer = new Tokenizer(); int limitQuotedToken = -1; for (int i = 0; i <= len; i++) { ch = (i == len) ? e : format.charAt(i); if (tokenizer.isInQuotes()) { if (tokenizer.isQuoteChar(ch)) { ch = '\''; } else if (ch == '\'') { // double the single quote sb.append(ch); } sb.append(ch); continue; } if (!tokenizer.next(ch, i)) { if (tokenizer.consumed) { int index = tokenizer.getLastMatch(); String s = javaDateTokens[index]; // consecutive quoted tokens if (s.startsWith("\'") && s.endsWith("\'")) { if (limitQuotedToken == sb.length()) { sb.setLength(sb.length() - 1); s = s.substring(1); } limitQuotedToken = sb.length() + s.length(); } sb.append(s); i = tokenizer.matchOffset; } else { if (tokenizer.isQuoteChar(ch)) { ch = '\''; sb.append(ch); } else if (tokenizer.isLiteral(ch)) { sb.append(ch); } else if (ch == e) { // } else { throw Error.error(ErrorCode.X_22007, format.substring(i)); } } tokenizer.reset(); } } if (tokenizer.isInQuotes()) { throw Error.error(ErrorCode.X_22007); } String javaPattern = sb.toString(); return javaPattern; } public static int toStandardIntervalPart(String format) { int len = format.length(); char ch; Tokenizer tokenizer = new Tokenizer(); for (int i = 0; i <= len; i++) { ch = (i == len) ? e : format.charAt(i); if (!tokenizer.next(ch, i)) { int index = tokenizer.getLastMatch(); if (index >= 0) { return sqlIntervalCodes[index]; } return -1; } } return -1; } /** * Timestamp String generator */ public static class SystemTimeString { private java.util.Date date = new java.util.Date(); private SimpleDateFormat dateFormat = new SimpleDateFormat(sdftsSysPattern); public SystemTimeString() { dateFormat.setCalendar( new GregorianCalendar( TimeZone.getTimeZone("GMT"), defaultLocale)); dateFormat.setLenient(false); } public synchronized String getTimestampString() { date.setTime(System.currentTimeMillis()); return dateFormat.format(date); } } /** * This class can match 64 tokens at maximum. */ static class Tokenizer { private int lastMatched; private int matchOffset; private int offset; private long state; private boolean consumed; private boolean isInQuotes; private boolean matched; // private final char quoteChar; private final char[] literalChars; private static char[] defaultLiterals = new char[] { ' ', ',', '-', '.', '/', ':', ';' }; char[][] tokens; public Tokenizer() { this.quoteChar = '\"'; this.literalChars = defaultLiterals; tokens = dateTokens; reset(); } /** * Resets for next reuse. * */ public void reset() { lastMatched = -1; offset = -1; state = 0; consumed = false; matched = false; } /** * Returns the length of a token to match. */ public int length() { return offset; } /** * Returns an index of the last matched token. */ public int getLastMatch() { return lastMatched; } /** * Indicates whether the last character has been consumed by the matcher. */ public boolean isConsumed() { return consumed; } /** * Indicates whether the last character has been matched by the matcher. */ public boolean wasMatched() { return matched; } /** * Indicates if tokenizing a quoted string */ public boolean isInQuotes() { return isInQuotes; } /** * returns true if character is the quote char and sets state */ public boolean isQuoteChar(char ch) { if (quoteChar == ch) { isInQuotes = !isInQuotes; return true; } return false; } /** * Returns true if ch is in the list of literals */ public boolean isLiteral(char ch) { return ArrayUtil.isInSortedArray(ch, literalChars); } /** * Checks whether the specified bit is not set. * * @param bit numbered from high bit */ private boolean isZeroBit(int bit) { return (state & (1L << bit)) == 0; } /** * Sets the specified bit. * @param bit numbered from high bit */ private void setBit(int bit) { state |= (1L << bit); } /** * Matches the specified character against tokens. * * @param ch character * @param position in the string */ public boolean next(char ch, int position) { int index = ++offset; int len = offset + 1; int left = 0; matched = false; for (int i = tokens.length; --i >= 0; ) { if (isZeroBit(i)) { if (tokens[i][index] == Character.toUpperCase(ch)) { if (tokens[i].length == len) { setBit(i); lastMatched = i; consumed = true; matched = true; matchOffset = position; } else { ++left; } } else { setBit(i); } } } return left > 0; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy