diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index e5cee733c..7f45580cf 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -602,7 +602,8 @@ begin //cctFormula cctNumber: Result := FloatToStr(ACell^.NumberValue); - cctUTF8String: Result := ACell^.UTF8StringValue; + cctUTF8String: Result := ACell^.UTF8StringValue; + cctDateTime: Result := SysUtils.DateToStr(ACell^.DateTimeValue); else Result := ''; end; diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index ebf9ede51..e86ed98e9 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -52,7 +52,7 @@ unit xlsbiff8; interface uses - Classes, SysUtils, fpcanvas, + Classes, SysUtils, fpcanvas, DateUtils, fpspreadsheet, xlscommon, {$ifdef USE_NEW_OLE} fpolebasic, @@ -104,7 +104,7 @@ type procedure ReadXF(const AStream: TStream); procedure ReadFormat(const AStream: TStream); function FindFormatRecordForCell(const AFXIndex: Integer): TFormatRecordData; - class function ConvertExcelDateToTDateTime(const AExcelDateNum: Integer): TDateTime; + class function ConvertExcelDateToTDateTime(const AExcelDateNum: Double; ABaseDate: TDateTime): TDateTime; // Workbook Globals records // procedure ReadCodepage in xlscommon @@ -1472,6 +1472,7 @@ begin INT_EXCEL_ID_FONT: ReadFont(AStream); INT_EXCEL_ID_XF: ReadXF(AStream); INT_EXCEL_ID_FORMAT: ReadFormat(AStream); + INT_EXCEL_ID_DATEMODE: ReadDateMode(AStream); else // nothing end; @@ -1557,6 +1558,7 @@ var ARow, ACol, XF: WORD; Number: Double; lFormatData: TFormatRecordData; + lDateTime: TDateTime; begin ReadRowColXF(AStream,ARow,ACol,XF); @@ -1570,14 +1572,17 @@ begin // See: http://www.gaia-gis.it/FreeXL/freexl-1.0.0a-doxy-doc/Format.html // Unfornately Excel doesnt give us a direct way to find this, // we need to guess by the FORMAT field -{ lFormatData := FindFormatRecordForCell(XF); + lFormatData := FindFormatRecordForCell(XF); if lFormatData <> nil then begin // Dates have / if Pos('/', lFormatData.FormatString) > 0 then begin + lDateTime := ConvertExcelDateToTDateTime(Number, FBaseDate); + FWorksheet.WriteDateTime(ARow,ACol,lDateTime); + Exit; end; - end;} + end; FWorksheet.WriteNumber(ARow,ACol,Number); end; @@ -1963,9 +1968,9 @@ begin end; class function TsSpreadBIFF8Reader.ConvertExcelDateToTDateTime( - const AExcelDateNum: Integer): TDateTime; + const AExcelDateNum: Double; ABaseDate: TDateTime): TDateTime; begin - + Result := IncDay(ABaseDate, Round(AExcelDateNum)); end; procedure TsSpreadBIFF8Reader.ReadFont(const AStream: TStream); diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 561667f03..4114c9468 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -7,13 +7,14 @@ unit xlscommon; interface uses - Classes, SysUtils, + Classes, SysUtils, DateUtils, fpspreadsheet, fpsutils, lconvencoding; const { RECORD IDs which didn't change across versions 2-8 } INT_EXCEL_ID_CODEPAGE = $0042; + INT_EXCEL_ID_DATEMODE = $0022; { Formula constants TokenID values } @@ -104,9 +105,12 @@ type TsSpreadBIFFReader = class(TsCustomSpreadReader) protected FCodepage: string; + FBaseDate: TDateTime; + constructor Create; override; // Here we can add reading of records which didn't change across BIFF2-8 versions // Workbook Globals records procedure ReadCodePage(AStream: TStream); + procedure ReadDateMode(AStream: TStream); end; { TsSpreadBIFFWriter } @@ -130,6 +134,14 @@ implementation { TsSpreadBIFFReader } +constructor TsSpreadBIFFReader.Create; +begin + inherited Create; + // Initial base date in case it wont be informed + FBaseDate := DateUtils.EncodeDateDay(1900, 1); + FBaseDate := DateUtils.IncDay(FBaseDate, -1); +end; + // In BIFF 8 it seams to always use the UTF-16 codepage procedure TsSpreadBIFFReader.ReadCodePage(AStream: TStream); var @@ -181,6 +193,30 @@ begin end; end; +procedure TsSpreadBIFFReader.ReadDateMode(AStream: TStream); +var + lBaseMode: Word; +begin + //5.28 DATEMODE + //BIFF2 BIFF3 BIFF4 BIFF5 BIFF8 + //0022H 0022H 0022H 0022H 0022H + //This record specifies the base date for displaying date values. All dates are stored as count of days past this base date. In + //BIFF2-BIFF4 this record is part of the Calculation Settings Block (➜4.3). In BIFF5-BIFF8 it is stored in the Workbook + //Globals Substream. + //Record DATEMODE, BIFF2-BIFF8: + //Offset Size Contents + //0 2 0 = Base date is 1899-Dec-31 (the cell value 1 represents 1900-Jan-01) + // 1 = Base date is 1904-Jan-01 (the cell value 1 represents 1904-Jan-02) + lBaseMode := WordLEtoN(AStream.ReadWord); + if lBaseMode = 0 then + begin + FBaseDate := DateUtils.EncodeDateDay(1900, 1); + FBaseDate := DateUtils.IncDay(FBaseDate, -1); + end + else + FBaseDate := DateUtils.EncodeDateDay(1904, 1); +end; + function TsSpreadBIFFWriter.FPSColorToEXCELPallete(AColor: TsColor): Word; begin case AColor of