* @author Glen Stampoultzis (glens at apache.org)
* @author Sergei Kozello (sergeikozello at mail.ru)
* @author Luc Girardin (luc dot girardin at macrofocus dot com)
+ * @author Dan Sherman (dsherman at isisph.com)
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook
* @version 1.0-pre
*/
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);
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 :
}
{
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;
+ }
}
* 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
*/
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);
+ }
}
/**
*
* @author Michael Harhen
* @author Glen Stampoultzis (glens at apache.org)
+ * @author Dan Sherman (dsherman at isisph.com)
*/
public class HSSFDateUtil
/**
* 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.
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;