improved DataFormatter, see Bugzilla 50841
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1083173 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8c44bb85d1
commit
1ba16fb3b9
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.8-beta2" date="2011-??-??">
|
<release version="3.8-beta2" date="2011-??-??">
|
||||||
|
<action dev="poi-developers" type="fix">50841 - Improved SpreadSheet DataFormatter to handle scientific notation, invalid dates and format spacers</action>
|
||||||
<action dev="poi-developers" type="fix">49381 - Correct createFreezePane in XSSF, so that the left row/column matches the documentation + HSSF</action>
|
<action dev="poi-developers" type="fix">49381 - Correct createFreezePane in XSSF, so that the left row/column matches the documentation + HSSF</action>
|
||||||
<action dev="poi-developers" type="fix">49253 - When setting repeating rows and columns for XSSF, don't break the print settings if they were already there</action>
|
<action dev="poi-developers" type="fix">49253 - When setting repeating rows and columns for XSSF, don't break the print settings if they were already there</action>
|
||||||
<action dev="poi-developers" type="fix">49219 - ExternalNameRecord support for DDE Link entries without an operation</action>
|
<action dev="poi-developers" type="fix">49219 - ExternalNameRecord support for DDE Link entries without an operation</action>
|
||||||
|
@ -389,7 +389,7 @@ public class ToCSV {
|
|||||||
// formatted String encapsulating the cells contents.
|
// formatted String encapsulating the cells contents.
|
||||||
this.workbook = WorkbookFactory.create(fis);
|
this.workbook = WorkbookFactory.create(fis);
|
||||||
this.evaluator = this.workbook.getCreationHelper().createFormulaEvaluator();
|
this.evaluator = this.workbook.getCreationHelper().createFormulaEvaluator();
|
||||||
this.formatter = new DataFormatter();
|
this.formatter = new DataFormatter(true);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if(fis != null) {
|
if(fis != null) {
|
||||||
|
@ -63,8 +63,27 @@ import java.text.*;
|
|||||||
* default format will only be used when a Format cannot be created from the
|
* default format will only be used when a Format cannot be created from the
|
||||||
* cell's data format string.
|
* cell's data format string.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that by default formatted numeric values are trimmed.
|
||||||
|
* Excel formats can contain spacers and padding and the default behavior is to strip them off.
|
||||||
|
* </p>
|
||||||
|
* <p>Example:</p>
|
||||||
|
* <p>
|
||||||
|
* Consider a numeric cell with a value <code>12.343</code> and format <code>"##.##_ "</code>.
|
||||||
|
* The trailing underscore and space ("_ ") in the format adds a space to the end and Excel formats this cell as <code>"12.34 "</code>,
|
||||||
|
* but <code>DataFormatter</code> trims the formatted value and returns <code>"12.34"</code>.
|
||||||
|
* </p>
|
||||||
|
* You can enable spaces by passing the <code>emulateCsv=true</code> flag in the <code>DateFormatter</code> cosntructor.
|
||||||
|
* If set to true, then the output tries to conform to what you get when you take an xls or xlsx in Excel and Save As CSV file:
|
||||||
|
* <ul>
|
||||||
|
* <li>returned values are not trimmed</li>
|
||||||
|
* <li>Invalid dates are formatted as 255 pound signs ("#")</li>
|
||||||
|
* <li>simulate Excel's handling of a format string of all # when the value is 0.
|
||||||
|
* Excel will output "", <code>DataFormatter</code> will output "0".
|
||||||
|
* </ul>
|
||||||
* @author James May (james dot may at fmr dot com)
|
* @author James May (james dot may at fmr dot com)
|
||||||
*
|
* @author Robert Kish
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class DataFormatter {
|
public class DataFormatter {
|
||||||
|
|
||||||
@ -80,7 +99,7 @@ public class DataFormatter {
|
|||||||
/** A regex to find patterns like [$$-1009] and [$?-452]. */
|
/** A regex to find patterns like [$$-1009] and [$?-452]. */
|
||||||
private static final Pattern specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
|
private static final Pattern specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A regex to match the colour formattings rules.
|
* A regex to match the colour formattings rules.
|
||||||
* Allowed colours are: Black, Blue, Cyan, Green,
|
* Allowed colours are: Black, Blue, Cyan, Green,
|
||||||
* Magenta, Red, White, Yellow, "Color n" (1<=n<=56)
|
* Magenta, Red, White, Yellow, "Color n" (1<=n<=56)
|
||||||
@ -89,7 +108,18 @@ public class DataFormatter {
|
|||||||
Pattern.compile("(\\[BLACK\\])|(\\[BLUE\\])|(\\[CYAN\\])|(\\[GREEN\\])|" +
|
Pattern.compile("(\\[BLACK\\])|(\\[BLUE\\])|(\\[CYAN\\])|(\\[GREEN\\])|" +
|
||||||
"(\\[MAGENTA\\])|(\\[RED\\])|(\\[WHITE\\])|(\\[YELLOW\\])|" +
|
"(\\[MAGENTA\\])|(\\[RED\\])|(\\[WHITE\\])|(\\[YELLOW\\])|" +
|
||||||
"(\\[COLOR\\s*\\d\\])|(\\[COLOR\\s*[0-5]\\d\\])", Pattern.CASE_INSENSITIVE);
|
"(\\[COLOR\\s*\\d\\])|(\\[COLOR\\s*[0-5]\\d\\])", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cells formatted with a date or time format and which contain invalid date or time values
|
||||||
|
* show 255 pound signs ("#").
|
||||||
|
*/
|
||||||
|
private static final String invalidDateTimeString;
|
||||||
|
static {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
for(int i = 0; i < 255; i++) buf.append('#');
|
||||||
|
invalidDateTimeString = buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The decimal symbols of the locale used for formatting values.
|
* The decimal symbols of the locale used for formatting values.
|
||||||
*/
|
*/
|
||||||
@ -115,11 +145,33 @@ public class DataFormatter {
|
|||||||
*/
|
*/
|
||||||
private final Map<String,Format> formats;
|
private final Map<String,Format> formats;
|
||||||
|
|
||||||
|
private boolean emulateCsv = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a formatter using the {@link Locale#getDefault() default locale}.
|
* Creates a formatter using the {@link Locale#getDefault() default locale}.
|
||||||
*/
|
*/
|
||||||
public DataFormatter() {
|
public DataFormatter() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a formatter using the {@link Locale#getDefault() default locale}.
|
||||||
|
*
|
||||||
|
* @param emulateCsv whether to emulate CSV output.
|
||||||
|
*/
|
||||||
|
public DataFormatter(boolean emulateCsv) {
|
||||||
this(Locale.getDefault());
|
this(Locale.getDefault());
|
||||||
|
this.emulateCsv = emulateCsv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a formatter using the given locale.
|
||||||
|
*
|
||||||
|
* @param emulateCsv whether to emulate CSV output.
|
||||||
|
*/
|
||||||
|
public DataFormatter(Locale locale, boolean emulateCsv) {
|
||||||
|
this(locale);
|
||||||
|
this.emulateCsv = emulateCsv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,27 +229,42 @@ public class DataFormatter {
|
|||||||
return getFormat(cell.getNumericCellValue(), formatIndex, formatStr);
|
return getFormat(cell.getNumericCellValue(), formatIndex, formatStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Format getFormat(double cellValue, int formatIndex, String formatStr) {
|
private Format getFormat(double cellValue, int formatIndex, String formatStrIn) {
|
||||||
// Excel supports positive/negative/zero, but java
|
String formatStr = formatStrIn;
|
||||||
// doesn't, so we need to do it specially
|
// Excel supports positive/negative/zero, but java
|
||||||
if(formatStr.indexOf(';') != formatStr.lastIndexOf(';')) {
|
// doesn't, so we need to do it specially
|
||||||
int lastAt = formatStr.lastIndexOf(';');
|
final int firstAt = formatStr.indexOf(';');
|
||||||
String zeroFormat = formatStr.substring(lastAt+1);
|
final int lastAt = formatStr.lastIndexOf(';');
|
||||||
String normalFormat = formatStr.substring(0,lastAt);
|
// p and p;n are ok by default. p;n;z and p;n;z;s need to be fixed.
|
||||||
if(cellValue == 0.0) {
|
if (firstAt != -1 && firstAt != lastAt) {
|
||||||
formatStr = zeroFormat;
|
final int secondAt = formatStr.indexOf(';', firstAt + 1);
|
||||||
} else {
|
if (secondAt == lastAt) { // p;n;z
|
||||||
formatStr = normalFormat;
|
if (cellValue == 0.0) {
|
||||||
}
|
formatStr = formatStr.substring(lastAt + 1);
|
||||||
|
} else {
|
||||||
|
formatStr = formatStr.substring(0, lastAt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cellValue == 0.0) { // p;n;z;s
|
||||||
|
formatStr = formatStr.substring(secondAt + 1, lastAt);
|
||||||
|
} else {
|
||||||
|
formatStr = formatStr.substring(0, secondAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Excel's # with value 0 will output empty where Java will output 0. This hack removes the # from the format.
|
||||||
|
if (emulateCsv && cellValue == 0.0 && formatStr.contains("#") && !formatStr.contains("0")) {
|
||||||
|
formatStr = formatStr.replaceAll("#", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we already have it cached
|
// See if we already have it cached
|
||||||
Format format = formats.get(formatStr);
|
Format format = formats.get(formatStr);
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
|
if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
|
||||||
if (DataFormatter.isWholeNumber(cellValue)) {
|
if (isWholeNumber(cellValue)) {
|
||||||
return generalWholeNumFormat;
|
return generalWholeNumFormat;
|
||||||
}
|
}
|
||||||
return generalDecimalNumFormat;
|
return generalDecimalNumFormat;
|
||||||
@ -261,7 +328,6 @@ public class DataFormatter {
|
|||||||
return getDefaultFormat(cellValue);
|
return getDefaultFormat(cellValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(DateUtil.isADateFormat(formatIndex,formatStr) &&
|
if(DateUtil.isADateFormat(formatIndex,formatStr) &&
|
||||||
DateUtil.isValidExcelDate(cellValue)) {
|
DateUtil.isValidExcelDate(cellValue)) {
|
||||||
return createDateFormat(formatStr, cellValue);
|
return createDateFormat(formatStr, cellValue);
|
||||||
@ -269,6 +335,10 @@ public class DataFormatter {
|
|||||||
if (numPattern.matcher(formatStr).find()) {
|
if (numPattern.matcher(formatStr).find()) {
|
||||||
return createNumberFormat(formatStr, cellValue);
|
return createNumberFormat(formatStr, cellValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (emulateCsv) {
|
||||||
|
return new ConstantStringFormat(cleanFormatForNumber(formatStr));
|
||||||
|
}
|
||||||
// TODO - when does this occur?
|
// TODO - when does this occur?
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -311,9 +381,33 @@ public class DataFormatter {
|
|||||||
char[] chars = formatStr.toCharArray();
|
char[] chars = formatStr.toCharArray();
|
||||||
boolean mIsMonth = true;
|
boolean mIsMonth = true;
|
||||||
List<Integer> ms = new ArrayList<Integer>();
|
List<Integer> ms = new ArrayList<Integer>();
|
||||||
|
boolean isElapsed = false;
|
||||||
for(int j=0; j<chars.length; j++) {
|
for(int j=0; j<chars.length; j++) {
|
||||||
char c = chars[j];
|
char c = chars[j];
|
||||||
|
if (c == '[' && !isElapsed) {
|
||||||
|
isElapsed = true;
|
||||||
|
mIsMonth = false;
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
else if (c == ']' && isElapsed) {
|
||||||
|
isElapsed = false;
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
else if (isElapsed) {
|
||||||
if (c == 'h' || c == 'H') {
|
if (c == 'h' || c == 'H') {
|
||||||
|
sb.append('H');
|
||||||
|
}
|
||||||
|
else if (c == 'm' || c == 'M') {
|
||||||
|
sb.append('m');
|
||||||
|
}
|
||||||
|
else if (c == 's' || c == 'S') {
|
||||||
|
sb.append('s');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (c == 'h' || c == 'H') {
|
||||||
mIsMonth = false;
|
mIsMonth = false;
|
||||||
if (hasAmPm) {
|
if (hasAmPm) {
|
||||||
sb.append('h');
|
sb.append('h');
|
||||||
@ -373,32 +467,61 @@ public class DataFormatter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Format createNumberFormat(String formatStr, double cellValue) {
|
private String cleanFormatForNumber(String formatStr) {
|
||||||
StringBuffer sb = new StringBuffer(formatStr);
|
StringBuffer sb = new StringBuffer(formatStr);
|
||||||
|
|
||||||
// If they requested spacers, with "_",
|
if (emulateCsv) {
|
||||||
// remove those as we don't do spacing
|
// Requested spacers with "_" are replaced by a single space.
|
||||||
// If they requested full-column-width
|
// Full-column-width padding "*" are removed.
|
||||||
// padding, with "*", remove those too
|
// Not processing fractions at this time. Replace ? with space.
|
||||||
for(int i = 0; i < sb.length(); i++) {
|
// This matches CSV output.
|
||||||
char c = sb.charAt(i);
|
for (int i = 0; i < sb.length(); i++) {
|
||||||
if(c == '_' || c == '*') {
|
char c = sb.charAt(i);
|
||||||
if(i > 0 && sb.charAt((i-1)) == '\\') {
|
if (c == '_' || c == '*' || c == '?') {
|
||||||
// It's escaped, don't worry
|
if (i > 0 && sb.charAt((i - 1)) == '\\') {
|
||||||
continue;
|
// It's escaped, don't worry
|
||||||
} else {
|
continue;
|
||||||
if(i < sb.length()-1) {
|
}
|
||||||
// Remove the character we're supposed
|
if (c == '?') {
|
||||||
// to match the space of / pad to the
|
sb.setCharAt(i, ' ');
|
||||||
// column width with
|
} else if (i < sb.length() - 1) {
|
||||||
sb.deleteCharAt(i+1);
|
// Remove the character we're supposed
|
||||||
}
|
// to match the space of / pad to the
|
||||||
// Remove the _ too
|
// column width with
|
||||||
sb.deleteCharAt(i);
|
if (c == '_') {
|
||||||
}
|
sb.setCharAt(i + 1, ' ');
|
||||||
|
} else {
|
||||||
|
sb.deleteCharAt(i + 1);
|
||||||
|
}
|
||||||
|
// Remove the character too
|
||||||
|
sb.deleteCharAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If they requested spacers, with "_",
|
||||||
|
// remove those as we don't do spacing
|
||||||
|
// If they requested full-column-width
|
||||||
|
// padding, with "*", remove those too
|
||||||
|
for (int i = 0; i < sb.length(); i++) {
|
||||||
|
char c = sb.charAt(i);
|
||||||
|
if (c == '_' || c == '*') {
|
||||||
|
if (i > 0 && sb.charAt((i - 1)) == '\\') {
|
||||||
|
// It's escaped, don't worry
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i < sb.length() - 1) {
|
||||||
|
// Remove the character we're supposed
|
||||||
|
// to match the space of / pad to the
|
||||||
|
// column width with
|
||||||
|
sb.deleteCharAt(i + 1);
|
||||||
|
}
|
||||||
|
// Remove the _ too
|
||||||
|
sb.deleteCharAt(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, handle the other aspects like
|
// Now, handle the other aspects like
|
||||||
// quoting and scientific notation
|
// quoting and scientific notation
|
||||||
for(int i = 0; i < sb.length(); i++) {
|
for(int i = 0; i < sb.length(); i++) {
|
||||||
@ -415,8 +538,14 @@ public class DataFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Format createNumberFormat(String formatStr, double cellValue) {
|
||||||
|
final String format = cleanFormatForNumber(formatStr);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DecimalFormat df = new DecimalFormat(sb.toString(), decimalSymbols);
|
DecimalFormat df = new DecimalFormat(format, decimalSymbols);
|
||||||
setExcelStyleRoundingMode(df);
|
setExcelStyleRoundingMode(df);
|
||||||
return df;
|
return df;
|
||||||
} catch(IllegalArgumentException iae) {
|
} catch(IllegalArgumentException iae) {
|
||||||
@ -522,22 +651,32 @@ public class DataFormatter {
|
|||||||
*/
|
*/
|
||||||
public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {
|
public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {
|
||||||
// Is it a date?
|
// Is it a date?
|
||||||
if(DateUtil.isADateFormat(formatIndex,formatString) &&
|
if(DateUtil.isADateFormat(formatIndex,formatString)) {
|
||||||
DateUtil.isValidExcelDate(value)) {
|
if(DateUtil.isValidExcelDate(value)) {
|
||||||
Format dateFormat = getFormat(value, formatIndex, formatString);
|
Format dateFormat = getFormat(value, formatIndex, formatString);
|
||||||
if(dateFormat instanceof ExcelStyleDateFormatter) {
|
if(dateFormat instanceof ExcelStyleDateFormatter) {
|
||||||
// Hint about the raw excel value
|
// Hint about the raw excel value
|
||||||
((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(value);
|
((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(value);
|
||||||
|
}
|
||||||
|
Date d = DateUtil.getJavaDate(value, use1904Windowing);
|
||||||
|
return performDateFormatting(d, dateFormat);
|
||||||
}
|
}
|
||||||
Date d = DateUtil.getJavaDate(value, use1904Windowing);
|
// RK: Invalid dates are 255 #s.
|
||||||
return performDateFormatting(d, dateFormat);
|
if (emulateCsv) {
|
||||||
|
return invalidDateTimeString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// else Number
|
// else Number
|
||||||
Format numberFormat = getFormat(value, formatIndex, formatString);
|
Format numberFormat = getFormat(value, formatIndex, formatString);
|
||||||
if (numberFormat == null) {
|
if (numberFormat == null) {
|
||||||
return String.valueOf(value);
|
return String.valueOf(value);
|
||||||
}
|
}
|
||||||
return numberFormat.format(new Double(value));
|
// RK: This hack handles scientific notation by adding the missing + back.
|
||||||
|
String result = numberFormat.format(new Double(value));
|
||||||
|
if (result.contains("E") && !result.contains("E-")) {
|
||||||
|
result = result.replaceFirst("E", "E+");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -669,9 +808,21 @@ public class DataFormatter {
|
|||||||
* on Java 1.5.
|
* on Java 1.5.
|
||||||
*/
|
*/
|
||||||
public static void setExcelStyleRoundingMode(DecimalFormat format) {
|
public static void setExcelStyleRoundingMode(DecimalFormat format) {
|
||||||
|
setExcelStyleRoundingMode(format, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables custom rounding mode
|
||||||
|
* on the Decimal Format if possible.
|
||||||
|
* This will work for Java 1.6, but isn't possible
|
||||||
|
* on Java 1.5.
|
||||||
|
* @param format DecimalFormat
|
||||||
|
* @param roundingMode RoundingMode
|
||||||
|
*/
|
||||||
|
public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) {
|
||||||
try {
|
try {
|
||||||
Method srm = format.getClass().getMethod("setRoundingMode", RoundingMode.class);
|
Method srm = format.getClass().getMethod("setRoundingMode", RoundingMode.class);
|
||||||
srm.invoke(format, RoundingMode.HALF_UP);
|
srm.invoke(format, roundingMode);
|
||||||
} catch(NoSuchMethodException e) {
|
} catch(NoSuchMethodException e) {
|
||||||
// Java 1.5
|
// Java 1.5
|
||||||
} catch(IllegalAccessException iae) {
|
} catch(IllegalAccessException iae) {
|
||||||
@ -684,7 +835,7 @@ public class DataFormatter {
|
|||||||
// Not much we can do here
|
// Not much we can do here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format class for Excel's SSN format. This class mimics Excel's built-in
|
* Format class for Excel's SSN format. This class mimics Excel's built-in
|
||||||
* SSN formatting.
|
* SSN formatting.
|
||||||
@ -794,4 +945,31 @@ public class DataFormatter {
|
|||||||
return df.parseObject(source, pos);
|
return df.parseObject(source, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format class that does nothing and always returns a constant string.
|
||||||
|
*
|
||||||
|
* This format is used to simulate Excel's handling of a format string
|
||||||
|
* of all # when the value is 0. Excel will output "", Java will output "0".
|
||||||
|
*
|
||||||
|
* @see DataFormatter#createFormat(double, int, String)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static final class ConstantStringFormat extends Format {
|
||||||
|
private static final DecimalFormat df = createIntegerOnlyFormat("##########");
|
||||||
|
private final String str;
|
||||||
|
public ConstantStringFormat(String s) {
|
||||||
|
str = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
|
||||||
|
return toAppendTo.append(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parseObject(String source, ParsePosition pos) {
|
||||||
|
return df.parseObject(source, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,9 @@ public class DateUtil {
|
|||||||
*/
|
*/
|
||||||
private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]");
|
private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]");
|
||||||
private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]");
|
private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]");
|
||||||
private static final Pattern date_ptrn3 = Pattern.compile("^[yYmMdDhHsS\\-/,. :\"\\\\]+0?[ampAMP/]*$");
|
private static final Pattern date_ptrn3 = Pattern.compile("^[\\[\\]yYmMdDhHsS\\-/,. :\"\\\\]+0*[ampAMP/]*$");
|
||||||
|
// elapsed time patterns: [h],[m] and [s]
|
||||||
|
private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a Date, converts it into a double representing its internal Excel representation,
|
* Given a Date, converts it into a double representing its internal Excel representation,
|
||||||
@ -258,7 +260,12 @@ public class DateUtil {
|
|||||||
sb.append(c);
|
sb.append(c);
|
||||||
}
|
}
|
||||||
fs = sb.toString();
|
fs = sb.toString();
|
||||||
|
|
||||||
|
// short-circuit if it indicates elapsed time: [h], [m] or [s]
|
||||||
|
if(date_ptrn4.matcher(fs).matches()){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// If it starts with [$-...], then could be a date, but
|
// If it starts with [$-...], then could be a date, but
|
||||||
// who knows what that starting bit is all about
|
// who knows what that starting bit is all about
|
||||||
fs = date_ptrn1.matcher(fs).replaceAll("");
|
fs = date_ptrn1.matcher(fs).replaceAll("");
|
||||||
|
@ -16,130 +16,157 @@
|
|||||||
==================================================================== */
|
==================================================================== */
|
||||||
package org.apache.poi.ss.usermodel;
|
package org.apache.poi.ss.usermodel;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.text.*;
|
import java.text.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper around a {@link SimpleDateFormat} instance,
|
* A wrapper around a {@link SimpleDateFormat} instance,
|
||||||
* which handles a few Excel-style extensions that
|
* which handles a few Excel-style extensions that
|
||||||
* are not supported by {@link SimpleDateFormat}.
|
* are not supported by {@link SimpleDateFormat}.
|
||||||
* Currently, the extensions are around the handling
|
* Currently, the extensions are around the handling
|
||||||
* of elapsed time, eg rendering 1 day 2 hours
|
* of elapsed time, eg rendering 1 day 2 hours
|
||||||
* as 26 hours.
|
* as 26 hours.
|
||||||
*/
|
*/
|
||||||
public class ExcelStyleDateFormatter extends SimpleDateFormat {
|
public class ExcelStyleDateFormatter extends SimpleDateFormat {
|
||||||
public static final char MMMMM_START_SYMBOL = '\ue001';
|
public static final char MMMMM_START_SYMBOL = '\ue001';
|
||||||
public static final char MMMMM_TRUNCATE_SYMBOL = '\ue002';
|
public static final char MMMMM_TRUNCATE_SYMBOL = '\ue002';
|
||||||
public static final char H_BRACKET_SYMBOL = '\ue010';
|
public static final char H_BRACKET_SYMBOL = '\ue010';
|
||||||
public static final char HH_BRACKET_SYMBOL = '\ue011';
|
public static final char HH_BRACKET_SYMBOL = '\ue011';
|
||||||
public static final char M_BRACKET_SYMBOL = '\ue012';
|
public static final char M_BRACKET_SYMBOL = '\ue012';
|
||||||
public static final char MM_BRACKET_SYMBOL = '\ue013';
|
public static final char MM_BRACKET_SYMBOL = '\ue013';
|
||||||
public static final char S_BRACKET_SYMBOL = '\ue014';
|
public static final char S_BRACKET_SYMBOL = '\ue014';
|
||||||
public static final char SS_BRACKET_SYMBOL = '\ue015';
|
public static final char SS_BRACKET_SYMBOL = '\ue015';
|
||||||
|
public static final char L_BRACKET_SYMBOL = '\ue016';
|
||||||
private DecimalFormat format1digit = new DecimalFormat("0");
|
public static final char LL_BRACKET_SYMBOL = '\ue017';
|
||||||
private DecimalFormat format2digits = new DecimalFormat("00");
|
|
||||||
{
|
|
||||||
DataFormatter.setExcelStyleRoundingMode(format1digit);
|
|
||||||
DataFormatter.setExcelStyleRoundingMode(format2digits);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double dateToBeFormatted = 0.0;
|
|
||||||
|
|
||||||
public ExcelStyleDateFormatter() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
public ExcelStyleDateFormatter(String pattern) {
|
|
||||||
super(processFormatPattern(pattern));
|
|
||||||
}
|
|
||||||
public ExcelStyleDateFormatter(String pattern,
|
|
||||||
DateFormatSymbols formatSymbols) {
|
|
||||||
super(processFormatPattern(pattern), formatSymbols);
|
|
||||||
}
|
|
||||||
public ExcelStyleDateFormatter(String pattern, Locale locale) {
|
|
||||||
super(processFormatPattern(pattern), locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private DecimalFormat format1digit = new DecimalFormat("0");
|
||||||
* Takes a format String, and replaces Excel specific bits
|
private DecimalFormat format2digits = new DecimalFormat("00");
|
||||||
* with our detection sequences
|
|
||||||
*/
|
|
||||||
private static String processFormatPattern(String f) {
|
|
||||||
f = f.replaceAll("MMMMM", MMMMM_START_SYMBOL + "MMM" + MMMMM_TRUNCATE_SYMBOL);
|
|
||||||
f = f.replaceAll("\\[H\\]", String.valueOf(H_BRACKET_SYMBOL));
|
|
||||||
f = f.replaceAll("\\[HH\\]", String.valueOf(HH_BRACKET_SYMBOL));
|
|
||||||
f = f.replaceAll("\\[m\\]", String.valueOf(M_BRACKET_SYMBOL));
|
|
||||||
f = f.replaceAll("\\[mm\\]", String.valueOf(MM_BRACKET_SYMBOL));
|
|
||||||
f = f.replaceAll("\\[s\\]", String.valueOf(S_BRACKET_SYMBOL));
|
|
||||||
f = f.replaceAll("\\[ss\\]", String.valueOf(SS_BRACKET_SYMBOL));
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private DecimalFormat format3digit = new DecimalFormat("0");
|
||||||
* Used to let us know what the date being
|
private DecimalFormat format4digits = new DecimalFormat("00");
|
||||||
* formatted is, in Excel terms, which we
|
|
||||||
* may wish to use when handling elapsed
|
|
||||||
* times.
|
|
||||||
*/
|
|
||||||
public void setDateToBeFormatted(double date) {
|
|
||||||
this.dateToBeFormatted = date;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public StringBuffer format(Date date, StringBuffer paramStringBuffer,
|
|
||||||
FieldPosition paramFieldPosition) {
|
|
||||||
// Do the normal format
|
|
||||||
String s = super.format(date, paramStringBuffer, paramFieldPosition).toString();
|
|
||||||
|
|
||||||
// Now handle our special cases
|
|
||||||
if(s.indexOf(MMMMM_START_SYMBOL) != -1) {
|
|
||||||
s = s.replaceAll(
|
|
||||||
MMMMM_START_SYMBOL + "(\\w)\\w+" + MMMMM_TRUNCATE_SYMBOL,
|
|
||||||
"$1"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(s.indexOf(H_BRACKET_SYMBOL) != -1 ||
|
|
||||||
s.indexOf(HH_BRACKET_SYMBOL) != -1) {
|
|
||||||
double hours = dateToBeFormatted * 24;
|
|
||||||
s = s.replaceAll(
|
|
||||||
String.valueOf(H_BRACKET_SYMBOL),
|
|
||||||
format1digit.format(hours)
|
|
||||||
);
|
|
||||||
s = s.replaceAll(
|
|
||||||
String.valueOf(HH_BRACKET_SYMBOL),
|
|
||||||
format2digits.format(hours)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(s.indexOf(M_BRACKET_SYMBOL) != -1 ||
|
|
||||||
s.indexOf(MM_BRACKET_SYMBOL) != -1) {
|
|
||||||
double minutes = dateToBeFormatted * 24 * 60;
|
|
||||||
s = s.replaceAll(
|
|
||||||
String.valueOf(M_BRACKET_SYMBOL),
|
|
||||||
format1digit.format(minutes)
|
|
||||||
);
|
|
||||||
s = s.replaceAll(
|
|
||||||
String.valueOf(MM_BRACKET_SYMBOL),
|
|
||||||
format2digits.format(minutes)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if(s.indexOf(S_BRACKET_SYMBOL) != -1 ||
|
|
||||||
s.indexOf(SS_BRACKET_SYMBOL) != -1) {
|
|
||||||
double seconds = dateToBeFormatted * 24 * 60 * 60;
|
|
||||||
s = s.replaceAll(
|
|
||||||
String.valueOf(S_BRACKET_SYMBOL),
|
|
||||||
format1digit.format(seconds)
|
|
||||||
);
|
|
||||||
s = s.replaceAll(
|
|
||||||
String.valueOf(SS_BRACKET_SYMBOL),
|
|
||||||
format2digits.format(seconds)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new StringBuffer(s);
|
{
|
||||||
}
|
DataFormatter.setExcelStyleRoundingMode(format1digit, RoundingMode.DOWN);
|
||||||
|
DataFormatter.setExcelStyleRoundingMode(format2digits, RoundingMode.DOWN);
|
||||||
|
DataFormatter.setExcelStyleRoundingMode(format3digit);
|
||||||
|
DataFormatter.setExcelStyleRoundingMode(format4digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double dateToBeFormatted = 0.0;
|
||||||
|
|
||||||
|
public ExcelStyleDateFormatter() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExcelStyleDateFormatter(String pattern) {
|
||||||
|
super(processFormatPattern(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExcelStyleDateFormatter(String pattern,
|
||||||
|
DateFormatSymbols formatSymbols) {
|
||||||
|
super(processFormatPattern(pattern), formatSymbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExcelStyleDateFormatter(String pattern, Locale locale) {
|
||||||
|
super(processFormatPattern(pattern), locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a format String, and replaces Excel specific bits
|
||||||
|
* with our detection sequences
|
||||||
|
*/
|
||||||
|
private static String processFormatPattern(String f) {
|
||||||
|
String t = f.replaceAll("MMMMM", MMMMM_START_SYMBOL + "MMM" + MMMMM_TRUNCATE_SYMBOL);
|
||||||
|
t = t.replaceAll("\\[H\\]", String.valueOf(H_BRACKET_SYMBOL));
|
||||||
|
t = t.replaceAll("\\[HH\\]", String.valueOf(HH_BRACKET_SYMBOL));
|
||||||
|
t = t.replaceAll("\\[m\\]", String.valueOf(M_BRACKET_SYMBOL));
|
||||||
|
t = t.replaceAll("\\[mm\\]", String.valueOf(MM_BRACKET_SYMBOL));
|
||||||
|
t = t.replaceAll("\\[s\\]", String.valueOf(S_BRACKET_SYMBOL));
|
||||||
|
t = t.replaceAll("\\[ss\\]", String.valueOf(SS_BRACKET_SYMBOL));
|
||||||
|
t = t.replaceAll("s.000", "s.S");
|
||||||
|
t = t.replaceAll("s.00", "s." + LL_BRACKET_SYMBOL);
|
||||||
|
t = t.replaceAll("s.0", "s." + L_BRACKET_SYMBOL);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to let us know what the date being
|
||||||
|
* formatted is, in Excel terms, which we
|
||||||
|
* may wish to use when handling elapsed
|
||||||
|
* times.
|
||||||
|
*/
|
||||||
|
public void setDateToBeFormatted(double date) {
|
||||||
|
this.dateToBeFormatted = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringBuffer format(Date date, StringBuffer paramStringBuffer,
|
||||||
|
FieldPosition paramFieldPosition) {
|
||||||
|
// Do the normal format
|
||||||
|
String s = super.format(date, paramStringBuffer, paramFieldPosition).toString();
|
||||||
|
|
||||||
|
// Now handle our special cases
|
||||||
|
if (s.indexOf(MMMMM_START_SYMBOL) != -1) {
|
||||||
|
s = s.replaceAll(
|
||||||
|
MMMMM_START_SYMBOL + "(\\w)\\w+" + MMMMM_TRUNCATE_SYMBOL,
|
||||||
|
"$1"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.indexOf(H_BRACKET_SYMBOL) != -1 ||
|
||||||
|
s.indexOf(HH_BRACKET_SYMBOL) != -1) {
|
||||||
|
float hours = (float) dateToBeFormatted * 24;
|
||||||
|
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(H_BRACKET_SYMBOL),
|
||||||
|
format1digit.format(hours)
|
||||||
|
);
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(HH_BRACKET_SYMBOL),
|
||||||
|
format2digits.format(hours)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.indexOf(M_BRACKET_SYMBOL) != -1 ||
|
||||||
|
s.indexOf(MM_BRACKET_SYMBOL) != -1) {
|
||||||
|
float minutes = (float) dateToBeFormatted * 24 * 60;
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(M_BRACKET_SYMBOL),
|
||||||
|
format1digit.format(minutes)
|
||||||
|
);
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(MM_BRACKET_SYMBOL),
|
||||||
|
format2digits.format(minutes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (s.indexOf(S_BRACKET_SYMBOL) != -1 ||
|
||||||
|
s.indexOf(SS_BRACKET_SYMBOL) != -1) {
|
||||||
|
float seconds = (float) (dateToBeFormatted * 24.0 * 60.0 * 60.0);
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(S_BRACKET_SYMBOL),
|
||||||
|
format1digit.format(seconds)
|
||||||
|
);
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(SS_BRACKET_SYMBOL),
|
||||||
|
format2digits.format(seconds)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.indexOf(L_BRACKET_SYMBOL) != -1 ||
|
||||||
|
s.indexOf(LL_BRACKET_SYMBOL) != -1) {
|
||||||
|
float millisTemp = (float) ((dateToBeFormatted - Math.floor(dateToBeFormatted)) * 24.0 * 60.0 * 60.0);
|
||||||
|
float millis = (millisTemp - (int) millisTemp);
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(L_BRACKET_SYMBOL),
|
||||||
|
format3digit.format(millis * 10)
|
||||||
|
);
|
||||||
|
s = s.replaceAll(
|
||||||
|
String.valueOf(LL_BRACKET_SYMBOL),
|
||||||
|
format4digits.format(millis * 100)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StringBuffer(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,8 @@ public final class TestHSSFDateUtil extends TestCase {
|
|||||||
"mm/dd HH:MM PM", "mm/dd HH:MM pm",
|
"mm/dd HH:MM PM", "mm/dd HH:MM pm",
|
||||||
"m/d/yy h:mm AM/PM",
|
"m/d/yy h:mm AM/PM",
|
||||||
"hh:mm:ss", "hh:mm:ss.0", "mm:ss.0",
|
"hh:mm:ss", "hh:mm:ss.0", "mm:ss.0",
|
||||||
|
//support elapsed time [h],[m],[s]
|
||||||
|
"[hh]", "[mm]", "[ss]", "[SS]", "[red][hh]"
|
||||||
};
|
};
|
||||||
for(int i=0; i<formats.length; i++) {
|
for(int i=0; i<formats.length; i++) {
|
||||||
assertTrue(
|
assertTrue(
|
||||||
@ -286,6 +287,7 @@ public final class TestHSSFDateUtil extends TestCase {
|
|||||||
"0.0", "0.000",
|
"0.0", "0.000",
|
||||||
"0%", "0.0%",
|
"0%", "0.0%",
|
||||||
"[]Foo", "[BLACK]0.00%",
|
"[]Foo", "[BLACK]0.00%",
|
||||||
|
"[ms]", "[Mh]",
|
||||||
"", null
|
"", null
|
||||||
};
|
};
|
||||||
for(int i=0; i<formats.length; i++) {
|
for(int i=0; i<formats.length; i++) {
|
||||||
|
@ -27,7 +27,7 @@ import org.apache.poi.hssf.usermodel.TestHSSFDataFormatter;
|
|||||||
/**
|
/**
|
||||||
* Tests of {@link DataFormatter}
|
* Tests of {@link DataFormatter}
|
||||||
*
|
*
|
||||||
* See {@link TestHSSFDataFormatter} too for
|
* See {@link TestHSSFDataFormatter} too for
|
||||||
* more tests.
|
* more tests.
|
||||||
*/
|
*/
|
||||||
public class TestDataFormatter extends TestCase {
|
public class TestDataFormatter extends TestCase {
|
||||||
@ -179,6 +179,38 @@ public class TestDataFormatter extends TestCase {
|
|||||||
assertEquals("12.34", dfUS.formatRawCellContents(12.343, -1, "*-##.##"));
|
assertEquals("12.34", dfUS.formatRawCellContents(12.343, -1, "*-##.##"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataFormatter is the CSV mode preserves spaces
|
||||||
|
*/
|
||||||
|
public void testPaddingSpacesCSV() {
|
||||||
|
DataFormatter dfUS = new DataFormatter(Locale.US, true);
|
||||||
|
assertEquals("12.34 ", dfUS.formatRawCellContents(12.343, -1, "##.##_ "));
|
||||||
|
assertEquals("-12.34 ", dfUS.formatRawCellContents(-12.343, -1, "##.##_ "));
|
||||||
|
assertEquals(". ", dfUS.formatRawCellContents(0.0, -1, "##.##_ "));
|
||||||
|
assertEquals("12.34 ", dfUS.formatRawCellContents(12.343, -1, "##.##_1"));
|
||||||
|
assertEquals("-12.34 ", dfUS.formatRawCellContents(-12.343, -1, "##.##_1"));
|
||||||
|
assertEquals(". ", dfUS.formatRawCellContents(0.0, -1, "##.##_1"));
|
||||||
|
assertEquals("12.34 ", dfUS.formatRawCellContents(12.343, -1, "##.##_)"));
|
||||||
|
assertEquals("-12.34 ", dfUS.formatRawCellContents(-12.343, -1, "##.##_)"));
|
||||||
|
assertEquals(". ", dfUS.formatRawCellContents(0.0, -1, "##.##_)"));
|
||||||
|
assertEquals(" 12.34", dfUS.formatRawCellContents(12.343, -1, "_-##.##"));
|
||||||
|
assertEquals("- 12.34", dfUS.formatRawCellContents(-12.343, -1, "_-##.##"));
|
||||||
|
assertEquals(" .", dfUS.formatRawCellContents(0.0, -1, "_-##.##"));
|
||||||
|
|
||||||
|
assertEquals("12.34", dfUS.formatRawCellContents(12.343, -1, "##.##* "));
|
||||||
|
assertEquals("-12.34", dfUS.formatRawCellContents(-12.343, -1, "##.##* "));
|
||||||
|
assertEquals(".", dfUS.formatRawCellContents(0.0, -1, "##.##* "));
|
||||||
|
assertEquals("12.34", dfUS.formatRawCellContents(12.343, -1, "##.##*1"));
|
||||||
|
assertEquals("-12.34", dfUS.formatRawCellContents(-12.343, -1, "##.##*1"));
|
||||||
|
assertEquals(".", dfUS.formatRawCellContents(0.0, -1, "##.##*1"));
|
||||||
|
assertEquals("12.34", dfUS.formatRawCellContents(12.343, -1, "##.##*)"));
|
||||||
|
assertEquals("-12.34", dfUS.formatRawCellContents(-12.343, -1, "##.##*)"));
|
||||||
|
assertEquals(".", dfUS.formatRawCellContents(0.0, -1, "##.##*)"));
|
||||||
|
assertEquals("12.34", dfUS.formatRawCellContents(12.343, -1, "*-##.##"));
|
||||||
|
assertEquals("-12.34", dfUS.formatRawCellContents(-12.343, -1, "*-##.##"));
|
||||||
|
assertEquals(".", dfUS.formatRawCellContents(0.0, -1, "*-##.##"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that the special Excel month format MMMMM
|
* Test that the special Excel month format MMMMM
|
||||||
* gets turned into the first letter of the month
|
* gets turned into the first letter of the month
|
||||||
@ -196,14 +228,14 @@ public class TestDataFormatter extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that we can handle elapsed time,
|
* Test that we can handle elapsed time,
|
||||||
* eg formatting 1 day 4 hours as 28 hours
|
* eg formatting 1 day 4 hours as 28 hours
|
||||||
*/
|
*/
|
||||||
public void testElapsedTime() {
|
public void testElapsedTime() {
|
||||||
DataFormatter dfUS = new DataFormatter(Locale.US);
|
DataFormatter dfUS = new DataFormatter(Locale.US);
|
||||||
|
|
||||||
double hour = 1.0/24.0;
|
double hour = 1.0/24.0;
|
||||||
|
|
||||||
assertEquals("01:00", dfUS.formatRawCellContents(1*hour, -1, "hh:mm"));
|
assertEquals("01:00", dfUS.formatRawCellContents(1*hour, -1, "hh:mm"));
|
||||||
assertEquals("05:00", dfUS.formatRawCellContents(5*hour, -1, "hh:mm"));
|
assertEquals("05:00", dfUS.formatRawCellContents(5*hour, -1, "hh:mm"));
|
||||||
assertEquals("20:00", dfUS.formatRawCellContents(20*hour, -1, "hh:mm"));
|
assertEquals("20:00", dfUS.formatRawCellContents(20*hour, -1, "hh:mm"));
|
||||||
@ -212,7 +244,7 @@ public class TestDataFormatter extends TestCase {
|
|||||||
assertEquals("02:00", dfUS.formatRawCellContents(26*hour, -1, "hh:mm"));
|
assertEquals("02:00", dfUS.formatRawCellContents(26*hour, -1, "hh:mm"));
|
||||||
assertEquals("20:00", dfUS.formatRawCellContents(44*hour, -1, "hh:mm"));
|
assertEquals("20:00", dfUS.formatRawCellContents(44*hour, -1, "hh:mm"));
|
||||||
assertEquals("02:00", dfUS.formatRawCellContents(50*hour, -1, "hh:mm"));
|
assertEquals("02:00", dfUS.formatRawCellContents(50*hour, -1, "hh:mm"));
|
||||||
|
|
||||||
assertEquals("01:00", dfUS.formatRawCellContents(1*hour, -1, "[hh]:mm"));
|
assertEquals("01:00", dfUS.formatRawCellContents(1*hour, -1, "[hh]:mm"));
|
||||||
assertEquals("05:00", dfUS.formatRawCellContents(5*hour, -1, "[hh]:mm"));
|
assertEquals("05:00", dfUS.formatRawCellContents(5*hour, -1, "[hh]:mm"));
|
||||||
assertEquals("20:00", dfUS.formatRawCellContents(20*hour, -1, "[hh]:mm"));
|
assertEquals("20:00", dfUS.formatRawCellContents(20*hour, -1, "[hh]:mm"));
|
||||||
@ -221,12 +253,73 @@ public class TestDataFormatter extends TestCase {
|
|||||||
assertEquals("26:00", dfUS.formatRawCellContents(26*hour, -1, "[hh]:mm"));
|
assertEquals("26:00", dfUS.formatRawCellContents(26*hour, -1, "[hh]:mm"));
|
||||||
assertEquals("44:00", dfUS.formatRawCellContents(44*hour, -1, "[hh]:mm"));
|
assertEquals("44:00", dfUS.formatRawCellContents(44*hour, -1, "[hh]:mm"));
|
||||||
assertEquals("50:00", dfUS.formatRawCellContents(50*hour, -1, "[hh]:mm"));
|
assertEquals("50:00", dfUS.formatRawCellContents(50*hour, -1, "[hh]:mm"));
|
||||||
|
|
||||||
assertEquals("30:00", dfUS.formatRawCellContents(0.5*hour, -1, "[mm]:ss"));
|
assertEquals("01", dfUS.formatRawCellContents(1*hour, -1, "[hh]"));
|
||||||
assertEquals("60:00", dfUS.formatRawCellContents(1*hour, -1, "[mm]:ss"));
|
assertEquals("05", dfUS.formatRawCellContents(5*hour, -1, "[hh]"));
|
||||||
assertEquals("120:00", dfUS.formatRawCellContents(2*hour, -1, "[mm]:ss"));
|
assertEquals("20", dfUS.formatRawCellContents(20*hour, -1, "[hh]"));
|
||||||
|
assertEquals("23", dfUS.formatRawCellContents(23*hour, -1, "[hh]"));
|
||||||
|
assertEquals("24", dfUS.formatRawCellContents(24*hour, -1, "[hh]"));
|
||||||
|
assertEquals("26", dfUS.formatRawCellContents(26*hour, -1, "[hh]"));
|
||||||
|
assertEquals("44", dfUS.formatRawCellContents(44*hour, -1, "[hh]"));
|
||||||
|
assertEquals("50", dfUS.formatRawCellContents(50*hour, -1, "[hh]"));
|
||||||
|
|
||||||
|
double minute = 1.0/24.0/60.0;
|
||||||
|
assertEquals("01:00", dfUS.formatRawCellContents(1*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("05:00", dfUS.formatRawCellContents(5*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("20:00", dfUS.formatRawCellContents(20*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("23:00", dfUS.formatRawCellContents(23*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("24:00", dfUS.formatRawCellContents(24*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("26:00", dfUS.formatRawCellContents(26*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("44:00", dfUS.formatRawCellContents(44*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("50:00", dfUS.formatRawCellContents(50*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("59:00", dfUS.formatRawCellContents(59*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("60:00", dfUS.formatRawCellContents(60*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("61:00", dfUS.formatRawCellContents(61*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("119:00", dfUS.formatRawCellContents(119*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("120:00", dfUS.formatRawCellContents(120*minute, -1, "[mm]:ss"));
|
||||||
|
assertEquals("121:00", dfUS.formatRawCellContents(121*minute, -1, "[mm]:ss"));
|
||||||
|
|
||||||
|
assertEquals("01", dfUS.formatRawCellContents(1*minute, -1, "[mm]"));
|
||||||
|
assertEquals("05", dfUS.formatRawCellContents(5*minute, -1, "[mm]"));
|
||||||
|
assertEquals("20", dfUS.formatRawCellContents(20*minute, -1, "[mm]"));
|
||||||
|
assertEquals("23", dfUS.formatRawCellContents(23*minute, -1, "[mm]"));
|
||||||
|
assertEquals("24", dfUS.formatRawCellContents(24*minute, -1, "[mm]"));
|
||||||
|
assertEquals("26", dfUS.formatRawCellContents(26*minute, -1, "[mm]"));
|
||||||
|
assertEquals("44", dfUS.formatRawCellContents(44*minute, -1, "[mm]"));
|
||||||
|
assertEquals("50", dfUS.formatRawCellContents(50*minute, -1, "[mm]"));
|
||||||
|
assertEquals("59", dfUS.formatRawCellContents(59*minute, -1, "[mm]"));
|
||||||
|
assertEquals("60", dfUS.formatRawCellContents(60*minute, -1, "[mm]"));
|
||||||
|
assertEquals("61", dfUS.formatRawCellContents(61*minute, -1, "[mm]"));
|
||||||
|
assertEquals("119", dfUS.formatRawCellContents(119*minute, -1, "[mm]"));
|
||||||
|
assertEquals("120", dfUS.formatRawCellContents(120*minute, -1, "[mm]"));
|
||||||
|
assertEquals("121", dfUS.formatRawCellContents(121*minute, -1, "[mm]"));
|
||||||
|
|
||||||
|
double second = 1.0/24.0/60.0/60.0;
|
||||||
|
assertEquals("86400", dfUS.formatRawCellContents(86400*second, -1, "[ss]"));
|
||||||
|
assertEquals("01", dfUS.formatRawCellContents(1*second, -1, "[ss]"));
|
||||||
|
assertEquals("05", dfUS.formatRawCellContents(5*second, -1, "[ss]"));
|
||||||
|
assertEquals("20", dfUS.formatRawCellContents(20*second, -1, "[ss]"));
|
||||||
|
assertEquals("23", dfUS.formatRawCellContents(23*second, -1, "[ss]"));
|
||||||
|
assertEquals("24", dfUS.formatRawCellContents(24*second, -1, "[ss]"));
|
||||||
|
assertEquals("26", dfUS.formatRawCellContents(26*second, -1, "[ss]"));
|
||||||
|
assertEquals("44", dfUS.formatRawCellContents(44*second, -1, "[ss]"));
|
||||||
|
assertEquals("50", dfUS.formatRawCellContents(50*second, -1, "[ss]"));
|
||||||
|
assertEquals("59", dfUS.formatRawCellContents(59*second, -1, "[ss]"));
|
||||||
|
assertEquals("60", dfUS.formatRawCellContents(60*second, -1, "[ss]"));
|
||||||
|
assertEquals("61", dfUS.formatRawCellContents(61*second, -1, "[ss]"));
|
||||||
|
assertEquals("119", dfUS.formatRawCellContents(119*second, -1, "[ss]"));
|
||||||
|
assertEquals("120", dfUS.formatRawCellContents(120*second, -1, "[ss]"));
|
||||||
|
assertEquals("121", dfUS.formatRawCellContents(121*second, -1, "[ss]"));
|
||||||
|
|
||||||
|
assertEquals("28:48:00",dfUS.formatRawCellContents(1.2, -1, "[h]:mm:ss"));
|
||||||
|
|
||||||
|
assertEquals("57:07.2", dfUS.formatRawCellContents(.123, -1, "mm:ss.0;@"));
|
||||||
|
assertEquals("57:41.8", dfUS.formatRawCellContents(.1234, -1, "mm:ss.0;@"));
|
||||||
|
assertEquals("57:41.76", dfUS.formatRawCellContents(.1234, -1, "mm:ss.00;@"));
|
||||||
|
assertEquals("57:41.760", dfUS.formatRawCellContents(.1234, -1, "mm:ss.000;@"));
|
||||||
|
assertEquals("24:00.0", dfUS.formatRawCellContents(123456.6, -1, "mm:ss.0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDateWindowing() {
|
public void testDateWindowing() {
|
||||||
DataFormatter dfUS = new DataFormatter(Locale.US);
|
DataFormatter dfUS = new DataFormatter(Locale.US);
|
||||||
|
|
||||||
@ -234,4 +327,31 @@ public class TestDataFormatter extends TestCase {
|
|||||||
assertEquals("1899-12-31 00:00:00", dfUS.formatRawCellContents(0.0, -1, "yyyy-mm-dd hh:mm:ss", false));
|
assertEquals("1899-12-31 00:00:00", dfUS.formatRawCellContents(0.0, -1, "yyyy-mm-dd hh:mm:ss", false));
|
||||||
assertEquals("1904-01-01 00:00:00", dfUS.formatRawCellContents(0.0, -1, "yyyy-mm-dd hh:mm:ss", true));
|
assertEquals("1904-01-01 00:00:00", dfUS.formatRawCellContents(0.0, -1, "yyyy-mm-dd hh:mm:ss", true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testScientificNotation() {
|
||||||
|
DataFormatter dfUS = new DataFormatter(Locale.US);
|
||||||
|
|
||||||
|
assertEquals("1.23E+01", dfUS.formatRawCellContents(12.343, -1, "0.00E+00"));
|
||||||
|
assertEquals("-1.23E+01", dfUS.formatRawCellContents(-12.343, -1, "0.00E+00"));
|
||||||
|
assertEquals("0.00E+00", dfUS.formatRawCellContents(0.0, -1, "0.00E+00"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidDate() {
|
||||||
|
DataFormatter df1 = new DataFormatter(Locale.US);
|
||||||
|
assertEquals("-1.0", df1.formatRawCellContents(-1, -1, "mm/dd/yyyy"));
|
||||||
|
|
||||||
|
DataFormatter df2 = new DataFormatter(Locale.US, true);
|
||||||
|
assertEquals("###############################################################################################################################################################################################################################################################",
|
||||||
|
df2.formatRawCellContents(-1, -1, "mm/dd/yyyy"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testOther() {
|
||||||
|
DataFormatter dfUS = new DataFormatter(Locale.US, true);
|
||||||
|
|
||||||
|
assertEquals(" 12.34 ", dfUS.formatRawCellContents(12.34, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
|
||||||
|
assertEquals("-12.34 ", dfUS.formatRawCellContents(-12.34, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
|
||||||
|
assertEquals(" - ", dfUS.formatRawCellContents(0.0, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
|
||||||
|
assertEquals(" $- ", dfUS.formatRawCellContents(0.0, -1, "_-$* #,##0.00_-;-$* #,##0.00_-;_-$* \"-\"??_-;_-@_-"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user