From 612d99b8eba46cc7ce9a9c790082121d9849a6fb Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Tue, 4 Dec 2007 17:06:57 +0000 Subject: [PATCH] Support 1904 date windowing, in addition to 1900 date windowing (patch from bug #43551) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@601004 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 1 + src/documentation/content/xdocs/status.xml | 1 + .../apache/poi/hssf/usermodel/HSSFCell.java | 2 +- .../poi/hssf/usermodel/HSSFDateUtil.java | 94 +++++++------ .../eventusermodel/examples/XLS2CSVmra.java | 2 +- .../hssf/record/formula/functions/Date.java | 2 +- .../hssf/record/formula/functions/Day.java | 2 +- .../hssf/record/formula/functions/Month.java | 2 +- .../hssf/record/formula/functions/Year.java | 2 +- .../poi/hssf/usermodel/TestHSSFCell.java | 131 +++++++++++++----- .../poi/hssf/usermodel/TestHSSFDateUtil.java | 90 +++++++----- .../hssf/usermodel/TestReadWriteChart.java | 2 +- 12 files changed, 221 insertions(+), 110 deletions(-) diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index ef6511128e..bc26a060a6 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -36,6 +36,7 @@ + 43551 - [PATCH] Support for 1904 date windowing in HSSF (previously only supported 1900 date windowing) 41064 - [PATCH] Support for String continue records 27511 - [PATCH] Support for data validation, via DVRecord and DVALRecord 43877 and 39512 - Fix for handling mixed OBJ and CONTINUE records. diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index d09ccb87cc..4675e56e32 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,6 +33,7 @@ + 43551 - [PATCH] Support for 1904 date windowing in HSSF (previously only supported 1900 date windowing) 41064 - [PATCH] Support for String continue records 27511 - [PATCH] Support for data validation, via DVRecord and DVALRecord 43877 - Fix for handling mixed OBJ and CONTINUE records diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index ac3943da46..9cc4550719 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -521,7 +521,7 @@ public class HSSFCell */ public void setCellValue(Date value) { - setCellValue(HSSFDateUtil.getExcelDate(value)); + setCellValue(HSSFDateUtil.getExcelDate(value, this.book.isUsing1904DateWindowing())); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java index fc9ac3a3b3..fb3a92df86 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java @@ -35,6 +35,8 @@ import java.util.GregorianCalendar; * @author Glen Stampoultzis (glens at apache.org) * @author Dan Sherman (dsherman at isisph.com) * @author Hack Kampbjorn (hak at 2mba.dk) + * @author Alex Jacoby (ajacoby at gmail.com) + * @author Pavel Krupets (pkrupets at palmtreebusiness dot com) */ public class HSSFDateUtil @@ -54,19 +56,26 @@ public class HSSFDateUtil * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1) * @param date the Date */ - - public static double getExcelDate(Date date) - { + public static double getExcelDate(Date date) { + return getExcelDate(date, false); + } + /** + * Given a Date, converts it into a double representing its internal Excel representation, + * which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds. + * + * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1) + * @param date the Date + * @param use1904windowing Should 1900 or 1904 date windowing be used? + */ + public static double getExcelDate(Date date, boolean use1904windowing) { Calendar calStart = new GregorianCalendar(); - - calStart.setTime( - date); // If date includes hours, minutes, and seconds, set them to 0 - if (calStart.get(Calendar.YEAR) < 1900) + calStart.setTime(date); // If date includes hours, minutes, and seconds, set them to 0 + + if ((!use1904windowing && calStart.get(Calendar.YEAR) < 1900) || + (use1904windowing && calStart.get(Calendar.YEAR) < 1904)) { return BAD_DATE; - } - else - { + } else { // Because of daylight time saving we cannot use // date.getTime() - calStart.getTimeInMillis() // as the difference in milliseconds between 00:00 and 04:00 @@ -80,32 +89,39 @@ public class HSSFDateUtil ) * 1000 + calStart.get(Calendar.MILLISECOND) ) / ( double ) DAY_MILLISECONDS; calStart = dayStart(calStart); - - double value = fraction + absoluteDay(calStart); - if (value >= 60) { - value += 1; + double value = fraction + absoluteDay(calStart, use1904windowing); + + if (!use1904windowing && value >= 60) { + value++; + } else if (use1904windowing) { + value--; } return value; } } - - /** - * Given a excel date, converts it into a Date. - * Assumes 1900 date windowing. - * - * @param date the Excel Date - * - * @return Java representation of a date (null if error) - * @see #getJavaDate(double,boolean) - */ - - public static Date getJavaDate(double date) - { - return getJavaDate(date,false); - } + /** + * Given an Excel date with using 1900 date windowing, and + * converts it to a java.util.Date. + * + * NOTE: If the default TimeZone in Java uses Daylight + * Saving Time then the conversion back to an Excel date may not give + * the same value, that is the comparison + * excelDate == getExcelDate(getJavaDate(excelDate,false)) + * is not always true. For example if default timezone is + * Europe/Copenhagen, on 2004-03-28 the minute after + * 01:59 CET is 03:00 CEST, if the excel date represents a time between + * 02:00 and 03:00 then it is converted to past 03:00 summer time + * + * @param date The Excel date. + * @return Java representation of the date, or null if date is not a valid Excel date + * @see java.util.TimeZone + */ + public static Date getJavaDate(double date) { + return getJavaDate(date, false); + } /** * Given an Excel date with either 1900 or 1904 date windowing, * converts it to a java.util.Date. @@ -142,7 +158,7 @@ public class HSSFDateUtil GregorianCalendar calendar = new GregorianCalendar(startYear,0, wholeDays + dayAdjust); int millisecondsInDay = (int)((date - Math.floor(date)) * - (double) DAY_MILLISECONDS + 0.5); + DAY_MILLISECONDS + 0.5); calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay); return calendar.getTime(); } @@ -238,7 +254,7 @@ public class HSSFDateUtil * Check if a cell contains a date * Since dates are stored internally in Excel as double values * we infer it is a date if it is formatted as such. - * @see #isADateFormat(int,string) + * @see #isADateFormat(int, String) * @see #isInternalDateFormat(int) */ public static boolean isCellDateFormatted(HSSFCell cell) { @@ -259,7 +275,7 @@ public class HSSFDateUtil * excel date formats. * As Excel stores a great many of its dates in "non-internal" * date formats, you will not normally want to use this method. - * @see #isADateFormat(int,string) + * @see #isADateFormat(int,String) * @see #isInternalDateFormat(int) */ public static boolean isCellInternalDateFormatted(HSSFCell cell) { @@ -296,10 +312,10 @@ public class HSSFDateUtil * @exception IllegalArgumentException if date is invalid */ - private static int absoluteDay(Calendar cal) + static int absoluteDay(Calendar cal, boolean use1904windowing) { return cal.get(Calendar.DAY_OF_YEAR) - + daysInPriorYears(cal.get(Calendar.YEAR)); + + daysInPriorYears(cal.get(Calendar.YEAR), use1904windowing); } /** @@ -307,14 +323,14 @@ public class HSSFDateUtil * * @return days number of days in years prior to yr. * @param yr a year (1900 < yr < 4000) + * @param use1904windowing * @exception IllegalArgumentException if year is outside of range. */ - private static int daysInPriorYears(int yr) + private static int daysInPriorYears(int yr, boolean use1904windowing) { - if (yr < 1900) { - throw new IllegalArgumentException( - "'year' must be 1900 or greater"); + if ((!use1904windowing && yr < 1900) || (use1904windowing && yr < 1900)) { + throw new IllegalArgumentException("'year' must be 1900 or greater"); } int yr1 = yr - 1; @@ -323,7 +339,7 @@ public class HSSFDateUtil + yr1 / 400 // plus years divisible by 400 - 460; // leap days in previous 1900 years - return 365 * (yr - 1900) + leapDays; + return 365 * (yr - (use1904windowing ? 1904 : 1900)) + leapDays; } // set HH:MM:SS fields of cal to 00:00:00:000 diff --git a/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java b/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java index 5f7392739f..1c8d994bfe 100644 --- a/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java +++ b/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java @@ -283,7 +283,7 @@ public class XLS2CSVmra implements HSSFListener { format = format.replaceAll("\\\\-","-"); // Format as a date - Date d = HSSFDateUtil.getJavaDate(value); + Date d = HSSFDateUtil.getJavaDate(value, false); DateFormat df = new SimpleDateFormat(format); return df.format(d); } else { diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Date.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Date.java index 34dcc7cdae..6861a02524 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Date.java +++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Date.java @@ -70,7 +70,7 @@ public class Date extends NumericFunction { c.set(year, month, day, 0, 0, 0); c.set(Calendar.MILLISECOND, 0); - return new NumberEval(HSSFDateUtil.getExcelDate(c.getTime())); + return new NumberEval(HSSFDateUtil.getExcelDate(c.getTime(), false)); // XXX fix 1900/1904 problem } } diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Day.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Day.java index 283633442e..0ae5694767 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Day.java +++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Day.java @@ -46,7 +46,7 @@ srcCellRow, srcCellCol); if (ve instanceof NumericValueEval) { NumericValueEval ne = (NumericValueEval) ve; if (HSSFDateUtil.isValidExcelDate(ne.getNumberValue())) { - java.util.Date d = HSSFDateUtil.getJavaDate(ne.getNumberValue()); + java.util.Date d = HSSFDateUtil.getJavaDate(ne.getNumberValue(), false); // XXX fix 1900/1904 problem java.util.Calendar c = java.util.Calendar.getInstance(); c.setTime(d); retval = new NumberEval(c.get(java.util.Calendar.DAY_OF_MONTH)); diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Month.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Month.java index 065c565d14..d5178b22df 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Month.java +++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Month.java @@ -50,7 +50,7 @@ public class Month extends NumericFunction { if (ve instanceof NumericValueEval) { NumericValueEval ne = (NumericValueEval) ve; if (HSSFDateUtil.isValidExcelDate(ne.getNumberValue())) { - java.util.Date d = HSSFDateUtil.getJavaDate(ne.getNumberValue()); + java.util.Date d = HSSFDateUtil.getJavaDate(ne.getNumberValue(), false); // XXX fix 1900/1904 problem retval = new NumberEval(d.getMonth()+1); } else { retval = ErrorEval.NUM_ERROR; diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Year.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Year.java index 76ea617cc6..b461a09668 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Year.java +++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Year.java @@ -51,7 +51,7 @@ public class Year extends NumericFunction { if (ve instanceof NumericValueEval) { NumericValueEval ne = (NumericValueEval) ve; if (HSSFDateUtil.isValidExcelDate(ne.getNumberValue())) { - java.util.Date d = HSSFDateUtil.getJavaDate(ne.getNumberValue()); + java.util.Date d = HSSFDateUtil.getJavaDate(ne.getNumberValue(), false); // XXX fix 1900/1904 problem retval = new NumberEval(d.getYear()+1900); } else { retval = ErrorEval.NUM_ERROR; diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java index 5742660260..ee3cace263 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java @@ -44,6 +44,7 @@ import java.util.GregorianCalendar; * paticular datatypes, etc. * @author Andrew C. Oliver (andy at superlinksoftware dot com) * @author Dan Sherman (dsherman at isisph.com) + * @author Alex Jacoby (ajacoby at gmail.com) */ public class TestHSSFCell @@ -107,42 +108,106 @@ extends TestCase { } /** - * Checks that the recognition of files using 1904 date windowing - * is working properly. Conversion of the date is also an issue, - * but there's a separate unit test for that. - */ - public void testDateWindowing() throws Exception { - GregorianCalendar cal = new GregorianCalendar(2000,0,1); // Jan. 1, 2000 - Date date = cal.getTime(); - String path = System.getProperty("HSSF.testdata.path"); + * Checks that the recognition of files using 1904 date windowing + * is working properly. Conversion of the date is also an issue, + * but there's a separate unit test for that. + */ + public void testDateWindowingRead() throws Exception { + GregorianCalendar cal = new GregorianCalendar(2000,0,1); // Jan. 1, 2000 + Date date = cal.getTime(); + String path = System.getProperty("HSSF.testdata.path"); - // first check a file with 1900 Date Windowing - String filename = path + "/1900DateWindowing.xls"; - FileInputStream stream = new FileInputStream(filename); - POIFSFileSystem fs = new POIFSFileSystem(stream); - HSSFWorkbook workbook = new HSSFWorkbook(fs); - HSSFSheet sheet = workbook.getSheetAt(0); + // first check a file with 1900 Date Windowing + String filename = path + "/1900DateWindowing.xls"; + FileInputStream stream = new FileInputStream(filename); + POIFSFileSystem fs = new POIFSFileSystem(stream); + HSSFWorkbook workbook = new HSSFWorkbook(fs); + HSSFSheet sheet = workbook.getSheetAt(0); - assertEquals("Date from file using 1900 Date Windowing", - date.getTime(), - sheet.getRow(0).getCell((short)0) - .getDateCellValue().getTime()); - stream.close(); - - // now check a file with 1904 Date Windowing - filename = path + "/1904DateWindowing.xls"; - stream = new FileInputStream(filename); - fs = new POIFSFileSystem(stream); - workbook = new HSSFWorkbook(fs); - sheet = workbook.getSheetAt(0); + assertEquals("Date from file using 1900 Date Windowing", + date.getTime(), + sheet.getRow(0).getCell((short)0) + .getDateCellValue().getTime()); + stream.close(); + + // now check a file with 1904 Date Windowing + filename = path + "/1904DateWindowing.xls"; + stream = new FileInputStream(filename); + fs = new POIFSFileSystem(stream); + workbook = new HSSFWorkbook(fs); + sheet = workbook.getSheetAt(0); - assertEquals("Date from file using 1904 Date Windowing", - date.getTime(), - sheet.getRow(0).getCell((short)0) - .getDateCellValue().getTime()); - stream.close(); - } - + assertEquals("Date from file using 1904 Date Windowing", + date.getTime(), + sheet.getRow(0).getCell((short)0) + .getDateCellValue().getTime()); + stream.close(); + } + + /** + * Checks that dates are properly written to both types of files: + * those with 1900 and 1904 date windowing. Note that if the + * previous test ({@link #testDateWindowingRead}) fails, the + * results of this test are meaningless. + */ + public void testDateWindowingWrite() throws Exception { + GregorianCalendar cal = new GregorianCalendar(2000,0,1); // Jan. 1, 2000 + Date date = cal.getTime(); + String path = System.getProperty("HSSF.testdata.path"); + + // first check a file with 1900 Date Windowing + String filename = path + "/1900DateWindowing.xls"; + writeCell(filename, 0, (short) 1, date); + assertEquals("Date from file using 1900 Date Windowing", + date.getTime(), + readCell(filename, 0, (short) 1).getTime()); + + // now check a file with 1904 Date Windowing + filename = path + "/1904DateWindowing.xls"; + writeCell(filename, 0, (short) 1, date); + assertEquals("Date from file using 1900 Date Windowing", + date.getTime(), + readCell(filename, 0, (short) 1).getTime()); + } + + /** + * Sets cell value and writes file. + */ + private void writeCell(String filename, + int rowIdx, short colIdx, Date date) throws Exception { + FileInputStream stream = new FileInputStream(filename); + POIFSFileSystem fs = new POIFSFileSystem(stream); + HSSFWorkbook workbook = new HSSFWorkbook(fs); + HSSFSheet sheet = workbook.getSheetAt(0); + HSSFRow row = sheet.getRow(rowIdx); + HSSFCell cell = row.getCell(colIdx); + + if (cell == null) { + cell = row.createCell(colIdx); + } + cell.setCellValue(date); + + // Write the file + stream.close(); + FileOutputStream oStream = new FileOutputStream(filename); + workbook.write(oStream); + oStream.close(); + } + + /** + * Reads cell value from file. + */ + private Date readCell(String filename, + int rowIdx, short colIdx) throws Exception { + FileInputStream stream = new FileInputStream(filename); + POIFSFileSystem fs = new POIFSFileSystem(stream); + HSSFWorkbook workbook = new HSSFWorkbook(fs); + HSSFSheet sheet = workbook.getSheetAt(0); + HSSFRow row = sheet.getRow(rowIdx); + HSSFCell cell = row.getCell(colIdx); + return cell.getDateCellValue(); + } + /** * Tests that the active cell can be correctly read and set */ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java index f8aeb8517e..6b37f749ee 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java @@ -36,6 +36,8 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; * * @author Dan Sherman (dsherman at isisph.com) * @author Hack Kampbjorn (hak at 2mba.dk) + * @author Pavel Krupets (pkrupets at palmtreebusiness dot com) + * @author Alex Jacoby (ajacoby at gmail.com) * @version %I%, %G% */ @@ -47,7 +49,9 @@ public class TestHSSFDateUtil public static final int CALENDAR_FEBRUARY = 1; public static final int CALENDAR_MARCH = 2; public static final int CALENDAR_APRIL = 3; - + public static final int CALENDAR_JULY = 6; + public static final int CALENDAR_OCTOBER = 9; + public TestHSSFDateUtil(String s) { super(s); @@ -67,10 +71,10 @@ public class TestHSSFDateUtil GregorianCalendar date = new GregorianCalendar(2002, 0, 1, hour, 1, 1); double excelDate = - HSSFDateUtil.getExcelDate(date.getTime()); + HSSFDateUtil.getExcelDate(date.getTime(), false); assertEquals("Checking hour = " + hour, date.getTime().getTime(), - HSSFDateUtil.getJavaDate(excelDate).getTime()); + HSSFDateUtil.getJavaDate(excelDate, false).getTime()); } // check 1900 and 1904 date windowing conversions @@ -99,7 +103,7 @@ public class TestHSSFDateUtil public void testExcelConversionOnDSTStart() { TimeZone cet = TimeZone.getTimeZone("Europe/Copenhagen"); TimeZone.setDefault(cet); - Calendar cal = new GregorianCalendar(2004, Calendar.MARCH, 28); + Calendar cal = new GregorianCalendar(2004, CALENDAR_MARCH, 28); for (int hour = 0; hour < 24; hour++) { // Skip 02:00 CET as that is the Daylight change time @@ -110,7 +114,7 @@ public class TestHSSFDateUtil cal.set(Calendar.HOUR_OF_DAY, hour); Date javaDate = cal.getTime(); - double excelDate = HSSFDateUtil.getExcelDate(javaDate); + double excelDate = HSSFDateUtil.getExcelDate(javaDate, false); double difference = excelDate - Math.floor(excelDate); int differenceInHours = (int) (difference * 24 * 60 + 0.5) / 60; assertEquals("Checking " + hour + " hour on Daylight Saving Time start date", @@ -118,7 +122,7 @@ public class TestHSSFDateUtil differenceInHours); assertEquals("Checking " + hour + " hour on Daylight Saving Time start date", javaDate.getTime(), - HSSFDateUtil.getJavaDate(excelDate).getTime()); + HSSFDateUtil.getJavaDate(excelDate, false).getTime()); } } @@ -129,8 +133,8 @@ public class TestHSSFDateUtil public void testJavaConversionOnDSTStart() { TimeZone cet = TimeZone.getTimeZone("Europe/Copenhagen"); TimeZone.setDefault(cet); - Calendar cal = new GregorianCalendar(2004, Calendar.MARCH, 28); - double excelDate = HSSFDateUtil.getExcelDate(cal.getTime()); + Calendar cal = new GregorianCalendar(2004, CALENDAR_MARCH, 28); + double excelDate = HSSFDateUtil.getExcelDate(cal.getTime(), false); double oneHour = 1.0 / 24; double oneMinute = oneHour / 60; for (int hour = 0; hour < 24; hour++, excelDate += oneHour) { @@ -142,10 +146,10 @@ public class TestHSSFDateUtil } cal.set(Calendar.HOUR_OF_DAY, hour); - Date javaDate = HSSFDateUtil.getJavaDate(excelDate); + Date javaDate = HSSFDateUtil.getJavaDate(excelDate, false); assertEquals("Checking " + hour + " hours on Daylight Saving Time start date", excelDate, - HSSFDateUtil.getExcelDate(javaDate), oneMinute); + HSSFDateUtil.getExcelDate(javaDate, false), oneMinute); } } @@ -156,11 +160,11 @@ public class TestHSSFDateUtil public void testExcelConversionOnDSTEnd() { TimeZone cet = TimeZone.getTimeZone("Europe/Copenhagen"); TimeZone.setDefault(cet); - Calendar cal = new GregorianCalendar(2004, Calendar.OCTOBER, 31); + Calendar cal = new GregorianCalendar(2004, CALENDAR_OCTOBER, 31); for (int hour = 0; hour < 24; hour++) { cal.set(Calendar.HOUR_OF_DAY, hour); Date javaDate = cal.getTime(); - double excelDate = HSSFDateUtil.getExcelDate(javaDate); + double excelDate = HSSFDateUtil.getExcelDate(javaDate, false); double difference = excelDate - Math.floor(excelDate); int differenceInHours = (int) (difference * 24 * 60 + 0.5) / 60; assertEquals("Checking " + hour + " hour on Daylight Saving Time end date", @@ -168,7 +172,7 @@ public class TestHSSFDateUtil differenceInHours); assertEquals("Checking " + hour + " hour on Daylight Saving Time start date", javaDate.getTime(), - HSSFDateUtil.getJavaDate(excelDate).getTime()); + HSSFDateUtil.getJavaDate(excelDate, false).getTime()); } } @@ -179,16 +183,16 @@ public class TestHSSFDateUtil public void testJavaConversionOnDSTEnd() { TimeZone cet = TimeZone.getTimeZone("Europe/Copenhagen"); TimeZone.setDefault(cet); - Calendar cal = new GregorianCalendar(2004, Calendar.OCTOBER, 31); - double excelDate = HSSFDateUtil.getExcelDate(cal.getTime()); + Calendar cal = new GregorianCalendar(2004, CALENDAR_OCTOBER, 31); + double excelDate = HSSFDateUtil.getExcelDate(cal.getTime(), false); double oneHour = 1.0 / 24; double oneMinute = oneHour / 60; for (int hour = 0; hour < 24; hour++, excelDate += oneHour) { cal.set(Calendar.HOUR_OF_DAY, hour); - Date javaDate = HSSFDateUtil.getJavaDate(excelDate); + Date javaDate = HSSFDateUtil.getJavaDate(excelDate, false); assertEquals("Checking " + hour + " hours on Daylight Saving Time start date", excelDate, - HSSFDateUtil.getExcelDate(javaDate), oneMinute); + HSSFDateUtil.getExcelDate(javaDate, false), oneMinute); } } @@ -315,25 +319,38 @@ public class TestHSSFDateUtil } public void testDateBug_2Excel() { - assertEquals(59.0, HSSFDateUtil.getExcelDate(createDate(1900, CALENDAR_FEBRUARY, 28)), 0.00001); - assertEquals(61.0, HSSFDateUtil.getExcelDate(createDate(1900, CALENDAR_MARCH, 1)), 0.00001); + assertEquals(59.0, HSSFDateUtil.getExcelDate(createDate(1900, CALENDAR_FEBRUARY, 28), false), 0.00001); + assertEquals(61.0, HSSFDateUtil.getExcelDate(createDate(1900, CALENDAR_MARCH, 1), false), 0.00001); - assertEquals(37315.00, HSSFDateUtil.getExcelDate(createDate(2002, CALENDAR_FEBRUARY, 28)), 0.00001); - assertEquals(37316.00, HSSFDateUtil.getExcelDate(createDate(2002, CALENDAR_MARCH, 1)), 0.00001); - assertEquals(37257.00, HSSFDateUtil.getExcelDate(createDate(2002, CALENDAR_JANUARY, 1)), 0.00001); - assertEquals(38074.00, HSSFDateUtil.getExcelDate(createDate(2004, CALENDAR_MARCH, 28)), 0.00001); + assertEquals(37315.00, HSSFDateUtil.getExcelDate(createDate(2002, CALENDAR_FEBRUARY, 28), false), 0.00001); + assertEquals(37316.00, HSSFDateUtil.getExcelDate(createDate(2002, CALENDAR_MARCH, 1), false), 0.00001); + assertEquals(37257.00, HSSFDateUtil.getExcelDate(createDate(2002, CALENDAR_JANUARY, 1), false), 0.00001); + assertEquals(38074.00, HSSFDateUtil.getExcelDate(createDate(2004, CALENDAR_MARCH, 28), false), 0.00001); } public void testDateBug_2Java() { - assertEquals(createDate(1900, Calendar.FEBRUARY, 28), HSSFDateUtil.getJavaDate(59.0)); - assertEquals(createDate(1900, Calendar.MARCH, 1), HSSFDateUtil.getJavaDate(61.0)); + assertEquals(createDate(1900, CALENDAR_FEBRUARY, 28), HSSFDateUtil.getJavaDate(59.0, false)); + assertEquals(createDate(1900, CALENDAR_MARCH, 1), HSSFDateUtil.getJavaDate(61.0, false)); - assertEquals(createDate(2002, Calendar.FEBRUARY, 28), HSSFDateUtil.getJavaDate(37315.00)); - assertEquals(createDate(2002, Calendar.MARCH, 1), HSSFDateUtil.getJavaDate(37316.00)); - assertEquals(createDate(2002, Calendar.JANUARY, 1), HSSFDateUtil.getJavaDate(37257.00)); - assertEquals(createDate(2004, Calendar.MARCH, 28), HSSFDateUtil.getJavaDate(38074.00)); + assertEquals(createDate(2002, CALENDAR_FEBRUARY, 28), HSSFDateUtil.getJavaDate(37315.00, false)); + assertEquals(createDate(2002, CALENDAR_MARCH, 1), HSSFDateUtil.getJavaDate(37316.00, false)); + assertEquals(createDate(2002, CALENDAR_JANUARY, 1), HSSFDateUtil.getJavaDate(37257.00, false)); + assertEquals(createDate(2004, CALENDAR_MARCH, 28), HSSFDateUtil.getJavaDate(38074.00, false)); } - + + public void testDate1904() { + assertEquals(createDate(1904, CALENDAR_JANUARY, 2), HSSFDateUtil.getJavaDate(1.0, true)); + assertEquals(createDate(1904, CALENDAR_JANUARY, 1), HSSFDateUtil.getJavaDate(0.0, true)); + assertEquals(0.0, HSSFDateUtil.getExcelDate(createDate(1904, CALENDAR_JANUARY, 1), true), 0.00001); + assertEquals(1.0, HSSFDateUtil.getExcelDate(createDate(1904, CALENDAR_JANUARY, 2), true), 0.00001); + + assertEquals(createDate(1998, CALENDAR_JULY, 5), HSSFDateUtil.getJavaDate(35981, false)); + assertEquals(createDate(1998, CALENDAR_JULY, 5), HSSFDateUtil.getJavaDate(34519, true)); + + assertEquals(35981.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), false), 0.00001); + assertEquals(34519.0, HSSFDateUtil.getExcelDate(createDate(1998, CALENDAR_JULY, 5), true), 0.00001); + } + private Date createDate(int year, int month, int day) { Calendar c = new GregorianCalendar(); c.set(year, month, day, 0, 0, 0); @@ -341,10 +358,21 @@ public class TestHSSFDateUtil return c.getTime(); } + /** + * Check if HSSFDateUtil.getAbsoluteDay works as advertised. + */ + public void testAbsoluteDay() { + // 1 Jan 1900 is 1 day after 31 Dec 1899 + GregorianCalendar calendar = new GregorianCalendar(1900, 0, 1); + assertEquals("Checking absolute day (1 Jan 1900)", 1, HSSFDateUtil.absoluteDay(calendar, false)); + // 1 Jan 1901 is 366 days after 31 Dec 1899 + calendar = new GregorianCalendar(1901, 0, 1); + assertEquals("Checking absolute day (1 Jan 1901)", 366, HSSFDateUtil.absoluteDay(calendar, false)); + } + public static void main(String [] args) { System.out .println("Testing org.apache.poi.hssf.usermodel.TestHSSFDateUtil"); junit.textui.TestRunner.run(TestHSSFDateUtil.class); } } - diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java b/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java index f53b2f88da..7f2819433b 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestReadWriteChart.java @@ -63,7 +63,7 @@ public class TestReadWriteChart //System.out.println("first assertion for date"); assertEquals(new GregorianCalendar(2000, 0, 1, 10, 51, 2).getTime(), HSSFDateUtil - .getJavaDate(firstCell.getNumericCellValue())); + .getJavaDate(firstCell.getNumericCellValue(), false)); HSSFRow row = sheet.createRow(( short ) 15); HSSFCell cell = row.createCell(( short ) 1);