提交 bd628341 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Reimplement date-time variant of TO_CHAR without a Calendar

上级 22ed525f
...@@ -1449,7 +1449,8 @@ public class Function extends Expression implements FunctionCall { ...@@ -1449,7 +1449,8 @@ public class Function extends Expression implements FunctionCall {
case Value.TIME: case Value.TIME:
case Value.DATE: case Value.DATE:
case Value.TIMESTAMP: case Value.TIMESTAMP:
result = ValueString.get(ToChar.toChar(v0.getTimestamp(), case Value.TIMESTAMP_TZ:
result = ValueString.get(ToChar.toCharDateTime(v0,
v1 == null ? null : v1.getString(), v1 == null ? null : v1.getString(),
v2 == null ? null : v2.getString()), v2 == null ? null : v2.getString()),
database.getMode().treatEmptyStringsAsNull); database.getMode().treatEmptyStringsAsNull);
......
...@@ -6,20 +6,18 @@ ...@@ -6,20 +6,18 @@
package org.h2.util; package org.h2.util;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Date; import java.text.DateFormatSymbols;
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.Arrays; import java.util.Arrays;
import java.util.Calendar;
import java.util.Currency; import java.util.Currency;
import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.value.Value;
/** /**
* Emulates Oracle's TO_CHAR function. * Emulates Oracle's TO_CHAR function.
...@@ -29,7 +27,7 @@ public class ToChar { ...@@ -29,7 +27,7 @@ public class ToChar {
/** /**
* The beginning of the Julian calendar. * The beginning of the Julian calendar.
*/ */
private static final long JULIAN_EPOCH; private static final int JULIAN_EPOCH = -2_440_588;
private static final int[] ROMAN_VALUES = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, private static final int[] ROMAN_VALUES = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9,
5, 4, 1 }; 5, 4, 1 };
...@@ -37,15 +35,6 @@ public class ToChar { ...@@ -37,15 +35,6 @@ public class ToChar {
private static final String[] ROMAN_NUMERALS = { "M", "CM", "D", "CD", "C", "XC", private static final String[] ROMAN_NUMERALS = { "M", "CM", "D", "CD", "C", "XC",
"L", "XL", "X", "IX", "V", "IV", "I" }; "L", "XL", "X", "IX", "V", "IV", "I" };
static {
GregorianCalendar epoch = new GregorianCalendar(Locale.ENGLISH);
epoch.setGregorianChange(new Date(Long.MAX_VALUE));
epoch.clear();
epoch.set(4713, Calendar.JANUARY, 1, 0, 0, 0);
epoch.set(Calendar.ERA, GregorianCalendar.BC);
JULIAN_EPOCH = epoch.getTimeInMillis();
}
private ToChar() { private ToChar() {
// utility class // utility class
} }
...@@ -592,22 +581,35 @@ public class ToChar { ...@@ -592,22 +581,35 @@ public class ToChar {
* See also TO_CHAR(datetime) and datetime format models * See also TO_CHAR(datetime) and datetime format models
* in the Oracle documentation. * in the Oracle documentation.
* *
* @param ts the timestamp to format * @param value the date-time value to format
* @param format the format pattern to use (if any) * @param format the format pattern to use (if any)
* @param nlsParam the NLS parameter (if any) * @param nlsParam the NLS parameter (if any)
* @return the formatted timestamp * @return the formatted timestamp
*/ */
public static String toChar(Timestamp ts, String format, @SuppressWarnings("unused") String nlsParam) { public static String toCharDateTime(Value value, String format, @SuppressWarnings("unused") String nlsParam) {
long[] a = DateTimeUtils.dateAndTimeFromValue(value);
long dateValue = a[0];
long timeNanos = a[1];
int year = DateTimeUtils.yearFromDateValue(dateValue);
int monthOfYear = DateTimeUtils.monthFromDateValue(dateValue);
int dayOfMonth = DateTimeUtils.dayFromDateValue(dateValue);
long second = timeNanos / 1_000_000_000;
int nanos = (int) (timeNanos - second * 1_000_000_000);
int minute = (int) (second / 60);
second -= minute * 60;
int hour = minute / 60;
minute -= hour * 60;
int h12 = (hour + 11) % 12 + 1;
boolean isAM = hour < 12;
if (format == null) { if (format == null) {
format = "DD-MON-YY HH.MI.SS.FF PM"; format = "DD-MON-YY HH.MI.SS.FF PM";
} }
GregorianCalendar cal = new GregorianCalendar(Locale.ENGLISH);
cal.setTimeInMillis(ts.getTime());
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
boolean fillMode = true; boolean fillMode = true;
DateFormatSymbols dfs = null;
for (int i = 0; i < format.length();) { for (int i = 0; i < format.length();) {
Capitalization cap; Capitalization cap;
...@@ -615,94 +617,114 @@ public class ToChar { ...@@ -615,94 +617,114 @@ public class ToChar {
// AD / BC // AD / BC
if ((cap = containsAt(format, i, "A.D.", "B.C.")) != null) { if ((cap = containsAt(format, i, "A.D.", "B.C.")) != null) {
String era = cal.get(Calendar.ERA) == GregorianCalendar.AD ? "A.D." : "B.C."; String era = year > 0 ? "A.D." : "B.C.";
output.append(cap.apply(era)); output.append(cap.apply(era));
i += 4; i += 4;
} else if ((cap = containsAt(format, i, "AD", "BC")) != null) { } else if ((cap = containsAt(format, i, "AD", "BC")) != null) {
String era = cal.get(Calendar.ERA) == GregorianCalendar.AD ? "AD" : "BC"; String era = year > 0 ? "AD" : "BC";
output.append(cap.apply(era)); output.append(cap.apply(era));
i += 2; i += 2;
// AM / PM // AM / PM
} else if ((cap = containsAt(format, i, "A.M.", "P.M.")) != null) { } else if ((cap = containsAt(format, i, "A.M.", "P.M.")) != null) {
String am = cal.get(Calendar.AM_PM) == Calendar.AM ? "A.M." : "P.M."; String am = isAM ? "A.M." : "P.M.";
output.append(cap.apply(am)); output.append(cap.apply(am));
i += 4; i += 4;
} else if ((cap = containsAt(format, i, "AM", "PM")) != null) { } else if ((cap = containsAt(format, i, "AM", "PM")) != null) {
String am = cal.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM"; String am = isAM ? "AM" : "PM";
output.append(cap.apply(am)); output.append(cap.apply(am));
i += 2; i += 2;
// Long/short date/time format // Long/short date/time format
} else if ((cap = containsAt(format, i, "DL")) != null) { } else if ((cap = containsAt(format, i, "DL")) != null) {
output.append(new SimpleDateFormat("EEEE, MMMM d, yyyy").format(ts)); if (dfs == null) {
dfs = DateFormatSymbols.getInstance();
}
String day = dfs.getWeekdays()[DateTimeUtils.getSundayDayOfWeek(dateValue)];
String month = dfs.getMonths()[monthOfYear - 1];
output.append(day).append(", ").append(month).append(' ').append(dayOfMonth).append(", ");
StringUtils.appendZeroPadded(output, 4, Math.abs(year));
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "DS")) != null) { } else if ((cap = containsAt(format, i, "DS")) != null) {
output.append(new SimpleDateFormat("MM/dd/yyyy").format(ts)); StringUtils.appendZeroPadded(output, 2, monthOfYear);
output.append('/');
StringUtils.appendZeroPadded(output, 2, dayOfMonth);
output.append('/');
StringUtils.appendZeroPadded(output, 4, Math.abs(year));
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "TS")) != null) { } else if ((cap = containsAt(format, i, "TS")) != null) {
output.append(new SimpleDateFormat("h:mm:ss aa").format(ts)); if (dfs == null) {
dfs = DateFormatSymbols.getInstance();
}
output.append(h12).append(':');
StringUtils.appendZeroPadded(output, 2, minute);
output.append(':');
StringUtils.appendZeroPadded(output, 2, second);
output.append(' ');
output.append(dfs.getAmPmStrings()[isAM ? 0 : 1]);
i += 2; i += 2;
// Day // Day
} else if ((cap = containsAt(format, i, "DDD")) != null) { } else if ((cap = containsAt(format, i, "DDD")) != null) {
output.append(cal.get(Calendar.DAY_OF_YEAR)); output.append(DateTimeUtils.getDayOfYear(dateValue));
i += 3; i += 3;
} else if ((cap = containsAt(format, i, "DD")) != null) { } else if ((cap = containsAt(format, i, "DD")) != null) {
output.append(String.format("%02d", output.append(String.format("%02d",
cal.get(Calendar.DAY_OF_MONTH))); dayOfMonth));
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "DY")) != null) { } else if ((cap = containsAt(format, i, "DY")) != null) {
String day = new SimpleDateFormat("EEE").format(ts); if (dfs == null) {
dfs = DateFormatSymbols.getInstance();
}
String day = dfs.getShortWeekdays()[DateTimeUtils.getSundayDayOfWeek(dateValue)];
output.append(cap.apply(day)); output.append(cap.apply(day));
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "DAY")) != null) { } else if ((cap = containsAt(format, i, "DAY")) != null) {
String day = new SimpleDateFormat("EEEE").format(ts); if (dfs == null) {
dfs = DateFormatSymbols.getInstance();
}
String day = dfs.getWeekdays()[DateTimeUtils.getSundayDayOfWeek(dateValue)];
if (fillMode) { if (fillMode) {
day = StringUtils.pad(day, "Wednesday".length(), " ", true); day = StringUtils.pad(day, "Wednesday".length(), " ", true);
} }
output.append(cap.apply(day)); output.append(cap.apply(day));
i += 3; i += 3;
} else if ((cap = containsAt(format, i, "D")) != null) { } else if ((cap = containsAt(format, i, "D")) != null) {
output.append(cal.get(Calendar.DAY_OF_WEEK)); output.append(DateTimeUtils.getSundayDayOfWeek(dateValue));
i += 1; i += 1;
} else if ((cap = containsAt(format, i, "J")) != null) { } else if ((cap = containsAt(format, i, "J")) != null) {
long millis = ts.getTime() - JULIAN_EPOCH; output.append(DateTimeUtils.absoluteDayFromDateValue(dateValue) - JULIAN_EPOCH);
long days = (long) Math.floor(millis / (1000 * 60 * 60 * 24));
output.append(days);
i += 1; i += 1;
// Hours // Hours
} else if ((cap = containsAt(format, i, "HH24")) != null) { } else if ((cap = containsAt(format, i, "HH24")) != null) {
output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR_OF_DAY))); StringUtils.appendZeroPadded(output, 2, hour);
i += 4; i += 4;
} else if ((cap = containsAt(format, i, "HH12")) != null) { } else if ((cap = containsAt(format, i, "HH12")) != null) {
output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR))); StringUtils.appendZeroPadded(output, 2, h12);
i += 4; i += 4;
} else if ((cap = containsAt(format, i, "HH")) != null) { } else if ((cap = containsAt(format, i, "HH")) != null) {
output.append(new DecimalFormat("00").format(cal.get(Calendar.HOUR))); StringUtils.appendZeroPadded(output, 2, h12);
i += 2; i += 2;
// Minutes // Minutes
} else if ((cap = containsAt(format, i, "MI")) != null) { } else if ((cap = containsAt(format, i, "MI")) != null) {
output.append(new DecimalFormat("00").format(cal.get(Calendar.MINUTE))); StringUtils.appendZeroPadded(output, 2, minute);
i += 2; i += 2;
// Seconds // Seconds
} else if ((cap = containsAt(format, i, "SSSSS")) != null) { } else if ((cap = containsAt(format, i, "SSSSS")) != null) {
int seconds = cal.get(Calendar.HOUR_OF_DAY) * 60 * 60; int seconds = (int) (timeNanos / 1_000_000_000);
seconds += cal.get(Calendar.MINUTE) * 60;
seconds += cal.get(Calendar.SECOND);
output.append(seconds); output.append(seconds);
i += 5; i += 5;
} else if ((cap = containsAt(format, i, "SS")) != null) { } else if ((cap = containsAt(format, i, "SS")) != null) {
output.append(new DecimalFormat("00").format(cal.get(Calendar.SECOND))); StringUtils.appendZeroPadded(output, 2, second);
i += 2; i += 2;
// Fractional seconds // Fractional seconds
...@@ -710,11 +732,11 @@ public class ToChar { ...@@ -710,11 +732,11 @@ public class ToChar {
} else if ((cap = containsAt(format, i, "FF1", "FF2", } else if ((cap = containsAt(format, i, "FF1", "FF2",
"FF3", "FF4", "FF5", "FF6", "FF7", "FF8", "FF9")) != null) { "FF3", "FF4", "FF5", "FF6", "FF7", "FF8", "FF9")) != null) {
int x = Integer.parseInt(format.substring(i + 2, i + 3)); int x = Integer.parseInt(format.substring(i + 2, i + 3));
int ff = (int) (cal.get(Calendar.MILLISECOND) * Math.pow(10, x - 3)); int ff = (int) (nanos * Math.pow(10, x - 9));
output.append(ff); output.append(ff);
i += 3; i += 3;
} else if ((cap = containsAt(format, i, "FF")) != null) { } else if ((cap = containsAt(format, i, "FF")) != null) {
output.append(cal.get(Calendar.MILLISECOND) * 1000); output.append(nanos / 1_000);
i += 2; i += 2;
// Time zone // Time zone
...@@ -732,59 +754,67 @@ public class ToChar { ...@@ -732,59 +754,67 @@ public class ToChar {
// Week // Week
} else if ((cap = containsAt(format, i, "IW", "WW")) != null) { } else if ((cap = containsAt(format, i, "IW", "WW")) != null) {
output.append(cal.get(Calendar.WEEK_OF_YEAR)); output.append(DateTimeUtils.getWeekOfYear(dateValue, 0, 1));
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "W")) != null) { } else if ((cap = containsAt(format, i, "W")) != null) {
int w = (int) (1 + Math.floor(cal.get(Calendar.DAY_OF_MONTH) / 7)); int w = 1 + dayOfMonth / 7;
output.append(w); output.append(w);
i += 1; i += 1;
// Year // Year
} else if ((cap = containsAt(format, i, "Y,YYY")) != null) { } else if ((cap = containsAt(format, i, "Y,YYY")) != null) {
output.append(new DecimalFormat("#,###").format(getYear(cal))); output.append(new DecimalFormat("#,###").format(Math.abs(year)));
i += 5; i += 5;
} else if ((cap = containsAt(format, i, "SYYYY")) != null) { } else if ((cap = containsAt(format, i, "SYYYY")) != null) {
if (cal.get(Calendar.ERA) == GregorianCalendar.BC) { if (year <= 0) {
output.append('-'); output.append('-');
} }
output.append(new DecimalFormat("0000").format(getYear(cal))); StringUtils.appendZeroPadded(output, 4, Math.abs(year));
i += 5; i += 5;
} else if ((cap = containsAt(format, i, "YYYY", "IYYY", "RRRR")) != null) { } else if ((cap = containsAt(format, i, "YYYY", "IYYY", "RRRR")) != null) {
output.append(new DecimalFormat("0000").format(getYear(cal))); StringUtils.appendZeroPadded(output, 4, Math.abs(year));
i += 4; i += 4;
} else if ((cap = containsAt(format, i, "YYY", "IYY")) != null) { } else if ((cap = containsAt(format, i, "YYY", "IYY")) != null) {
output.append(new DecimalFormat("000").format(getYear(cal) % 1000)); StringUtils.appendZeroPadded(output, 3, Math.abs(year) % 1000);
i += 3; i += 3;
} else if ((cap = containsAt(format, i, "YY", "IY", "RR")) != null) { } else if ((cap = containsAt(format, i, "YY", "IY", "RR")) != null) {
output.append(new DecimalFormat("00").format(getYear(cal) % 100)); StringUtils.appendZeroPadded(output, 2, Math.abs(year) % 100);
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "I", "Y")) != null) { } else if ((cap = containsAt(format, i, "I", "Y")) != null) {
output.append(getYear(cal) % 10); output.append(Math.abs(year) % 10);
i += 1; i += 1;
// Month / quarter // Month / quarter
} else if ((cap = containsAt(format, i, "MONTH")) != null) { } else if ((cap = containsAt(format, i, "MONTH")) != null) {
String month = new SimpleDateFormat("MMMM").format(ts); if (dfs == null) {
dfs = DateFormatSymbols.getInstance();
}
String month = dfs.getMonths()[monthOfYear - 1];
if (fillMode) { if (fillMode) {
month = StringUtils.pad(month, "September".length(), " ", true); month = StringUtils.pad(month, "September".length(), " ", true);
} }
output.append(cap.apply(month)); output.append(cap.apply(month));
i += 5; i += 5;
} else if ((cap = containsAt(format, i, "MON")) != null) { } else if ((cap = containsAt(format, i, "MON")) != null) {
String month = new SimpleDateFormat("MMM").format(ts); if (dfs == null) {
dfs = DateFormatSymbols.getInstance();
}
String month = dfs.getShortMonths()[monthOfYear - 1];
if (month.endsWith(".")) {
month = month.substring(0, month.length() - 1);
}
output.append(cap.apply(month)); output.append(cap.apply(month));
i += 3; i += 3;
} else if ((cap = containsAt(format, i, "MM")) != null) { } else if ((cap = containsAt(format, i, "MM")) != null) {
output.append(String.format("%02d", cal.get(Calendar.MONTH) + 1)); StringUtils.appendZeroPadded(output, 2, monthOfYear);
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "RM")) != null) { } else if ((cap = containsAt(format, i, "RM")) != null) {
int month = cal.get(Calendar.MONTH) + 1; output.append(cap.apply(toRomanNumeral(monthOfYear)));
output.append(cap.apply(toRomanNumeral(month)));
i += 2; i += 2;
} else if ((cap = containsAt(format, i, "Q")) != null) { } else if ((cap = containsAt(format, i, "Q")) != null) {
int q = (int) (1 + Math.floor(cal.get(Calendar.MONTH) / 3)); int q = 1 + ((monthOfYear - 1) / 3);
output.append(q); output.append(q);
i += 1; i += 1;
...@@ -835,14 +865,6 @@ public class ToChar { ...@@ -835,14 +865,6 @@ public class ToChar {
return output.toString(); 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 * Returns a capitalization strategy if the specified string contains any of
* the specified substrings at the specified index. The capitalization * the specified substrings at the specified index. The capitalization
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论