From 6e2cc3fe7e1a91dbadc21929498418d15b24c761 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Thu, 9 Aug 2018 14:28:17 +0000 Subject: [PATCH] fpspreadsheet: Fix reading of ods files containing formulas with several arguments (separator semicolon). Update unit tests. (Still issues with files posted at https://forum.lazarus.freepascal.org/index.php/topic,42168.msg293792.html). git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6574 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../source/common/fpsexprparser.pas | 100 ++++++++++-------- .../fpspreadsheet/source/common/fpsfunc.pas | 56 ++++++---- .../source/common/fpsopendocument.pas | 13 ++- .../source/common/fpspreadsheet.pas | 2 +- .../fpspreadsheet/source/common/fpstypes.pas | 56 +++++----- .../fpspreadsheet/source/common/fpsutils.pas | 20 ++++ .../tests/singleformulatests.pas | 29 ++++- .../tests/testcases_calcrpnformula.inc | 17 ++- 8 files changed, 182 insertions(+), 111 deletions(-) diff --git a/components/fpspreadsheet/source/common/fpsexprparser.pas b/components/fpspreadsheet/source/common/fpsexprparser.pas index ef6062ba8..22ef3a7b9 100644 --- a/components/fpspreadsheet/source/common/fpsexprparser.pas +++ b/components/fpspreadsheet/source/common/fpsexprparser.pas @@ -687,6 +687,7 @@ type FDialect: TsFormulaDialect; FSourceCell: PCell; FDestCell: PCell; + FListSep: Char; procedure CheckEOF; function GetAsBoolean: Boolean; function GetAsDateTime: TDateTime; @@ -703,13 +704,14 @@ type FFormatSettings: TFormatSettings; FContains3DRef: Boolean; class function BuiltinExpressionManager: TsBuiltInExpressionManager; - function BuildStringFormula(AFormatSettings: TFormatSettings): String; + function BuildStringFormula: String; procedure ParserError(Msg: String); function GetExpression: String; function GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; virtual; procedure SetExpression(const AValue: String); procedure SetLocalizedExpression(const AFormatSettings: TFormatSettings; const AValue: String); virtual; + procedure UpdateExprFormatSettings; procedure CheckResultType(const Res: TsExpressionResult; AType: TsResultType); inline; @@ -750,6 +752,7 @@ type property AsDateTime: TDateTime read GetAsDateTime; // The expression to parse property Expression: String read GetExpression write SetExpression; + property ListSeparator: Char read FListSep; property LocalizedExpression[AFormatSettings: TFormatSettings]: String read GetLocalizedExpression write SetLocalizedExpression; property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula; @@ -847,6 +850,7 @@ procedure RegisterFunction(const AName: ShortString; const AResultType: Char; const AParamTypes: String; const AExcelCode: Integer; ACallBack: TsExprFunctionEvent); overload; var + // Format settings used in stored parsed formulas. ExprFormatSettings: TFormatSettings; const @@ -1060,7 +1064,7 @@ begin Result := ttLessThanEqual; end else - if D = FParser.FFormatSettings.ListSeparator then + if D = FParser.ListSeparator then Result := ttListSep else case D of @@ -1316,7 +1320,7 @@ end; function TsExpressionScanner.IsDelim(C: Char): Boolean; begin - Result := (C in Delimiters) or (C = FParser.FFormatSettings.ListSeparator); + Result := (C in Delimiters) or (C = FParser.ListSeparator); end; function TsExpressionScanner.IsDigit(C: Char): Boolean; @@ -1326,7 +1330,7 @@ end; function TsExpressionScanner.IsWordDelim(C: Char): Boolean; begin - Result := (C in WordDelimiters) or (C = FParser.FFormatSettings.ListSeparator); + Result := (C in WordDelimiters) or (C = FParser.ListSeparator); end; function TsExpressionScanner.NextPos: Char; @@ -1368,12 +1372,14 @@ end; constructor TsExpressionParser.Create(AWorksheet: TsBasicWorksheet); begin inherited Create; - FDialect := fdExcelA1; FWorksheet := AWorksheet; FIdentifiers := TsExprIdentifierDefs.Create(TsExprIdentifierDef); FIdentifiers.FParser := Self; FScanner := TsExpressionScanner.Create(self); FHashList := TFPHashObjectList.Create(False); + SetDialect(fdExcelA1); + FFormatSettings := InitFormatSettings(TsWorksheet(FWorksheet).Workbook); + UpdateExprFormatSettings; end; destructor TsExpressionParser.Destroy; @@ -1386,17 +1392,14 @@ begin end; { Constructs the string formula from the tree of expression nodes. Gets the - decimal and list separator from the formatsettings provided. } -function TsExpressionParser.BuildStringFormula(AFormatSettings: TFormatSettings): String; + decimal and list separator from current formatsettings. } +function TsExpressionParser.BuildStringFormula: String; begin - ExprFormatSettings := AFormatSettings; +// ExprFormatSettings := AFormatSettings; if FExprNode = nil then Result := '' else - begin - FFormatSettings := AFormatSettings; Result := FExprNode.AsString; - end; end; class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager; @@ -1485,6 +1488,8 @@ begin end; procedure TsExpressionParser.EvaluateExpression(out AResult: TsExpressionResult); +var + fs: TFormatSettings; begin { // Not needed. May be missing after copying formulas if (FExpression = '') then @@ -1492,7 +1497,15 @@ begin } if not Assigned(FExprNode) then ParserError(rsErrorInExpression); - FExprNode.GetNodeValue(AResult); + fs := ExprFormatSettings; + try + UpdateExprFormatSettings; +// ExprFormatSettings := FFormatSettings; +// FFormatSettings := ExprFormatSettings; + FExprNode.GetNodeValue(AResult); + finally + ExprFormatSettings := fs; + end; end; function TsExpressionParser.GetAsBoolean: Boolean; @@ -1915,19 +1928,16 @@ begin end; function TsExpressionParser.GetExpression: String; -var - fs: TFormatsettings; begin - fs := DefaultFormatSettings; - fs.DecimalSeparator := '.'; - fs.ListSeparator := ','; - Result := BuildStringFormula(fs); + Result := BuildStringFormula; //(FFormatSettings); end; function TsExpressionParser.GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; begin - ExprFormatSettings := AFormatSettings; - Result := BuildStringFormula(AFormatSettings); +// ExprFormatSettings := AFormatSettings; + FFormatSettings := AFormatSettings; + UpdateExprFormatSettings; + Result := BuildStringFormula; //(AFormatSettings); end; function TsExpressionParser.Has3DLinks: Boolean; @@ -1944,26 +1954,17 @@ end; procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect); begin - if FDialect = AValue then exit; FDialect := AValue; - { - if FScanner <> nil then - case FDialect of - fdExcelA1, fdExcelR1C1: FScanner.SheetNameTerminator := '!'; - fdOpenDocument: FScanner.Sheetnameterminator := '.'; - else raise Exception.Create('TsExpressionParser.SetDialect: Dialect not supported.'); - end; - } + case FDialect of + fdExcelA1, + fdExcelR1C1 : FListSep := ','; + fdOpenDocument : FListSep := ';' + end; end; procedure TsExpressionParser.SetExpression(const AValue: String); -var - fs: TFormatSettings; begin - fs := DefaultFormatSettings; - fs.DecimalSeparator := '.'; - fs.ListSeparator := ','; - SetLocalizedExpression(fs, AValue); + SetLocalizedExpression(FFormatSettings, AValue); end; procedure TsExpressionParser.SetLocalizedExpression(const AFormatSettings: TFormatSettings; @@ -1972,7 +1973,8 @@ begin if FExpression = AValue then exit; FFormatSettings := AFormatSettings; - ExprFormatSettings := AFormatSettings; + UpdateExprFormatSettings; +// ExprFormatSettings := AFormatSettings; FExpression := AValue; if (AValue <> '') and (AValue[1] = '=') then FScanner.Source := Copy(AValue, 2, Length(AValue)) @@ -2182,6 +2184,16 @@ begin Result := FScanner.TokenType; end; +procedure TsExpressionParser.UpdateExprFormatSettings; +var + book: TsWorkbook; +begin + book := TsWorksheet(FWorksheet).Workbook; + ExprFormatSettings.ShortDateFormat := book.FormatSettings.ShortDateFormat; + ExprFormatSettings.ShortTimeFormat := book.FormatSettings.ShortTimeFormat; + ExprFormatSettings.LongTimeFormat := book.FormatSettings.LongTimeFormat; +end; + {------------------------------------------------------------------------------} { TsSpreadsheetParser } @@ -3738,7 +3750,7 @@ begin for i := 0 to Length(FArgumentNodes)-1 do begin if (S <> '') then - S := S + Parser.FFormatSettings.ListSeparator; + S := S + Parser.ListSeparator; if Assigned(FArgumentNodes[i]) then S := S + FArgumentNodes[i].AsString; end; @@ -4431,8 +4443,9 @@ begin rtBoolean : if Arg.ResBoolean then Result := 1.0; rtHyperlink, rtString : begin - fs := ExprFormatSettings; //(Arg.Worksheet as TsWorksheet).Workbook.FormatSettings; - TryStrToDateTime(ArgToString(Arg), Result, fs); + fs := ExprFormatSettings; + if not TryStrToDateTime(ArgToString(Arg), Result, fs) then + Result := NaN; end; rtCell : begin cell := ArgToCell(Arg); @@ -4696,14 +4709,13 @@ begin end; initialization - ExprFormatSettings := DefaultFormatSettings; - ExprFormatSettings.DecimalSeparator := '.'; - ExprFormatSettings.ListSeparator := ','; - ExprFormatSettings.DateSeparator := '/'; - ExprFormatSettings.TimeSeparator := ':'; + // These are the format settings used in storage of parsed formulas. + ExprFormatSettings := InitFormatSettings(nil); + { ExprFormatSettings.ShortDateFormat := 'yyyy/m/d'; // the parser returns single digits ExprFormatSettings.LongTimeFormat := 'h:n:s'; ExprFormatSettings.ShortTimeFormat := 'h:n'; + } RegisterStdBuiltins(BuiltinIdentifiers); diff --git a/components/fpspreadsheet/source/common/fpsfunc.pas b/components/fpspreadsheet/source/common/fpsfunc.pas index b2d43695f..286d129c1 100644 --- a/components/fpspreadsheet/source/common/fpsfunc.pas +++ b/components/fpspreadsheet/source/common/fpsfunc.pas @@ -391,6 +391,10 @@ var begin start_date := ArgToDateTime(Args[0]); end_date := ArgToDateTime(Args[1]); + if IsNaN(start_date) or IsNaN(end_date) then begin + Result := ErrorResult(errWrongType); + exit; + end; interval := ArgToString(Args[2]); if end_date > start_date then @@ -428,8 +432,10 @@ begin if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then begin dt := ArgToDateTime(Args[0]); - DecodeDate(dt, y, m, d); - Result := IntegerResult(d); + if not IsNaN(dt) then begin + DecodeDate(dt, y, m, d); + Result := IntegerResult(d); + end; end; end; @@ -444,8 +450,10 @@ begin if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then begin dt := ArgToDateTime(Args[0]); - DecodeTime(dt, h, m, s, ms); - Result := IntegerResult(h); + if not IsNaN(dt) then begin + DecodeTime(dt, h, m, s, ms); + Result := IntegerResult(h); + end; end; end; @@ -459,8 +467,10 @@ begin if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then begin dt := ArgToDateTime(Args[0]); - DecodeTime(dt, h, m, s, ms); - Result := IntegerResult(m); + if not IsNaN(dt) then begin + DecodeTime(dt, h, m, s, ms); + Result := IntegerResult(m); + end; end; end; @@ -474,8 +484,10 @@ begin if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then begin dt := ArgToDateTime(Args[0]); - DecodeDate(dt, y, m, d); - Result := IntegerResult(m); + if not IsNaN(dt) then begin + DecodeDate(dt, y, m, d); + Result := IntegerResult(m); + end; end; end; @@ -498,8 +510,10 @@ begin if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then begin dt := ArgToDateTime(Args[0]); - DecodeTime(dt, h, m, s, ms); - Result := IntegerResult(s); + if not IsNaN(dt) then begin + DecodeTime(dt, h, m, s, ms); + Result := IntegerResult(s); + end; end; end; @@ -542,19 +556,17 @@ var dow: Integer; dt: TDateTime; begin + Result := ErrorResult(errWrongType); if Length(Args) = 2 then n := ArgToInt(Args[1]) else n := 1; - if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtCell] then - dt := ArgToDateTime(Args[0]) - else - if Args[0].ResultType in [rtString] then - if not TryStrToDate(Args[0].ResString, dt) then - begin - Result := ErrorResult(errWrongType); - exit; - end; + dt := NaN; + if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtCell, rtString] then + dt := ArgToDateTime(Args[0]); + if IsNaN(dt) then + exit; + dow := DayOfWeek(dt); // Sunday = 1 ... Saturday = 7 case n of 1: ; @@ -574,8 +586,10 @@ begin if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then begin dt := ArgToDateTime(Args[0]); - DecodeDate(dt, y, m, d); - Result := IntegerResult(y); + if not IsNaN(dt) then begin + DecodeDate(dt, y, m, d); + Result := IntegerResult(y); + end; end; end; diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas index 97e77236b..eb9311bb4 100644 --- a/components/fpspreadsheet/source/common/fpsopendocument.pas +++ b/components/fpspreadsheet/source/common/fpsopendocument.pas @@ -2400,6 +2400,7 @@ var p: Integer; fmt: PsCellFormat; ns: String; + hasFormula: Boolean; begin {$IFDEF FPSpreadDebug} DebugLn(Format('[ReadFormula] ARow=%d, ACol=%d, AStyleIndex=%d', [ARow, ACol, AStyleIndex])); @@ -2445,7 +2446,10 @@ begin formula^.Parser.Expression := formulaStr; formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect formula^.Text := formula^.Parser.Expression; - { + cell^.Flags := cell^.Flags + [cfHasFormula]; + hasFormula := true; + + { cell^.FormulaValue := formula; // Note: This formula is still in OpenDocument dialect. Conversion to // ExcelA1 dialect (used by fps) is postponed until all sheets have beeon @@ -2455,9 +2459,12 @@ begin {$IFDEF FPSpreadDebug} DebugLn(' Formula found: ' + formula); {$ENDIF} - end; + end else + hasFormula := false; // Read formula results + if hasFormula then TsWorkbook(FWorkbook).LockFormulas; + valueType := GetAttrValue(ACellNode, 'office:value-type'); valueStr := GetAttrValue(ACellNode, 'office:value'); calcExtValueType := GetAttrValue(ACellNode, 'calcext:value-type'); @@ -2520,6 +2527,8 @@ begin if (valueStr <> '') then TsWorksheet(FWorksheet).WriteText(cell, valueStr); + if hasFormula then TsWorkbook(FWorkbook).UnlockFormulas; + if FIsVirtualMode then TsWorkbook(Workbook).OnReadCellData(Workbook, ARow, ACol, cell); end; diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas index 98a59c692..86e3ffc74 100644 --- a/components/fpspreadsheet/source/common/fpspreadsheet.pas +++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas @@ -4793,7 +4793,7 @@ end; procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double); begin if ACell <> nil then begin - // Delete any pre-existing formula + // Delete any pre-existing formula, but only if FormulaLock is ON. DeleteFormula(ACell); // Write number to cell ACell^.ContentType := cctNumber; diff --git a/components/fpspreadsheet/source/common/fpstypes.pas b/components/fpspreadsheet/source/common/fpstypes.pas index ad1287f65..fc32d427c 100644 --- a/components/fpspreadsheet/source/common/fpstypes.pas +++ b/components/fpspreadsheet/source/common/fpstypes.pas @@ -1018,13 +1018,35 @@ const HEADER_FOOTER_INDEX_EVEN = 2; HEADER_FOOTER_INDEX_ALL = 1; -var - {@@ FPC format settings for which all strings have been converted to UTF8 } - UTF8FormatSettings: TFormatSettings; +procedure InitUTF8FormatSettings(out AFormatSettings: TFormatSettings); implementation +{@@ ---------------------------------------------------------------------------- + Creates a localized FPC format settings record in which all strings are + encoded as UTF8. +-------------------------------------------------------------------------------} +procedure InitUTF8FormatSettings(out AFormatSettings: TFormatSettings); +// remove when available in LazUtils +var + i: Integer; +begin + AFormatSettings := DefaultFormatSettings; + AFormatSettings.CurrencyString := AnsiToUTF8(DefaultFormatSettings.CurrencyString); + for i:=1 to 12 do begin + AFormatSettings.LongMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.LongMonthNames[i]); + AFormatSettings.ShortMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortMonthNames[i]); + end; + for i:=1 to 7 do begin + AFormatSettings.LongDayNames[i] := AnsiToUTF8(DefaultFormatSettings.LongDayNames[i]); + AFormatSettings.ShortDayNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortDayNames[i]); + end; +end; + + +{ TsFont } + constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles; AColor: TsColor; APosition: TsFontPosition); begin @@ -1078,7 +1100,7 @@ end; constructor TsBasicWorkbook.Create; begin inherited; - FormatSettings := UTF8FormatSettings; + InitUTF8FormatSettings(FormatSettings); FUnits := suMillimeters; // Units for column width and row height FFormatID := sfidUnknown; FLog := TStringList.Create; @@ -1138,31 +1160,5 @@ begin end; - -{@@ ---------------------------------------------------------------------------- - Creates a FPC format settings record in which all strings are encoded as - UTF8. --------------------------------------------------------------------------------} -procedure InitUTF8FormatSettings; -// remove when available in LazUtils -var - i: Integer; -begin - UTF8FormatSettings := DefaultFormatSettings; - UTF8FormatSettings.CurrencyString := AnsiToUTF8(DefaultFormatSettings.CurrencyString); - for i:=1 to 12 do begin - UTF8FormatSettings.LongMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.LongMonthNames[i]); - UTF8FormatSettings.ShortMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortMonthNames[i]); - end; - for i:=1 to 7 do begin - UTF8FormatSettings.LongDayNames[i] := AnsiToUTF8(DefaultFormatSettings.LongDayNames[i]); - UTF8FormatSettings.ShortDayNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortDayNames[i]); - end; -end; - - -initialization - InitUTF8FormatSettings; - end. diff --git a/components/fpspreadsheet/source/common/fpsutils.pas b/components/fpspreadsheet/source/common/fpsutils.pas index 26246e93e..4685431e5 100644 --- a/components/fpspreadsheet/source/common/fpsutils.pas +++ b/components/fpspreadsheet/source/common/fpsutils.pas @@ -202,6 +202,7 @@ procedure InitCell(AWorksheet: TsBasicWorksheet; ARow, ACol: Cardinal; out ACell: TCell); overload; procedure InitCryptoInfo(out AValue: TsCryptoInfo); procedure InitFormatRecord(out AValue: TsCellFormat); +function InitFormatSettings(AWorkbook: TsBasicWorkbook): TFormatSettings; procedure InitImageRecord(out AValue: TsImage; ARow, ACol: Cardinal; AOffsetX, AOffsetY, AScaleX, AScaleY: Double); procedure InitHeaderFooterImageRecord(out AImage: TsHeaderFooterImage); @@ -2439,6 +2440,25 @@ begin // NOTE: Cell protection is effective only after protecting a worksheet end; +{@@ ---------------------------------------------------------------------------- + Initializes the FormatSettingsRecord to international (non-localized) values +-------------------------------------------------------------------------------} +function InitFormatSettings(AWorkbook: TsBasicWorkbook): TFormatSettings; +begin + Result := DefaultFormatSettings; + Result.DecimalSeparator := '.'; + Result.ThousandSeparator := ','; + Result.ListSeparator := ','; + if AWorkbook <> nil then + with AWorkbook.FormatSettings do begin + Result.DateSeparator := DateSeparator; + Result.TimeSeparator := TimeSeparator; + Result.ShortDateFormat := ShortDateFormat; //'yyyy/m/d'; // the parser returns single digits + Result.LongTimeFormat := LongTimeFormat; //'h:n:s'; + Result.ShortTimeFormat := ShortTimeFormat; //'h:n'; + end; +end; + {@@ ---------------------------------------------------------------------------- Initializes the fields of a TsImage record diff --git a/components/fpspreadsheet/tests/singleformulatests.pas b/components/fpspreadsheet/tests/singleformulatests.pas index e79d0f28e..b4969e455 100644 --- a/components/fpspreadsheet/tests/singleformulatests.pas +++ b/components/fpspreadsheet/tests/singleformulatests.pas @@ -83,6 +83,10 @@ type procedure SumMultiSheetRange_FlippedSheetsAndCells_OOXML; procedure SumMultiSheetRange_FlippedSheetsAndCells_ODS; + procedure IfConst_BIFF8; + procedure IfConst_OOXML; + procedure IfConst_ODS; + procedure CountIfRange_BIFF8; procedure CountIfRangeSheet_BIFF8; @@ -335,7 +339,7 @@ procedure TSpreadSingleFormulaTests.YearConst_BIFF8; var s: String; begin - s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings); + s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings); TestFormula(Format('YEAR("%s")', [s]), '2012', ftkConstants, sfExcel8); end; @@ -348,7 +352,7 @@ procedure TSpreadSingleFormulaTests.MonthConst_BIFF8; var s: String; begin - s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings); + s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), DefaultFormatSettings); TestFormula(Format('MONTH("%s")', [s]), '2', ftkConstants, sfExcel8); end; @@ -361,7 +365,7 @@ procedure TSpreadSingleFormulaTests.DayConst_BIFF8; var s: String; begin - s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings); + s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), DefaultFormatSettings); TestFormula(Format('DAY("%s")', [s]), '5', ftkConstants, sfExcel8); end; @@ -376,7 +380,7 @@ procedure TSpreadSingleFormulaTests.HourConst_BIFF8; var s: String; begin - s := FormatDateTime(ExprFormatSettings.LongTimeFormat, EncodeTime(14, 20, 41, 0), ExprFormatSettings); + s := FormatDateTime(ExprFormatSettings.LongTimeFormat, EncodeTime(14, 20, 41, 0), DefaultFormatSettings); TestFormula(Format('HOUR("%s")', [s]), '14', ftkConstants, sfExcel8); end; @@ -515,6 +519,23 @@ end; { --- } +procedure TSpreadSingleFormulaTests.IfConst_BIFF8; +begin + TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfExcel8); +end; + +procedure TSpreadSingleFormulaTests.IfConst_OOXML; +begin + TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfOOXML); +end; + +procedure TSpreadSingleFormulaTests.IfConst_ODS; +begin + TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfOpenDocument); +end; + +{ --- } + procedure TSpreadSingleFormulaTests.CountIfRange_BIFF8; begin TestFormula('COUNTIF(C3:C5,">1")', '1', ftkCellRange, sfExcel8); diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index 9a4fcd5a7..ed7a81cc0 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -17,7 +17,6 @@ {------------------------------------------------------------------------------} // Addition Row := 0; - formula := '1+1'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -1874,7 +1873,7 @@ // DAY / argument string inc(Row); - s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test + s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test formula := 'DAY("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -1906,7 +1905,7 @@ // HOUR / argument string inc(Row); - s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test + s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test formula := 'HOUR("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -1938,7 +1937,7 @@ // MINUTE / argument string inc(Row); - s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test + s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test formula := 'MINUTE("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -1970,7 +1969,7 @@ // MONTH / argument string inc(Row); - s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test + s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test formula := 'MONTH("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -2022,7 +2021,7 @@ // SECOND / argument string inc(Row); - s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test + s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test formula := 'SECOND("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -2054,7 +2053,7 @@ // TIMEVALUE inc(Row); - s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization! + s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization! formula := 'TIMEVALUE("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -2104,7 +2103,7 @@ // WEEKDAY / argument string inc(Row); - s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test + s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test formula := 'WEEKDAY("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then @@ -2136,7 +2135,7 @@ // YEAR / argument string inc(Row); - s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test + s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test formula := 'YEAR("'+s+'")'; MyWorksheet.WriteText(Row, 0, formula); if UseRPNFormula then