From ee1db37c7f2c2558f494ec98411491c8344b8d42 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Thu, 22 May 2014 12:34:10 +0000 Subject: [PATCH] fpspreadsheet: In the unit tests, use the same methods for creating "soll" date/times and test format strings. --> No more biff-related regressions. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3078 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/excel2demo/excel2write.lpr | 4 +- components/fpspreadsheet/fpsutils.pas | 84 +++++++++++++------ .../fpspreadsheet/tests/formattests.pas | 17 ++-- components/fpspreadsheet/xlsbiff2.pas | 30 +++---- 4 files changed, 86 insertions(+), 49 deletions(-) diff --git a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr index d23f4a5b9..430a811ed 100644 --- a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr +++ b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr @@ -45,6 +45,8 @@ begin } // Write some number cells + MyWorksheet.WriteDateTime(0, 0, EncodeDate(2012,1,12)+EncodeTime(13,14,15,0), nfShortTimeAM); + (* MyWorksheet.WriteNumber(0, 0, 1.0); MyWorksheet.WriteUsedFormatting(0, 0, [uffBold, uffNumberFormat]); MyWorksheet.WriteNumber(0, 1, 2.0); @@ -378,7 +380,7 @@ begin MyWorksheet.WriteRowInfo(5, lRow); lRow.Height := 5; MyWorksheet.WriteRowInfo(6, lRow); - +*) // Save the spreadsheet to a file MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2, true); MyWorkbook.Free; diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 0bb9568d7..e77f296ee 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -86,9 +86,14 @@ function BuildNumberFormatString(ANumberFormat: TsNumberFormat; ACurrencySymbol: String = '?'): String; function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; AFormatString: String = ''): String; + +function AddAMPM(const ATimeFormatString: String; + const AFormatSettings: TFormatSettings): String; function StripAMPM(const ATimeFormatString: String): String; function CountDecs(AFormatString: String; ADecChars: TsDecsChars = ['0']): Byte; function AddIntervalBrackets(AFormatString: String): String; +function SpecialDateTimeFormat(ACode: String; + const AFormatSettings: TFormatSettings; ForWriting: Boolean): String; function SciFloat(AValue: Double; ADecimals: Byte): String; //function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String; @@ -784,20 +789,12 @@ function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; AFormatString: String = '') : string; var fmt: String; - am, pm: String; begin case ANumberFormat of nfFmtDateTime: - begin - fmt := lowercase(AFormatString); - if (fmt = 'dm') then Result := 'd/mmm' - else if (fmt = 'my') then Result := 'mmm/yy' - else if (fmt = 'ms') then Result := 'nn:ss' - else if (fmt = 'msz') then Result := 'nn:ss.z' - else Result := AFormatString; - end; + Result := SpecialDateTimeFormat(lowercase(AFormatString), AFormatSettings, false); nfShortDateTime: - Result := AFormatSettings.ShortDateFormat + ' ' + FormatSettings.ShortTimeFormat; + Result := AFormatSettings.ShortDateFormat + ' ' + AFormatSettings.ShortTimeFormat; // In the DefaultFormatSettings this is: d/m/y hh:nn nfShortDate: Result := AFormatSettings.ShortDateFormat; // --> d/m/y @@ -810,20 +807,14 @@ begin nfShortTimeAM: begin // --> hh:nn AM/PM Result := AFormatSettings.ShortTimeFormat; - if (pos('a', lowercase(AFormatSettings.ShortTimeFormat)) = 0) then begin - am := IfThen(AFormatSettings.TimeAMString = '', 'AM', AFormatSettings.TimeAMString); - pm := IfThen(AFormatSettings.TimePMString = '', 'PM', AFormatSettings.TimePMString); - Result := Format('%s %s/%s', [Result, am, pm]); - end; + if (pos('a', lowercase(AFormatSettings.ShortTimeFormat)) = 0) then + Result := AddAMPM(Result, AFormatSettings); end; nfLongTimeAM: // --> hh:nn:ss AM/PM begin Result := AFormatSettings.LongTimeFormat; - if pos('a', lowercase(AFormatSettings.LongTimeFormat)) = 0 then begin - am := IfThen(AFormatSettings.TimeAMString = '', 'AM', AFormatSettings.TimeAMString); - pm := IfThen(AFormatSettings.TimePMString = '', 'PM', AFormatSettings.TimePMString); - Result := Format('%s %s/%s', [Result, am, pm]); - end; + if pos('a', lowercase(AFormatSettings.LongTimeFormat)) = 0 then + Result := AddAMPM(Result, AFormatSettings); end; nfTimeInterval: // --> [h]:nn:ss if AFormatString = '' then @@ -904,19 +895,22 @@ begin end; if ANumberFormat in [nfCurrency, nfCurrencyRed] then Result := Result +';' + Format(POS_FMT[cf], ['0' + decs, ACurrencySymbol]) - { - Result := Result + ';0' + decs; - if cf in [2,3] then - Result := Format('%s "%s"', [Result, ACurrencySymbol]) - else - Result := Format('%s"%s"', [Result, ACurrencySymbol]); - } else Result := Result + ';-'; end; end; end; +function AddAMPM(const ATimeFormatString: String; + const AFormatSettings: TFormatSettings): String; +var + am, pm: String; +begin + am := IfThen(AFormatSettings.TimeAMString <> '', AFormatSettings.TimeAMString, 'AM'); + pm := IfThen(AFormatSettings.TimePMString <> '', AFormatSettings.TimePMString, 'PM'); + Result := Format('%s %s/%s', [StripAMPM(ATimeFormatString), am, pm]); +end; + function StripAMPM(const ATimeFormatString: String): String; var i: Integer; @@ -968,6 +962,42 @@ begin end; end; +{ Creates the formatstrings for the date/time codes "dm", "my", "ms" and "msz" + out of the formatsettings. } +function SpecialDateTimeFormat(ACode: String; + const AFormatSettings: TFormatSettings; ForWriting: Boolean): String; +var + pd, pm, py: Integer; + sdf: String; + MonthChar, MinuteChar, MillisecChar: Char; +begin + if ForWriting then begin + MonthChar := 'M'; MinuteChar := 'm'; MillisecChar := '0'; + end else begin + MonthChar := 'm'; MinuteChar := 'n'; MillisecChar := 'z'; + end; + ACode := lowercase(ACode); + sdf := lowercase(AFormatSettings.ShortDateFormat); + pd := pos('d', sdf); + pm := pos('m', sdf); + py := pos('y', sdf); + if ACode = 'dm' then begin + Result := DupeString(MonthChar, 3); + Result := IfThen(pd < py, 'd/'+Result, Result+'/d'); // d/mmm + end else + if ACode = 'my' then begin + Result := DupeString(MonthChar, 3); + Result := IfThen(pm < py, Result+'/yy', 'yy/'+Result); // mmm/yy + end else + if ACode = 'ms' then begin + Result := DupeString(MinuteChar, 2) + ':ss'; // mm:ss + end + else if ACode = 'msz' then + Result := DupeString(MinuteChar, 2) + ':ss.' + MillisecChar // mm:ss.z + else + Result := ACode; +end; + { Formats the number AValue in "scientific" format with the given number of decimals. "Scientific" is the same as "exponential", but with exponents rounded to multiples of 3 (like for "kilo" - "Mega" - "Giga" etc.). } diff --git a/components/fpspreadsheet/tests/formattests.pas b/components/fpspreadsheet/tests/formattests.pas index 573ecf739..69d069ec5 100644 --- a/components/fpspreadsheet/tests/formattests.pas +++ b/components/fpspreadsheet/tests/formattests.pas @@ -127,9 +127,12 @@ const procedure InitSollFmtData; var i: Integer; + fs: TFormatSettings; begin // Set up norm - MUST match spreadsheet cells exactly + fs := DefaultFormatSettings; + // Numbers SollNumbers[0] := 0.0; SollNumbers[1] := 1.0; @@ -182,13 +185,13 @@ begin for i:=Low(SollDateTimes) to High(SollDateTimes) do begin SollDateTimeStrings[i, 0] := DateToStr(SollDateTimes[i]) + ' ' + FormatDateTime('t', SollDateTimes[i]); SollDateTimeStrings[i, 1] := DateToStr(SollDateTimes[i]); - SollDateTimeStrings[i, 2] := FormatDateTime('hh:nn', SollDateTimes[i]); - SolLDateTimeStrings[i, 3] := FormatDateTime('hh:nn:ss', SollDateTimes[i]); - SollDateTimeStrings[i, 4] := FormatDateTime('hh:nn am/pm', SollDateTimes[i]); // dont't use "t" - it does the hours wrong - SollDateTimeStrings[i, 5] := FormatDateTime('hh:nn:ss am/pm', SollDateTimes[i]); - SollDateTimeStrings[i, 6] := FormatDateTime('dd/mmm', SollDateTimes[i]); - SollDateTimeStrings[i, 7] := FormatDateTime('mmm/yy', SollDateTimes[i]); - SollDateTimeStrings[i, 8] := FormatDateTime('nn:ss', SollDateTimes[i]); + SollDateTimeStrings[i, 2] := FormatDateTime(fs.ShortTimeFormat, SollDateTimes[i]); + SolLDateTimeStrings[i, 3] := FormatDateTime(fs.LongTimeFormat, SollDateTimes[i]); + SollDateTimeStrings[i, 4] := FormatDateTime(fs.ShortTimeFormat + ' am/pm', SollDateTimes[i]); // dont't use "t" - it does the hours wrong + SollDateTimeStrings[i, 5] := FormatDateTime(fs.LongTimeFormat + ' am/pm', SollDateTimes[i]); + SollDateTimeStrings[i, 6] := FormatDateTime(SpecialDateTimeFormat('dm', fs, false), SollDateTimes[i]); + SollDateTimeStrings[i, 7] := FormatDateTime(SpecialDateTimeFormat('my', fs, false), SollDateTimes[i]); + SollDateTimeStrings[i, 8] := FormatDateTime(SpecialDateTimeFormat('ms', fs, false), SollDateTimes[i]); SollDateTimeStrings[i, 9] := FormatDateTime('[h]:mm:ss', SollDateTimes[i], [fdoInterval]); end; diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index c2e12d82e..9778d7d99 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -176,11 +176,13 @@ end; procedure TsBIFF2NumFormatList.AddBuiltinFormats; var + fs: TFormatSettings; ds, ts, cs: string; begin - ds := DefaultFormatSettings.DecimalSeparator; - ts := DefaultFormatSettings.ThousandSeparator; - cs := DefaultFormatSettings.CurrencyString; + fs := Workbook.FormatSettings; + ds := fs.DecimalSeparator; + ts := fs.ThousandSeparator; + cs := fs.CurrencyString; AddFormat( 0, '', nfGeneral); AddFormat( 1, '0', nfFixed, 0); AddFormat( 2, '0'+ds+'00', nfFixed, 2); // 0.00 @@ -193,15 +195,15 @@ begin AddFormat( 9, '0%', nfPercentage, 0); AddFormat(10, '0'+ds+'00%', nfPercentage, 2); AddFormat(11, '0'+ds+'00E+00', nfExp, 2); - AddFormat(12, 'M/D/YY', nfShortDate); - AddFormat(13, 'D-MMM-YY', nfLongDate); - AddFormat(14, 'D-MMM', nfFmtDateTime); - AddFormat(15, 'MMM-YY', nfFmtDateTime); - AddFormat(16, 'h:mm AM/PM', nfShortTimeAM); - AddFormat(17, 'h:mm:ss AM/PM', nfLongTimeAM); - AddFormat(18, 'h:mm', nfShortTime); - AddFormat(19, 'h:mm:ss', nfLongTime); - AddFormat(20, 'M/D/YY h:mm', nfShortDateTime); + AddFormat(12, fs.ShortDateFormat, nfShortDate); + AddFormat(13, fs.LongDateFormat, nfLongDate); + AddFormat(14, SpecialDateTimeFormat('dm', fs, true), nfFmtDateTime); + AddFormat(15, SpecialDateTimeFormat('my', fs, true), nfFmtDateTime); + AddFormat(16, AddAMPM(fs.ShortTimeFormat, fs), nfShortTimeAM); + AddFormat(17, AddAMPM(fs.LongTimeFormat, fs), nfLongTimeAM); + AddFormat(18, fs.ShortTimeFormat, nfShortTime); + AddFormat(19, fs.LongTimeFormat, nfLongTime); + AddFormat(20, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime); FFirstFormatIndexInFile := 0; // BIFF2 stores built-in formats to file. FNextFormatIndex := 21; // not needed - there are not user-defined formats @@ -231,7 +233,7 @@ begin (fmt = 'dd-mm') or (fmt = 'dd/mm') or (fmt = 'dd-mmm') or (fmt = 'dd/mmm') then - AFormatString := 'D-MMM' + AFormatString := SpecialDateTimeFormat('dm', Workbook.FormatSettings, true) else if (fmt = 'm-yy') or (fmt = 'm/yy') or (fmt = 'mm-yy') or (fmt = 'mm/yy') or @@ -240,7 +242,7 @@ begin (fmt = 'mm-yyyy') or (fmt = 'mm/yyyy') or (fmt = 'mmm-yyyy') or (fmt = 'mmm-yyyy') then - AFormatString := 'MMM-YY' + AFormatString := SpecialDateTimeFormat('my', Workbook.FormatSettings, true) else if (copy(fmt, 1, 5) = 'nn:ss') or (copy(fmt, 1, 5) = 'mm:ss') or (copy(fmt, 1, 4) = 'n:ss') or (copy(fmt, 1, 4) = 'm:ss')