Index: src/java/org/apache/poi/hssf/model/Workbook.java =================================================================== RCS file: /home/cvspublic/jakarta-poi/src/java/org/apache/poi/hssf/model/Workbook.java,v --- src/java/org/apache/poi/hssf/model/Workbook.java 5 Oct 2002 02:38:06 -0000 1.18 +++ src/java/org/apache/poi/hssf/model/Workbook.java 7 Nov 2002 22:55:08 -0000 @@ -88,6 +88,7 @@ * @author Andrew C. Oliver (acoliver at apache dot org) * @author Glen Stampoultzis (glens at apache.org) * @author Sergei Kozello (sergeikozello at mail.ru) + * @author Dan Sherman (dsherman at isisph.com) * @see org.apache.poi.hssf.usermodel.HSSFWorkbook * @version 1.0-pre */ @@ -155,6 +156,8 @@ 0; // holds the position of sup book private short maxformatid = -1; // holds the max format id + private boolean uses1904datewindowing = + false; // whether 1904 date windowing is being used private static POILogger log = POILogFactory.getLogger(Workbook.class); @@ -248,6 +251,10 @@ retval.formats.add(rec); retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode(); break; + case DateWindow1904Record.sid : + log.log(DEBUG, "found datewindow1904 record at " + k); + retval.uses1904datewindowing = ((DateWindow1904Record)rec).getWindowing() == 1; + break; default : } @@ -1900,5 +1907,15 @@ public List getRecords() { return records; + } + + /** + * Whether date windowing is based on 1/2/1904 or 1/1/1900. + * Some versions of Excel (Mac) can save workbooks using 1904 date windowing. + * + * @return true if using 1904 date windowing + */ + public boolean isUsing1904DateWindowing() { + return uses1904datewindowing; } } Index: src/java/org/apache/poi/hssf/usermodel/HSSFCell.java =================================================================== RCS file: /home/cvspublic/jakarta-poi/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java,v --- src/java/org/apache/poi/hssf/usermodel/HSSFCell.java 11 Sep 2002 12:14:28 -0000 1.18 +++ src/java/org/apache/poi/hssf/usermodel/HSSFCell.java 7 Nov 2002 22:55:09 -0000 @@ -94,6 +94,7 @@ * NOTE: the alpha won't be implementing formulas * * @author Andrew C. Oliver (acoliver at apache dot org) + * @author Dan Sherman (dsherman at isisph.com) * @version 1.0-pre */ @@ -783,7 +784,12 @@ throw new NumberFormatException( "You cannot get a date value from an error cell"); } - return HSSFDateUtil.getJavaDate(cellValue); + if (book.isUsing1904DateWindowing()) { + return HSSFDateUtil.getJavaDate(cellValue,true); + } + else { + return HSSFDateUtil.getJavaDate(cellValue,false); + } } /** Index: src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java =================================================================== RCS file: /home/cvspublic/jakarta-poi/src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java,v --- src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java 1 Oct 2002 20:40:29 -0000 1.4 +++ src/java/org/apache/poi/hssf/usermodel/HSSFDateUtil.java 7 Nov 2002 22:55:09 -0000 @@ -69,6 +69,7 @@ * * @author Michael Harhen * @author Glen Stampoultzis (glens at apache.org) + * @author Dan Sherman (dsherman at isisph.com) */ public class HSSFDateUtil @@ -115,33 +116,54 @@ /** * 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) { - if (isValidExcelDate(date)) - { - int wholeDaysSince1900 = ( int ) Math.floor(date); - GregorianCalendar calendar = new GregorianCalendar(1900, - 0, wholeDaysSince1900 - - 1); - int millisecondsInDay = - ( int ) ((date - Math.floor(date)) - * ( double ) DAY_MILLISECONDS + 0.5); - + return getJavaDate(date,false); + } + + /** + * Given an Excel date with either 1900 or 1904 date windowing, + * converts it to a java.util.Date. + * + * @param date The Excel date. + * @param use1904windowing true if date uses 1904 windowing, + * or false if using 1900 date windowing. + * @return Java representation of the date, or null if date is not a valid Excel date + */ + public static Date getJavaDate(double date, boolean use1904windowing) { + if (isValidExcelDate(date)) { + int startYear = 1900; + int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't + int wholeDays = (int)Math.floor(date); + if (use1904windowing) { + startYear = 1904; + dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day + } + else if (wholeDays < 61) { + // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists + // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation + dayAdjust = 0; + } + GregorianCalendar calendar = new GregorianCalendar(startYear,0, + wholeDays + dayAdjust); + int millisecondsInDay = (int)((date - Math.floor(date)) * + (double) DAY_MILLISECONDS + 0.5); calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay); return calendar.getTime(); } - else - { + else { return null; } } - + /** * given a format ID this will check whether the format represents * an internal date format or not. @@ -164,6 +186,18 @@ case 0x2d: case 0x2e: case 0x2f: + // Additional internal date formats found by inspection + // Using Excel v.X 10.1.0 (Mac) + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: retval = true; break;