提交 b023ccbf authored 作者: Thomas Mueller's avatar Thomas Mueller

Formatting, Javadocs

上级 9752cf23
...@@ -407,7 +407,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -407,7 +407,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* new String[] { "-trace" }).start(); * new String[] { "-trace" }).start();
* </pre> * </pre>
* Supported options are: * Supported options are:
* -webPort, -webSSL, -webAllowOthers, -webDaemon, * -webPort, -webSSL, -webAllowOthers, -webDaemon,
* -trace, -ifExists, -baseDir, -properties. * -trace, -ifExists, -baseDir, -properties.
* See the main method for details. * See the main method for details.
* *
...@@ -429,7 +429,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -429,7 +429,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* new String[] { "-tcpPort", "9123", "-tcpAllowOthers" }).start(); * new String[] { "-tcpPort", "9123", "-tcpAllowOthers" }).start();
* </pre> * </pre>
* Supported options are: * Supported options are:
* -tcpPort, -tcpSSL, -tcpPassword, -tcpAllowOthers, -tcpDaemon, * -tcpPort, -tcpSSL, -tcpPassword, -tcpAllowOthers, -tcpDaemon,
* -trace, -ifExists, -baseDir, -key. * -trace, -ifExists, -baseDir, -key.
* See the main method for details. * See the main method for details.
* *
...@@ -451,7 +451,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler { ...@@ -451,7 +451,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
* Server.createPgServer("-pgAllowOthers").start(); * Server.createPgServer("-pgAllowOthers").start();
* </pre> * </pre>
* Supported options are: * Supported options are:
* -pgPort, -pgAllowOthers, -pgDaemon, * -pgPort, -pgAllowOthers, -pgDaemon,
* -trace, -ifExists, -baseDir, -key. * -trace, -ifExists, -baseDir, -key.
* See the main method for details. * See the main method for details.
* *
......
...@@ -219,7 +219,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData { ...@@ -219,7 +219,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData {
public SimpleResultSet(SimpleRowSource source) { public SimpleResultSet(SimpleRowSource source) {
this.source = source; this.source = source;
} }
/** /**
* Adds a column to the result set. * Adds a column to the result set.
* All columns must be added before adding rows. * All columns must be added before adding rows.
......
/* /*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: Daniel Gredler * Initial Developer: Daniel Gredler
*/ */
package org.h2.util; package org.h2.util;
import static java.lang.Math.abs; import static java.lang.Math.abs;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Date; import java.sql.Date;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Currency; import java.util.Currency;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
/** /**
* Emulates Oracle's TO_CHAR function. * Emulates Oracle's TO_CHAR function.
*/ */
public class ToChar { public class ToChar {
/** The beginning of the Julian calendar. */ /**
private static final long JULIAN_EPOCH; * The beginning of the Julian calendar.
*/
static { private static final long JULIAN_EPOCH;
GregorianCalendar epoch = new GregorianCalendar(Locale.ENGLISH);
epoch.setGregorianChange(new Date(Long.MAX_VALUE)); static {
epoch.clear(); GregorianCalendar epoch = new GregorianCalendar(Locale.ENGLISH);
epoch.set(4713, Calendar.JANUARY, 1, 0, 0, 0); epoch.setGregorianChange(new Date(Long.MAX_VALUE));
epoch.set(Calendar.ERA, GregorianCalendar.BC); epoch.clear();
JULIAN_EPOCH = epoch.getTimeInMillis(); epoch.set(4713, Calendar.JANUARY, 1, 0, 0, 0);
} epoch.set(Calendar.ERA, GregorianCalendar.BC);
JULIAN_EPOCH = epoch.getTimeInMillis();
private ToChar() { }
// utility class
} private ToChar() {
// utility class
/** }
* Emulates Oracle's TO_CHAR(number) function.
* /**
* <p><table border="1"> * Emulates Oracle's TO_CHAR(number) function.
* <tr><td><b>Input</b></td><td><b>Output</b></td><td><b>Closest {@link DecimalFormat} Equivalent</b></td></tr> *
* <tr><td>,</td><td>Grouping separator.</td><td>,</td></tr> * <p><table border="1">
* <tr><td>.</td><td>Decimal separator.</td><td>.</td></tr> * <th><td>Input</td>
* <tr><td>$</td><td>Leading dollar sign.</td><td>$</td></tr> * <td>Output</td>
* <tr><td>0</td><td>Leading or trailing zeroes.</td><td>0</td></tr> * <td>Closest {@link DecimalFormat} Equivalent</td></th>
* <tr><td>9</td><td>Digit.</td><td>#</td></tr> * <tr><td>,</td>
* <tr><td>B</td><td>Blanks integer part of a fixed point number less than 1.</td><td>#</td></tr> * <td>Grouping separator.</td>
* <tr><td>C</td><td>ISO currency symbol.</td><td>\u00A4</td></tr> * <td>,</td></tr>
* <tr><td>D</td><td>Local decimal separator.</td><td>.</td></tr> * <tr><td>.</td>
* <tr><td>EEEE</td><td>Returns a value in scientific notation.</td><td>E</td></tr> * <td>Decimal separator.</td>
* <tr><td>FM</td><td>Returns values with no leading or trailing spaces.</td><td>None.</td></tr> * <td>.</td></tr>
* <tr><td>G</td><td>Local grouping separator.</td><td>,</td></tr> * <tr><td>$</td>
* <tr><td>L</td><td>Local currency symbol.</td><td>\u00A4</td></tr> * <td>Leading dollar sign.</td>
* <tr><td>MI</td><td>Negative values get trailing minus sign, positive get trailing space.</td><td>-</td></tr> * <td>$</td></tr>
* <tr><td>PR</td><td>Negative values get enclosing angle brackets, positive get spaces.</td><td>None.</td></tr> * <tr><td>0</td>
* <tr><td>RN</td><td>Returns values in Roman numerals.</td><td>None.</td></tr> * <td>Leading or trailing zeroes.</td>
* <tr><td>S</td><td>Returns values with leading/trailing +/- signs.</td><td>None.</td></tr> * <td>0</td></tr>
* <tr><td>TM</td><td>Returns smallest number of characters possible.</td><td>None.</td></tr> * <tr><td>9</td>
* <tr><td>U</td><td>Returns the dual currency symbol.</td><td>None.</td></tr> * <td>Digit.</td>
* <tr><td>V</td><td>Returns a value multiplied by 10^n.</td><td>None.</td></tr> * <td>#</td></tr>
* <tr><td>X</td><td>Hex value.</td><td>None.</td></tr> * <tr><td>B</td>
* </table> * <td>Blanks integer part of a fixed point number less than 1.</td>
* * <td>#</td></tr>
* @param number the number to format * <tr><td>C</td>
* @param format the format pattern to use (if any) * <td>ISO currency symbol.</td>
* @param nlsParam the NLS parameter (if any) * <td>\u00A4</td></tr>
* @return the formatted number * <tr><td>D</td>
* @see <a href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions181.htm">TO_CHAR(number)</a> * <td>Local decimal separator.</td>
* @see <a href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34570">Number format models</a> * <td>.</td></tr>
*/ * <tr><td>EEEE</td>
public static String toChar(BigDecimal number, String format, String nlsParam) { * <td>Returns a value in scientific notation.</td>
* <td>E</td></tr>
// short-circuit logic for formats that don't follow common logic below * <tr><td>FM</td>
String formatUp = format != null ? format.toUpperCase() : null; * <td>Returns values with no leading or trailing spaces.</td>
if (formatUp == null || formatUp.equals("TM") || formatUp.equals("TM9")) { * <td>None.</td></tr>
String s = number.toPlainString(); * <tr><td>G</td>
return s.startsWith("0.") ? s.substring(1) : s; * <td>Local grouping separator.</td>
} else if (formatUp.equals("TME")) { * <td>,</td></tr>
int pow = number.precision() - number.scale() - 1; * <tr><td>L</td>
number = number.movePointLeft(pow); * <td>Local currency symbol.</td>
return number.toPlainString() + "E" + (pow < 0 ? '-' : '+') + (abs(pow) < 10 ? "0" : "") + abs(pow); * <td>\u00A4</td></tr>
} else if (formatUp.equals("RN")) { * <tr><td>MI</td>
boolean lowercase = format.startsWith("r"); * <td>Negative values get trailing minus sign, positive get trailing space.</td>
String rn = StringUtils.pad(toRomanNumeral(number.intValue()), 15, " ", false); * <td>-</td></tr>
return lowercase ? rn.toLowerCase() : rn; * <tr><td>PR</td>
} else if (formatUp.equals("FMRN")) { * <td>Negative values get enclosing angle brackets, positive get spaces.</td>
boolean lowercase = format.charAt(2) == 'r'; * <td>None.</td></tr>
String rn = toRomanNumeral(number.intValue()); * <tr><td>RN</td>
return lowercase ? rn.toLowerCase() : rn; * <td>Returns values in Roman numerals.</td>
} else if (formatUp.endsWith("X")) { * <td>None.</td></tr>
return toHex(number, format); * <tr><td>S</td>
} * <td>Returns values with leading/trailing +/- signs.</td>
* <td>None.</td></tr>
String originalFormat = format; * <tr><td>TM</td>
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(); * <td>Returns smallest number of characters possible.</td>
char localGrouping = symbols.getGroupingSeparator(); * <td>None.</td></tr>
char localDecimal = symbols.getDecimalSeparator(); * <tr><td>U</td>
* <td>Returns the dual currency symbol.</td>
boolean leadingSign = formatUp.startsWith("S"); * <td>None.</td></tr>
if (leadingSign) { * <tr><td>V</td>
format = format.substring(1); * <td>Returns a value multiplied by 10^n.</td>
} * <td>None.</td></tr>
* <tr><td>X</td>
boolean trailingSign = formatUp.endsWith("S"); * <td>Hex value.</td>
if (trailingSign) { * <td>None.</td></tr>
format = format.substring(0, format.length() - 1); * </table>
} * See also TO_CHAR(number) and number format models
* in the Oracle documentation.
boolean trailingMinus = formatUp.endsWith("MI"); *
if (trailingMinus) { * @param number the number to format
format = format.substring(0, format.length() - 2); * @param format the format pattern to use (if any)
} * @param nlsParam the NLS parameter (if any)
* @return the formatted number
boolean angleBrackets = formatUp.endsWith("PR"); */
if (angleBrackets) { public static String toChar(BigDecimal number, String format, String nlsParam) {
format = format.substring(0, format.length() - 2);
} // short-circuit logic for formats that don't follow common logic below
String formatUp = format != null ? format.toUpperCase() : null;
int v = formatUp.indexOf("V"); if (formatUp == null || formatUp.equals("TM") || formatUp.equals("TM9")) {
if (v >= 0) { String s = number.toPlainString();
int digits = 0; return s.startsWith("0.") ? s.substring(1) : s;
for (int i = v + 1; i < format.length(); i++) { } else if (formatUp.equals("TME")) {
char c = format.charAt(i); int pow = number.precision() - number.scale() - 1;
if (c == '0' || c == '9') { number = number.movePointLeft(pow);
digits++; return number.toPlainString() + "E" + (pow < 0 ? '-' : '+') + (abs(pow) < 10 ? "0" : "") + abs(pow);
} } else if (formatUp.equals("RN")) {
} boolean lowercase = format.startsWith("r");
number = number.movePointRight(digits); String rn = StringUtils.pad(toRomanNumeral(number.intValue()), 15, " ", false);
format = format.substring(0, v) + format.substring(v + 1); return lowercase ? rn.toLowerCase() : rn;
} } else if (formatUp.equals("FMRN")) {
boolean lowercase = format.charAt(2) == 'r';
Integer power; String rn = toRomanNumeral(number.intValue());
if (format.endsWith("EEEE")) { return lowercase ? rn.toLowerCase() : rn;
power = number.precision() - number.scale() - 1; } else if (formatUp.endsWith("X")) {
number = number.movePointLeft(power); return toHex(number, format);
format = format.substring(0, format.length() - 4); }
} else {
power = null; String originalFormat = format;
} DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
char localGrouping = symbols.getGroupingSeparator();
int maxLength = 1; char localDecimal = symbols.getDecimalSeparator();
boolean fillMode = !formatUp.startsWith("FM");
if (!fillMode) { boolean leadingSign = formatUp.startsWith("S");
format = format.substring(2); if (leadingSign) {
} format = format.substring(1);
}
// blanks flag doesn't seem to actually do anything
format = format.replaceAll("[Bb]", ""); boolean trailingSign = formatUp.endsWith("S");
if (trailingSign) {
// if we need to round the number to fit into the format specified, format = format.substring(0, format.length() - 1);
// go ahead and do that first }
int separator = findDecimalSeparator(format);
int formatScale = calculateScale(format, separator); boolean trailingMinus = formatUp.endsWith("MI");
if (formatScale < number.scale()) { if (trailingMinus) {
number = number.setScale(formatScale, BigDecimal.ROUND_HALF_UP); format = format.substring(0, format.length() - 2);
} }
// any 9s to the left of the decimal separator but to the right of a boolean angleBrackets = formatUp.endsWith("PR");
// 0 behave the same as a 0, e.g. "09999.99" -> "00000.99" if (angleBrackets) {
for (int i = format.indexOf('0'); i >= 0 && i < separator; i++) { format = format.substring(0, format.length() - 2);
if (format.charAt(i) == '9') { }
format = format.substring(0, i) + "0" + format.substring(i + 1);
} int v = formatUp.indexOf("V");
} if (v >= 0) {
int digits = 0;
StringBuilder output = new StringBuilder(); for (int i = v + 1; i < format.length(); i++) {
String unscaled = number.unscaledValue().abs().toString(); char c = format.charAt(i);
if (c == '0' || c == '9') {
// start at the decimal point and fill in the numbers to the left, digits++;
// working our way from right to left }
int i = separator - 1; }
int j = unscaled.length() - number.scale() - 1; number = number.movePointRight(digits);
for (; i >= 0; i--) { format = format.substring(0, v) + format.substring(v + 1);
char c = format.charAt(i); }
maxLength++;
if (c == '9' || c == '0') { Integer power;
if (j >= 0) { if (format.endsWith("EEEE")) {
char digit = unscaled.charAt(j); power = number.precision() - number.scale() - 1;
output.insert(0, digit); number = number.movePointLeft(power);
j--; format = format.substring(0, format.length() - 4);
} else if (c == '0' && power == null) { } else {
output.insert(0, '0'); power = null;
} }
} else if (c == ',') {
// only add the grouping separator if we have more numbers int maxLength = 1;
if (j >= 0 || (i > 0 && format.charAt(i - 1) == '0')) { boolean fillMode = !formatUp.startsWith("FM");
output.insert(0, c); if (!fillMode) {
} format = format.substring(2);
} else if (c == 'G' || c == 'g') { }
// only add the grouping separator if we have more numbers
if (j >= 0 || (i > 0 && format.charAt(i - 1) == '0')) { // blanks flag doesn't seem to actually do anything
output.insert(0, localGrouping); format = format.replaceAll("[Bb]", "");
}
} else if (c == 'C' || c == 'c') { // if we need to round the number to fit into the format specified,
Currency currency = Currency.getInstance(Locale.getDefault()); // go ahead and do that first
output.insert(0, currency.getCurrencyCode()); int separator = findDecimalSeparator(format);
maxLength += 6; int formatScale = calculateScale(format, separator);
} else if (c == 'L' || c == 'l' || c == 'U' || c == 'u') { if (formatScale < number.scale()) {
Currency currency = Currency.getInstance(Locale.getDefault()); number = number.setScale(formatScale, BigDecimal.ROUND_HALF_UP);
output.insert(0, currency.getSymbol()); }
maxLength += 9;
} else if (c == '$') { // any 9s to the left of the decimal separator but to the right of a
Currency currency = Currency.getInstance(Locale.getDefault()); // 0 behave the same as a 0, e.g. "09999.99" -> "00000.99"
String cs = currency.getSymbol(); for (int i = format.indexOf('0'); i >= 0 && i < separator; i++) {
output.insert(0, cs); if (format.charAt(i) == '9') {
} else { format = format.substring(0, i) + "0" + format.substring(i + 1);
throw DbException.get(ErrorCode.INVALID_TO_CHAR_FORMAT, originalFormat); }
} }
}
StringBuilder output = new StringBuilder();
// if the format (to the left of the decimal point) was too small String unscaled = number.unscaledValue().abs().toString();
// to hold the number, return a big "######" string
if (j >= 0) { // start at the decimal point and fill in the numbers to the left,
return StringUtils.pad("", format.length() + 1, "#", true); // working our way from right to left
} int i = separator - 1;
int j = unscaled.length() - number.scale() - 1;
if (separator < format.length()) { for (; i >= 0; i--) {
char c = format.charAt(i);
// add the decimal point maxLength++;
maxLength++; if (c == '9' || c == '0') {
char pt = format.charAt(separator); if (j >= 0) {
if (pt == 'd' || pt == 'D') { char digit = unscaled.charAt(j);
output.append(localDecimal); output.insert(0, digit);
} else { j--;
output.append(pt); } else if (c == '0' && power == null) {
} output.insert(0, '0');
}
// start at the decimal point and fill in the numbers to the right, } else if (c == ',') {
// working our way from left to right // only add the grouping separator if we have more numbers
i = separator + 1; if (j >= 0 || (i > 0 && format.charAt(i - 1) == '0')) {
j = unscaled.length() - number.scale(); output.insert(0, c);
for (; i < format.length(); i++) { }
char c = format.charAt(i); } else if (c == 'G' || c == 'g') {
maxLength++; // only add the grouping separator if we have more numbers
if (c == '9' || c == '0') { if (j >= 0 || (i > 0 && format.charAt(i - 1) == '0')) {
if (j < unscaled.length()) { output.insert(0, localGrouping);
char digit = unscaled.charAt(j); }
output.append(digit); } else if (c == 'C' || c == 'c') {
j++; Currency currency = Currency.getInstance(Locale.getDefault());
} else { output.insert(0, currency.getCurrencyCode());
if (c == '0' || fillMode) { maxLength += 6;
output.append('0'); } else if (c == 'L' || c == 'l' || c == 'U' || c == 'u') {
} Currency currency = Currency.getInstance(Locale.getDefault());
} output.insert(0, currency.getSymbol());
} else { maxLength += 9;
throw DbException.get(ErrorCode.INVALID_TO_CHAR_FORMAT, originalFormat); } else if (c == '$') {
} Currency currency = Currency.getInstance(Locale.getDefault());
} String cs = currency.getSymbol();
} output.insert(0, cs);
} else {
addSign(output, number.signum(), leadingSign, trailingSign, trailingMinus, angleBrackets, fillMode); throw DbException.get(ErrorCode.INVALID_TO_CHAR_FORMAT, originalFormat);
}
if (power != null) { }
output.append('E');
output.append(power < 0 ? '-' : '+'); // if the format (to the left of the decimal point) was too small
output.append(Math.abs(power) < 10 ? "0" : ""); // to hold the number, return a big "######" string
output.append(Math.abs(power)); if (j >= 0) {
} return StringUtils.pad("", format.length() + 1, "#", true);
}
if (fillMode) {
if (power != null) { if (separator < format.length()) {
output.insert(0, ' ');
} else { // add the decimal point
while (output.length() < maxLength) { maxLength++;
output.insert(0, ' '); char pt = format.charAt(separator);
} if (pt == 'd' || pt == 'D') {
} output.append(localDecimal);
} } else {
output.append(pt);
return output.toString(); }
}
// start at the decimal point and fill in the numbers to the right,
private static void addSign(StringBuilder output, int signum, boolean leadingSign, boolean trailingSign, // working our way from left to right
boolean trailingMinus, boolean angleBrackets, boolean fillMode) { i = separator + 1;
if (angleBrackets) { j = unscaled.length() - number.scale();
if (signum < 0) { for (; i < format.length(); i++) {
output.insert(0, '<'); char c = format.charAt(i);
output.append('>'); maxLength++;
} else if (fillMode) { if (c == '9' || c == '0') {
output.insert(0, ' '); if (j < unscaled.length()) {
output.append(' '); char digit = unscaled.charAt(j);
} output.append(digit);
} else { j++;
String sign; } else {
if (signum == 0) { if (c == '0' || fillMode) {
sign = ""; output.append('0');
} else if (signum < 0) { }
sign = "-"; }
} else { } else {
if (leadingSign || trailingSign) { throw DbException.get(ErrorCode.INVALID_TO_CHAR_FORMAT, originalFormat);
sign = "+"; }
} else if (fillMode) { }
sign = " "; }
} else {
sign = ""; addSign(output, number.signum(), leadingSign, trailingSign, trailingMinus, angleBrackets, fillMode);
}
} if (power != null) {
if (trailingMinus || trailingSign) { output.append('E');
output.append(sign); output.append(power < 0 ? '-' : '+');
} else { output.append(Math.abs(power) < 10 ? "0" : "");
output.insert(0, sign); output.append(Math.abs(power));
} }
}
} if (fillMode) {
if (power != null) {
private static int findDecimalSeparator(String format) { output.insert(0, ' ');
int index = format.indexOf('.'); } else {
if (index == -1) { while (output.length() < maxLength) {
index = format.indexOf('D'); output.insert(0, ' ');
if (index == -1) { }
index = format.indexOf('d'); }
if (index == -1) { }
index = format.length();
} return output.toString();
} }
}
return index; private static void addSign(StringBuilder output, int signum, boolean leadingSign, boolean trailingSign,
} boolean trailingMinus, boolean angleBrackets, boolean fillMode) {
if (angleBrackets) {
private static int calculateScale(String format, int separator) { if (signum < 0) {
int scale = 0; output.insert(0, '<');
for (int i = separator; i < format.length(); i++) { output.append('>');
char c = format.charAt(i); } else if (fillMode) {
if (c == '0' || c == '9') { output.insert(0, ' ');
scale++; output.append(' ');
} }
} } else {
return scale; String sign;
} if (signum == 0) {
sign = "";
private static String toRomanNumeral(int number) { } else if (signum < 0) {
int[] values = new int[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; sign = "-";
String[] numerals = new String[] { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; } else {
StringBuilder result = new StringBuilder(); if (leadingSign || trailingSign) {
for (int i = 0; i < values.length; i++) { sign = "+";
int value = values[i]; } else if (fillMode) {
String numeral = numerals[i]; sign = " ";
while (number >= value) { } else {
result.append(numeral); sign = "";
number -= value; }
} }
} if (trailingMinus || trailingSign) {
return result.toString(); output.append(sign);
} } else {
output.insert(0, sign);
private static String toHex(BigDecimal number, String format) { }
}
boolean fillMode = !format.toUpperCase().startsWith("FM"); }
boolean uppercase = !format.contains("x");
boolean zeroPadded = format.startsWith("0"); private static int findDecimalSeparator(String format) {
int digits = 0; int index = format.indexOf('.');
for (int i = 0; i < format.length(); i++) { if (index == -1) {
char c = format.charAt(i); index = format.indexOf('D');
if (c == '0' || c == 'X' || c == 'x') { if (index == -1) {
digits++; index = format.indexOf('d');
} if (index == -1) {
} index = format.length();
}
int i = number.setScale(0, BigDecimal.ROUND_HALF_UP).intValue(); }
String hex = Integer.toHexString(i); }
if (digits < hex.length()) { return index;
hex = StringUtils.pad("", digits + 1, "#", true); }
} else {
if (uppercase) { private static int calculateScale(String format, int separator) {
hex = hex.toUpperCase(); int scale = 0;
} for (int i = separator; i < format.length(); i++) {
if (zeroPadded) { char c = format.charAt(i);
hex = StringUtils.pad(hex, digits, "0", false); if (c == '0' || c == '9') {
} scale++;
if (fillMode) { }
hex = StringUtils.pad(hex, format.length() + 1, " ", false); }
} return scale;
} }
return hex; private static String toRomanNumeral(int number) {
} int[] values = new int[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
String[] numerals = new String[] { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
/** StringBuilder result = new StringBuilder();
* Emulates Oracle's TO_CHAR(datetime) function. for (int i = 0; i < values.length; i++) {
* int value = values[i];
* <p><table border="1"> String numeral = numerals[i];
* <tr><td><b>Input</b></td><td><b>Output</b></td><td><b>Closest {@link SimpleDateFormat} Equivalent</b></td></tr> while (number >= value) {
* <tr><td>- / , . ; : "text"</td><td>Reproduced verbatim.</td><td>'text'</td></tr> result.append(numeral);
* <tr><td>A.D. AD B.C. BC</td><td>Era designator, with or without periods.</td><td>G</td></tr> number -= value;
* <tr><td>A.M. AM P.M. PM</td><td>AM/PM marker.</td><td>a</td></tr> }
* <tr><td>CC SCC</td><td>Century.</td><td>None.</td></tr> }
* <tr><td>D</td><td>Day of week.</td><td>u</td></tr> return result.toString();
* <tr><td>DAY</td><td>Name of day.</td><td>EEEE</td></tr> }
* <tr><td>DY</td><td>Abbreviated day name.</td><td>EEE</td></tr>
* <tr><td>DD</td><td>Day of month.</td><td>d</td></tr> private static String toHex(BigDecimal number, String format) {
* <tr><td>DDD</td><td>Day of year.</td><td>D</td></tr>
* <tr><td>DL</td><td>Long date format.</td><td>EEEE, MMMM d, yyyy</td></tr> boolean fillMode = !format.toUpperCase().startsWith("FM");
* <tr><td>DS</td><td>Short date format.</td><td>MM/dd/yyyy</td></tr> boolean uppercase = !format.contains("x");
* <tr><td>E</td><td>Abbreviated era name (Japanese, Chinese, Thai)</td><td>None.</td></tr> boolean zeroPadded = format.startsWith("0");
* <tr><td>EE</td><td>Full era name (Japanese, Chinese, Thai)</td><td>None.</td></tr> int digits = 0;
* <tr><td>FF[1-9]</td><td>Fractional seconds.</td><td>S</td></tr> for (int i = 0; i < format.length(); i++) {
* <tr><td>FM</td><td>Returns values with no leading or trailing spaces.</td><td>None.</td></tr> char c = format.charAt(i);
* <tr><td>FX</td><td>Requires exact matches between character data and format model.</td><td>None.</td></tr> if (c == '0' || c == 'X' || c == 'x') {
* <tr><td>HH HH12</td><td>Hour in AM/PM (1-12).</td><td>hh</td></tr> digits++;
* <tr><td>HH24</td><td>Hour in day (0-23).</td><td>HH</td></tr> }
* <tr><td>IW</td><td>Week in year.</td><td>w</td></tr> }
* <tr><td>WW</td><td>Week in year.</td><td>w</td></tr>
* <tr><td>W</td><td>Week in month.</td><td>W</td></tr> int i = number.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
* <tr><td>IYYY IYY IY I</td><td>Last 4/3/2/1 digit(s) of ISO year.</td><td>yyyy yyy yy y</td></tr> String hex = Integer.toHexString(i);
* <tr><td>RRRR RR</td><td>Last 4/2 digits of year.</td><td>yyyy yy</td></tr> if (digits < hex.length()) {
* <tr><td>Y,YYY</td><td>Year with comma.</td><td>None.</td></tr> hex = StringUtils.pad("", digits + 1, "#", true);
* <tr><td>YEAR SYEAR</td><td>Year spelled out (S prefixes BC years with minus sign).</td><td>None.</td></tr> } else {
* <tr><td>YYYY SYYYY</td><td>4-digit year (S prefixes BC years with minus sign).</td><td>yyyy</td></tr> if (uppercase) {
* <tr><td>YYY YY Y</td><td>Last 3/2/1 digit(s) of year.</td><td>yyy yy y</td></tr> hex = hex.toUpperCase();
* <tr><td>J</td><td>Julian day (number of days since January 1, 4712 BC).</td><td>None.</td></tr> }
* <tr><td>MI</td><td>Minute in hour.</td><td>mm</td></tr> if (zeroPadded) {
* <tr><td>MM</td><td>Month in year.</td><td>MM</td></tr> hex = StringUtils.pad(hex, digits, "0", false);
* <tr><td>MON</td><td>Abbreviated name of month.</td><td>MMM</td></tr> }
* <tr><td>MONTH</td><td>Name of month, padded with spaces.</td><td>MMMM</td></tr> if (fillMode) {
* <tr><td>RM</td><td>Roman numeral month (I-XII).</td><td>None.</td></tr> hex = StringUtils.pad(hex, format.length() + 1, " ", false);
* <tr><td>Q</td><td>Quarter of year.</td><td>None.</td></tr> }
* <tr><td>SS</td><td>Seconds in minute.</td><td>ss</td></tr> }
* <tr><td>SSSSS</td><td>Seconds in day.</td><td>None.</td></tr>
* <tr><td>TS</td><td>Short time format.</td><td>h:mm:ss aa</td></tr> return hex;
* <tr><td>TZD</td><td>Daylight savings time zone abbreviation.</td><td>z</td></tr> }
* <tr><td>TZR</td><td>Time zone region information.</td><td>zzzz</td></tr>
* <tr><td>X</td><td>Local radix character.</td><td>None.</td></tr> /**
* </table> * Emulates Oracle's TO_CHAR(datetime) function.
* *
* @param ts the timestamp to format * <p><table border="1">
* @param format the format pattern to use (if any) * <th><td>Input</td>
* @param nlsParam the NLS parameter (if any) * <td>Output</td>
* @return the formatted timestamp * <td>Closest {@link SimpleDateFormat} Equivalent</td></th>
* @see <a href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions180.htm">TO_CHAR(datetime)</a> * <tr><td>- / , . ; : "text"</td>
* @see <a href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34924">Datetime format models</a> * <td>Reproduced verbatim.</td>
*/ * <td>'text'</td></tr>
public static String toChar(Timestamp ts, String format, String nlsParam) { * <tr><td>A.D. AD B.C. BC</td>
* <td>Era designator, with or without periods.</td>
if (format == null) { * <td>G</td></tr>
format = "DD-MON-YY HH.MI.SS.FF PM"; * <tr><td>A.M. AM P.M. PM</td>
} * <td>AM/PM marker.</td>
* <td>a</td></tr>
GregorianCalendar cal = new GregorianCalendar(Locale.ENGLISH); * <tr><td>CC SCC</td>
cal.setTimeInMillis(ts.getTime()); * <td>Century.</td>
StringBuilder output = new StringBuilder(); * <td>None.</td></tr>
boolean fillMode = true; * <tr><td>D</td>
* <td>Day of week.</td>
for (int i = 0; i < format.length();) { * <td>u</td></tr>
* <tr><td>DAY</td>
Capitalization cap; * <td>Name of day.</td>
* <td>EEEE</td></tr>
// AD / BC * <tr><td>DY</td>
* <td>Abbreviated day name.</td>
if ((cap = containsAt(format, i, "A.D.", "B.C.")) != null) { * <td>EEE</td></tr>
String era = cal.get(Calendar.ERA) == GregorianCalendar.AD ? "A.D." : "B.C."; * <tr><td>DD</td>
output.append(cap.apply(era)); * <td>Day of month.</td>
i += 4; * <td>d</td></tr>
} else if ((cap = containsAt(format, i, "AD", "BC")) != null) { * <tr><td>DDD</td>
String era = cal.get(Calendar.ERA) == GregorianCalendar.AD ? "AD" : "BC"; * <td>Day of year.</td>
output.append(cap.apply(era)); * <td>D</td></tr>
i += 2; * <tr><td>DL</td>
* <td>Long date format.</td>
// AM / PM * <td>EEEE, MMMM d, yyyy</td></tr>
* <tr><td>DS</td>
} else if ((cap = containsAt(format, i, "A.M.", "P.M.")) != null) { * <td>Short date format.</td>
String am = cal.get(Calendar.AM_PM) == Calendar.AM ? "A.M." : "P.M."; * <td>MM/dd/yyyy</td></tr>
output.append(cap.apply(am)); * <tr><td>E</td>
i += 4; * <td>Abbreviated era name (Japanese, Chinese, Thai)</td>
} else if ((cap = containsAt(format, i, "AM", "PM")) != null) { * <td>None.</td></tr>
String am = cal.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM"; * <tr><td>EE</td>
output.append(cap.apply(am)); * <td>Full era name (Japanese, Chinese, Thai)</td>
i += 2; * <td>None.</td></tr>
* <tr><td>FF[1-9]</td>
// Long/short date/time format * <td>Fractional seconds.</td>
* <td>S</td></tr>
} else if ((cap = containsAt(format, i, "DL")) != null) { * <tr><td>FM</td>
output.append(new SimpleDateFormat("EEEE, MMMM d, yyyy").format(ts)); * <td>Returns values with no leading or trailing spaces.</td>
i += 2; * <td>None.</td></tr>
} else if ((cap = containsAt(format, i, "DS")) != null) { * <tr><td>FX</td>
output.append(new SimpleDateFormat("MM/dd/yyyy").format(ts)); * <td>Requires exact matches between character data and format model.</td>
i += 2; * <td>None.</td></tr>
} else if ((cap = containsAt(format, i, "TS")) != null) { * <tr><td>HH HH12</td>
output.append(new SimpleDateFormat("h:mm:ss aa").format(ts)); * <td>Hour in AM/PM (1-12).</td>
i += 2; * <td>hh</td></tr>
* <tr><td>HH24</td>
// Day * <td>Hour in day (0-23).</td>
* <td>HH</td></tr>
} else if ((cap = containsAt(format, i, "DDD")) != null) { * <tr><td>IW</td>
output.append(cal.get(Calendar.DAY_OF_YEAR)); * <td>Week in year.</td>
i += 3; * <td>w</td></tr>
} else if ((cap = containsAt(format, i, "DD")) != null) { * <tr><td>WW</td>
output.append(cal.get(Calendar.DAY_OF_MONTH)); * <td>Week in year.</td>
i += 2; * <td>w</td></tr>
} else if ((cap = containsAt(format, i, "DY")) != null) { * <tr><td>W</td>
String day = new SimpleDateFormat("EEE").format(ts).toUpperCase(); * <td>Week in month.</td>
output.append(cap.apply(day)); * <td>W</td></tr>
i += 2; * <tr><td>IYYY IYY IY I</td>
} else if ((cap = containsAt(format, i, "DAY")) != null) { * <td>Last 4/3/2/1 digit(s) of ISO year.</td>
String day = new SimpleDateFormat("EEEE").format(ts); * <td>yyyy yyy yy y</td></tr>
if (fillMode) { * <tr><td>RRRR RR</td>
day = StringUtils.pad(day, "Wednesday".length(), " ", true); * <td>Last 4/2 digits of year.</td>
} * <td>yyyy yy</td></tr>
output.append(cap.apply(day)); * <tr><td>Y,YYY</td>
i += 3; * <td>Year with comma.</td>
} else if ((cap = containsAt(format, i, "D")) != null) { * <td>None.</td></tr>
output.append(cal.get(Calendar.DAY_OF_WEEK)); * <tr><td>YEAR SYEAR</td>
i += 1; * <td>Year spelled out (S prefixes BC years with minus sign).</td>
} else if ((cap = containsAt(format, i, "J")) != null) { * <td>None.</td></tr>
long millis = ts.getTime() - JULIAN_EPOCH; * <tr><td>YYYY SYYYY</td>
long days = (long) Math.floor(millis / (1000 * 60 * 60 * 24)); * <td>4-digit year (S prefixes BC years with minus sign).</td>
output.append(days); * <td>yyyy</td></tr>
i += 1; * <tr><td>YYY YY Y</td>
* <td>Last 3/2/1 digit(s) of year.</td>
// Hours * <td>yyy yy y</td></tr>
* <tr><td>J</td>
} else if ((cap = containsAt(format, i, "HH24")) != null) { * <td>Julian day (number of days since January 1, 4712 BC).</td>
output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR_OF_DAY))); * <td>None.</td></tr>
i += 4; * <tr><td>MI</td>
} else if ((cap = containsAt(format, i, "HH12")) != null) { * <td>Minute in hour.</td>
output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR))); * <td>mm</td></tr>
i += 4; * <tr><td>MM</td>
} else if ((cap = containsAt(format, i, "HH")) != null) { * <td>Month in year.</td>
output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR))); * <td>MM</td></tr>
i += 2; * <tr><td>MON</td>
* <td>Abbreviated name of month.</td>
// Minutes * <td>MMM</td></tr>
* <tr><td>MONTH</td>
} else if ((cap = containsAt(format, i, "MI")) != null) { * <td>Name of month, padded with spaces.</td>
output.append(new DecimalFormat("00").format(cal.get(Calendar.MINUTE))); * <td>MMMM</td></tr>
i += 2; * <tr><td>RM</td>
* <td>Roman numeral month.</td>
// Seconds * <td>None.</td></tr>
* <tr><td>Q</td>
} else if ((cap = containsAt(format, i, "SSSSS")) != null) { * <td>Quarter of year.</td>
int seconds = cal.get(Calendar.HOUR_OF_DAY) * 60 * 60; * <td>None.</td></tr>
seconds += cal.get(Calendar.MINUTE) * 60; * <tr><td>SS</td>
seconds += cal.get(Calendar.SECOND); * <td>Seconds in minute.</td>
output.append(seconds); * <td>ss</td></tr>
i += 5; * <tr><td>SSSSS</td>
} else if ((cap = containsAt(format, i, "SS")) != null) { * <td>Seconds in day.</td>
output.append(new DecimalFormat("00").format(cal.get(Calendar.SECOND))); * <td>None.</td></tr>
i += 2; * <tr><td>TS</td>
* <td>Short time format.</td>
// Fractional seconds * <td>h:mm:ss aa</td></tr>
* <tr><td>TZD</td>
} else if ((cap = containsAt(format, i, "FF1", "FF2", "FF3", "FF4", "FF5", "FF6", "FF7", "FF8", "FF9")) != null) { * <td>Daylight savings time zone abbreviation.</td>
int x = Integer.parseInt(format.substring(i + 2, i + 3)); * <td>z</td></tr>
int ff = (int) (cal.get(Calendar.MILLISECOND) * Math.pow(10, x - 3)); * <tr><td>TZR</td>
output.append(ff); * <td>Time zone region information.</td>
i += 3; * <td>zzzz</td></tr>
} else if ((cap = containsAt(format, i, "FF")) != null) { * <tr><td>X</td>
output.append(cal.get(Calendar.MILLISECOND) * 1000); * <td>Local radix character.</td>
i += 2; * <td>None.</td></tr>
* </table>
// Time zone * <p>
* See also TO_CHAR(datetime) and datetime format models
} else if ((cap = containsAt(format, i, "TZR")) != null) { * in the Oracle documentation.
TimeZone tz = TimeZone.getDefault(); *
output.append(tz.getID()); * @param ts the timestamp to format
i += 3; * @param format the format pattern to use (if any)
} else if ((cap = containsAt(format, i, "TZD")) != null) { * @param nlsParam the NLS parameter (if any)
TimeZone tz = TimeZone.getDefault(); * @return the formatted timestamp
boolean daylight = tz.inDaylightTime(new java.util.Date()); */
output.append(tz.getDisplayName(daylight, TimeZone.SHORT)); public static String toChar(Timestamp ts, String format, String nlsParam) {
i += 3;
if (format == null) {
// Week format = "DD-MON-YY HH.MI.SS.FF PM";
}
} else if ((cap = containsAt(format, i, "IW", "WW")) != null) {
output.append(cal.get(Calendar.WEEK_OF_YEAR)); GregorianCalendar cal = new GregorianCalendar(Locale.ENGLISH);
i += 2; cal.setTimeInMillis(ts.getTime());
} else if ((cap = containsAt(format, i, "W")) != null) { StringBuilder output = new StringBuilder();
int w = (int) (1 + Math.floor(cal.get(Calendar.DAY_OF_MONTH) / 7)); boolean fillMode = true;
output.append(w);
i += 1; for (int i = 0; i < format.length();) {
// Year Capitalization cap;
} else if ((cap = containsAt(format, i, "Y,YYY")) != null) { // AD / BC
output.append(new DecimalFormat("#,###").format(getYear(cal)));
i += 5; if ((cap = containsAt(format, i, "A.D.", "B.C.")) != null) {
} else if ((cap = containsAt(format, i, "SYYYY")) != null) { String era = cal.get(Calendar.ERA) == GregorianCalendar.AD ? "A.D." : "B.C.";
if (cal.get(Calendar.ERA) == GregorianCalendar.BC) { output.append(cap.apply(era));
output.append('-'); i += 4;
} } else if ((cap = containsAt(format, i, "AD", "BC")) != null) {
output.append(new DecimalFormat("0000").format(getYear(cal))); String era = cal.get(Calendar.ERA) == GregorianCalendar.AD ? "AD" : "BC";
i += 5; output.append(cap.apply(era));
} else if ((cap = containsAt(format, i, "YYYY", "IYYY", "RRRR")) != null) { i += 2;
output.append(new DecimalFormat("0000").format(getYear(cal)));
i += 4; // AM / PM
} else if ((cap = containsAt(format, i, "YYY", "IYY")) != null) {
output.append(new DecimalFormat("000").format(getYear(cal) % 1000)); } else if ((cap = containsAt(format, i, "A.M.", "P.M.")) != null) {
i += 3; String am = cal.get(Calendar.AM_PM) == Calendar.AM ? "A.M." : "P.M.";
} else if ((cap = containsAt(format, i, "YY", "IY", "RR")) != null) { output.append(cap.apply(am));
output.append(new DecimalFormat("00").format(getYear(cal) % 100)); i += 4;
i += 2; } else if ((cap = containsAt(format, i, "AM", "PM")) != null) {
} else if ((cap = containsAt(format, i, "I", "Y")) != null) { String am = cal.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM";
output.append(getYear(cal) % 10); output.append(cap.apply(am));
i += 1; i += 2;
// Month / quarter // Long/short date/time format
} else if ((cap = containsAt(format, i, "MONTH")) != null) { } else if ((cap = containsAt(format, i, "DL")) != null) {
String month = new SimpleDateFormat("MMMM").format(ts); output.append(new SimpleDateFormat("EEEE, MMMM d, yyyy").format(ts));
if (fillMode) { i += 2;
month = StringUtils.pad(month, "September".length(), " ", true); } else if ((cap = containsAt(format, i, "DS")) != null) {
} output.append(new SimpleDateFormat("MM/dd/yyyy").format(ts));
output.append(cap.apply(month)); i += 2;
i += 5; } else if ((cap = containsAt(format, i, "TS")) != null) {
} else if ((cap = containsAt(format, i, "MON")) != null) { output.append(new SimpleDateFormat("h:mm:ss aa").format(ts));
String month = new SimpleDateFormat("MMM").format(ts); i += 2;
output.append(cap.apply(month));
i += 3; // Day
} else if ((cap = containsAt(format, i, "MM")) != null) {
output.append(cal.get(Calendar.MONTH) + 1); } else if ((cap = containsAt(format, i, "DDD")) != null) {
i += 2; output.append(cal.get(Calendar.DAY_OF_YEAR));
} else if ((cap = containsAt(format, i, "RM")) != null) { i += 3;
int month = cal.get(Calendar.MONTH) + 1; } else if ((cap = containsAt(format, i, "DD")) != null) {
output.append(cap.apply(toRomanNumeral(month))); output.append(cal.get(Calendar.DAY_OF_MONTH));
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "Q")) != null) { } else if ((cap = containsAt(format, i, "DY")) != null) {
int q = (int) (1 + Math.floor(cal.get(Calendar.MONTH) / 3)); String day = new SimpleDateFormat("EEE").format(ts).toUpperCase();
output.append(q); output.append(cap.apply(day));
i += 1; i += 2;
} else if ((cap = containsAt(format, i, "DAY")) != null) {
// Local radix character String day = new SimpleDateFormat("EEEE").format(ts);
if (fillMode) {
} else if ((cap = containsAt(format, i, "X")) != null) { day = StringUtils.pad(day, "Wednesday".length(), " ", true);
char c = DecimalFormatSymbols.getInstance().getDecimalSeparator(); }
output.append(c); output.append(cap.apply(day));
i += 1; i += 3;
} else if ((cap = containsAt(format, i, "D")) != null) {
// Format modifiers output.append(cal.get(Calendar.DAY_OF_WEEK));
i += 1;
} else if ((cap = containsAt(format, i, "FM")) != null) { } else if ((cap = containsAt(format, i, "J")) != null) {
fillMode = !fillMode; long millis = ts.getTime() - JULIAN_EPOCH;
i += 2; long days = (long) Math.floor(millis / (1000 * 60 * 60 * 24));
} else if ((cap = containsAt(format, i, "FX")) != null) { output.append(days);
i += 2; i += 1;
// Literal text // Hours
} else if ((cap = containsAt(format, i, "\"")) != null) { } else if ((cap = containsAt(format, i, "HH24")) != null) {
for (i = i + 1; i < format.length(); i++) { output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR_OF_DAY)));
char c = format.charAt(i); i += 4;
if (c != '"') { } else if ((cap = containsAt(format, i, "HH12")) != null) {
output.append(c); output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR)));
} else { i += 4;
i++; } else if ((cap = containsAt(format, i, "HH")) != null) {
break; output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR)));
} i += 2;
}
} else if (format.charAt(i) == '-' || format.charAt(i) == '/' || format.charAt(i) == ',' // Minutes
|| format.charAt(i) == '.' || format.charAt(i) == ';' || format.charAt(i) == ':'
|| format.charAt(i) == ' ') { } else if ((cap = containsAt(format, i, "MI")) != null) {
output.append(format.charAt(i)); output.append(new DecimalFormat("00").format(cal.get(Calendar.MINUTE)));
i += 1; i += 2;
// Anything else // Seconds
} else { } else if ((cap = containsAt(format, i, "SSSSS")) != null) {
throw DbException.get(ErrorCode.INVALID_TO_CHAR_FORMAT, format); int seconds = cal.get(Calendar.HOUR_OF_DAY) * 60 * 60;
} seconds += cal.get(Calendar.MINUTE) * 60;
} seconds += cal.get(Calendar.SECOND);
output.append(seconds);
return output.toString(); i += 5;
} } else if ((cap = containsAt(format, i, "SS")) != null) {
output.append(new DecimalFormat("00").format(cal.get(Calendar.SECOND)));
private static int getYear(Calendar cal) { i += 2;
int year = cal.get(Calendar.YEAR);
if (cal.get(Calendar.ERA) == GregorianCalendar.BC) { // Fractional seconds
year--;
} } else if ((cap = containsAt(format, i, "FF1", "FF2", "FF3", "FF4", "FF5", "FF6", "FF7", "FF8", "FF9")) != null) {
return year; int x = Integer.parseInt(format.substring(i + 2, i + 3));
} int ff = (int) (cal.get(Calendar.MILLISECOND) * Math.pow(10, x - 3));
output.append(ff);
/** i += 3;
* Returns a capitalization strategy if the specified string contains any of the specified substrings at the } else if ((cap = containsAt(format, i, "FF")) != null) {
* specified index. The capitalization strategy indicates the casing of the substring that was found. If none output.append(cal.get(Calendar.MILLISECOND) * 1000);
* of the specified substrings are found, this method returns <code>null</code>. i += 2;
*
* @param s the string to check // Time zone
* @param index the index to check at
* @param substrings the substrings to check for within the string } else if ((cap = containsAt(format, i, "TZR")) != null) {
* @return a capitalization strategy if the specified string contains any of the specified substrings at the TimeZone tz = TimeZone.getDefault();
* specified index, <code>null</code> otherwise output.append(tz.getID());
*/ i += 3;
private static Capitalization containsAt(String s, int index, String... substrings) { } else if ((cap = containsAt(format, i, "TZD")) != null) {
for (String substring : substrings) { TimeZone tz = TimeZone.getDefault();
if (index + substring.length() <= s.length()) { boolean daylight = tz.inDaylightTime(new java.util.Date());
boolean found = true; output.append(tz.getDisplayName(daylight, TimeZone.SHORT));
Boolean up1 = null; i += 3;
Boolean up2 = null;
for (int i = 0; i < substring.length(); i++) { // Week
char c1 = s.charAt(index + i);
char c2 = substring.charAt(i); } else if ((cap = containsAt(format, i, "IW", "WW")) != null) {
if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) { output.append(cal.get(Calendar.WEEK_OF_YEAR));
found = false; i += 2;
break; } else if ((cap = containsAt(format, i, "W")) != null) {
} else if (Character.isLetter(c1)) { int w = (int) (1 + Math.floor(cal.get(Calendar.DAY_OF_MONTH) / 7));
if (up1 == null) { output.append(w);
up1 = Character.isUpperCase(c1); i += 1;
} else if (up2 == null) {
up2 = Character.isUpperCase(c1); // Year
}
} } else if ((cap = containsAt(format, i, "Y,YYY")) != null) {
} output.append(new DecimalFormat("#,###").format(getYear(cal)));
if (found) { i += 5;
return Capitalization.toCapitalization(up1, up2); } else if ((cap = containsAt(format, i, "SYYYY")) != null) {
} if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
} output.append('-');
} }
return null; output.append(new DecimalFormat("0000").format(getYear(cal)));
} i += 5;
} else if ((cap = containsAt(format, i, "YYYY", "IYYY", "RRRR")) != null) {
/** Represents a capitalization / casing strategy. */ output.append(new DecimalFormat("0000").format(getYear(cal)));
private enum Capitalization { i += 4;
} else if ((cap = containsAt(format, i, "YYY", "IYY")) != null) {
/** All letters are uppercased. */ output.append(new DecimalFormat("000").format(getYear(cal) % 1000));
UPPERCASE, i += 3;
} else if ((cap = containsAt(format, i, "YY", "IY", "RR")) != null) {
/** All letters are lowercased. */ output.append(new DecimalFormat("00").format(getYear(cal) % 100));
LOWERCASE, i += 2;
} else if ((cap = containsAt(format, i, "I", "Y")) != null) {
/** The string is capitalized (first letter uppercased, subsequent letters lowercased). */ output.append(getYear(cal) % 10);
CAPITALIZE; i += 1;
/** // Month / quarter
* Returns the capitalization / casing strategy which should be used when the first and second letters have
* the specified casing. } else if ((cap = containsAt(format, i, "MONTH")) != null) {
* String month = new SimpleDateFormat("MMMM").format(ts);
* @param up1 whether or not the first letter is uppercased if (fillMode) {
* @param up2 whether or not the second letter is uppercased month = StringUtils.pad(month, "September".length(), " ", true);
* @return the capitalization / casing strategy which should be used when the first and second letters have }
* the specified casing output.append(cap.apply(month));
*/ i += 5;
public static Capitalization toCapitalization(Boolean up1, Boolean up2) { } else if ((cap = containsAt(format, i, "MON")) != null) {
if (up1 == null) { String month = new SimpleDateFormat("MMM").format(ts);
return Capitalization.CAPITALIZE; output.append(cap.apply(month));
} else if (up2 == null) { i += 3;
return up1 ? Capitalization.UPPERCASE : Capitalization.LOWERCASE; } else if ((cap = containsAt(format, i, "MM")) != null) {
} else if (up1) { output.append(cal.get(Calendar.MONTH) + 1);
return up2 ? Capitalization.UPPERCASE : Capitalization.CAPITALIZE; i += 2;
} else { } else if ((cap = containsAt(format, i, "RM")) != null) {
return Capitalization.LOWERCASE; int month = cal.get(Calendar.MONTH) + 1;
} output.append(cap.apply(toRomanNumeral(month)));
} i += 2;
} else if ((cap = containsAt(format, i, "Q")) != null) {
/** int q = (int) (1 + Math.floor(cal.get(Calendar.MONTH) / 3));
* Applies this capitalization strategy to the specified string. output.append(q);
* i += 1;
* @param s the string to apply this strategy to
* @return the resultant string // Local radix character
*/
public String apply(String s) { } else if ((cap = containsAt(format, i, "X")) != null) {
if (s == null || s.isEmpty()) { char c = DecimalFormatSymbols.getInstance().getDecimalSeparator();
return s; output.append(c);
} i += 1;
switch (this) {
case UPPERCASE: // Format modifiers
return s.toUpperCase();
case LOWERCASE: } else if ((cap = containsAt(format, i, "FM")) != null) {
return s.toLowerCase(); fillMode = !fillMode;
case CAPITALIZE: i += 2;
return Character.toUpperCase(s.charAt(0)) + (s.length() > 1 ? s.toLowerCase().substring(1) : ""); } else if ((cap = containsAt(format, i, "FX")) != null) {
default: i += 2;
throw new IllegalArgumentException("Unknown capitalization strategy: " + this);
} // Literal text
}
} } else if ((cap = containsAt(format, i, "\"")) != null) {
} for (i = i + 1; i < format.length(); i++) {
char c = format.charAt(i);
if (c != '"') {
output.append(c);
} else {
i++;
break;
}
}
} else if (format.charAt(i) == '-' || format.charAt(i) == '/' || format.charAt(i) == ','
|| format.charAt(i) == '.' || format.charAt(i) == ';' || format.charAt(i) == ':'
|| format.charAt(i) == ' ') {
output.append(format.charAt(i));
i += 1;
// Anything else
} else {
throw DbException.get(ErrorCode.INVALID_TO_CHAR_FORMAT, format);
}
}
return output.toString();
}
private static int getYear(Calendar cal) {
int year = cal.get(Calendar.YEAR);
if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
year--;
}
return year;
}
/**
* Returns a capitalization strategy if the specified string contains any of
* the specified substrings at the specified index. The capitalization
* strategy indicates the casing of the substring that was found. If none of
* the specified substrings are found, this method returns <code>null</code>
* .
*
* @param s the string to check
* @param index the index to check at
* @param substrings the substrings to check for within the string
* @return a capitalization strategy if the specified string contains any of
* the specified substrings at the specified index,
* <code>null</code> otherwise
*/
private static Capitalization containsAt(String s, int index, String... substrings) {
for (String substring : substrings) {
if (index + substring.length() <= s.length()) {
boolean found = true;
Boolean up1 = null;
Boolean up2 = null;
for (int i = 0; i < substring.length(); i++) {
char c1 = s.charAt(index + i);
char c2 = substring.charAt(i);
if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) {
found = false;
break;
} else if (Character.isLetter(c1)) {
if (up1 == null) {
up1 = Character.isUpperCase(c1);
} else if (up2 == null) {
up2 = Character.isUpperCase(c1);
}
}
}
if (found) {
return Capitalization.toCapitalization(up1, up2);
}
}
}
return null;
}
/** Represents a capitalization / casing strategy. */
private enum Capitalization {
/**
* All letters are uppercased.
*/
UPPERCASE,
/**
* All letters are lowercased.
*/
LOWERCASE,
/**
* The string is capitalized (first letter uppercased, subsequent
* letters lowercased).
*/
CAPITALIZE;
/**
* Returns the capitalization / casing strategy which should be used
* when the first and second letters have the specified casing.
*
* @param up1 whether or not the first letter is uppercased
* @param up2 whether or not the second letter is uppercased
* @return the capitalization / casing strategy which should be used
* when the first and second letters have the specified casing
*/
public static Capitalization toCapitalization(Boolean up1, Boolean up2) {
if (up1 == null) {
return Capitalization.CAPITALIZE;
} else if (up2 == null) {
return up1 ? Capitalization.UPPERCASE : Capitalization.LOWERCASE;
} else if (up1) {
return up2 ? Capitalization.UPPERCASE : Capitalization.CAPITALIZE;
} else {
return Capitalization.LOWERCASE;
}
}
/**
* Applies this capitalization strategy to the specified string.
*
* @param s the string to apply this strategy to
* @return the resultant string
*/
public String apply(String s) {
if (s == null || s.isEmpty()) {
return s;
}
switch (this) {
case UPPERCASE:
return s.toUpperCase();
case LOWERCASE:
return s.toLowerCase();
case CAPITALIZE:
return Character.toUpperCase(s.charAt(0)) + (s.length() > 1 ? s.toLowerCase().substring(1) : "");
default:
throw new IllegalArgumentException("Unknown capitalization strategy: " + this);
}
}
}
}
...@@ -63,8 +63,8 @@ public class CompareMode { ...@@ -63,8 +63,8 @@ public class CompareMode {
private final String name; private final String name;
private final int strength; private final int strength;
/** /**
* If true, sort BINARY columns as if they contain unsigned bytes. * If true, sort BINARY columns as if they contain unsigned bytes.
*/ */
private final boolean binaryUnsigned; private final boolean binaryUnsigned;
...@@ -230,7 +230,7 @@ public class CompareMode { ...@@ -230,7 +230,7 @@ public class CompareMode {
public boolean isBinaryUnsigned() { public boolean isBinaryUnsigned() {
return binaryUnsigned; return binaryUnsigned;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == this) { if (obj == this) {
......
...@@ -739,7 +739,7 @@ public class DataType { ...@@ -739,7 +739,7 @@ public class DataType {
/** /**
* Convert a SQL type to a value type using SQL type name, in order to * Convert a SQL type to a value type using SQL type name, in order to
* manage SQL type extension mechanism. * manage SQL type extension mechanism.
* *
* @param sqlType the SQL type * @param sqlType the SQL type
* @param sqlTypeName the SQL type name * @param sqlTypeName the SQL type name
* @return the value type * @return the value type
...@@ -754,16 +754,16 @@ public class DataType { ...@@ -754,16 +754,16 @@ public class DataType {
} }
return convertSQLTypeToValueType(sqlType); return convertSQLTypeToValueType(sqlType);
} }
/** /**
* Get the SQL type from the result set meta data for the given column. This * Get the SQL type from the result set meta data for the given column. This
* method uses the SQL type and type name. * method uses the SQL type and type name.
* *
* @param meta the meta data * @param meta the meta data
* @param columnIndex the column index (1, 2,...) * @param columnIndex the column index (1, 2,...)
* @return the value type * @return the value type
*/ */
public static int getValueTypeFromResultSet(ResultSetMetaData meta, int columnIndex) public static int getValueTypeFromResultSet(ResultSetMetaData meta, int columnIndex)
throws SQLException { throws SQLException {
return convertSQLTypeToValueType( return convertSQLTypeToValueType(
meta.getColumnType(columnIndex), meta.getColumnType(columnIndex),
......
...@@ -37,13 +37,13 @@ public class ValueGeometry extends Value { ...@@ -37,13 +37,13 @@ public class ValueGeometry extends Value {
* cost a significant amount of cpu cycles. * cost a significant amount of cpu cycles.
*/ */
private Geometry geometry; private Geometry geometry;
/** /**
* As conversion from/to WKB cost a significant amount of cpu cycles, WKB * As conversion from/to WKB cost a significant amount of cpu cycles, WKB
* are kept in ValueGeometry instance * are kept in ValueGeometry instance
*/ */
private byte[] bytes; private byte[] bytes;
private int hashCode; private int hashCode;
private ValueGeometry(Geometry geometry) { private ValueGeometry(Geometry geometry) {
......
...@@ -86,7 +86,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo ...@@ -86,7 +86,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
this.fileName = null; this.fileName = null;
this.tempFile = null; this.tempFile = null;
} }
/** /**
* Create temporary CLOB from Reader. * Create temporary CLOB from Reader.
*/ */
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论