fpspreadsheet: Improved detection of built-in date/time formats in csv. Test case for date/time in CSV, passed.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3700 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2014-10-30 13:04:37 +00:00
parent a1fe949908
commit 6d388ffc1a
3 changed files with 70 additions and 23 deletions

View File

@ -116,6 +116,7 @@
<Unit3> <Unit3>
<Filename Value="sctrls.pas"/> <Filename Value="sctrls.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="sCtrls"/>
</Unit3> </Unit3>
<Unit4> <Unit4>
<Filename Value="sformatsettingsform.pas"/> <Filename Value="sformatsettingsform.pas"/>
@ -131,16 +132,20 @@
<ComponentName Value="SortParamsForm"/> <ComponentName Value="SortParamsForm"/>
<HasResources Value="True"/> <HasResources Value="True"/>
<ResourceBaseClass Value="Form"/> <ResourceBaseClass Value="Form"/>
<UnitName Value="sSortParamsForm"/>
</Unit5> </Unit5>
<Unit6> <Unit6>
<Filename Value="sfcurrencyform.pas"/> <Filename Value="sfcurrencyform.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<ComponentName Value="CurrencyForm"/> <ComponentName Value="CurrencyForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/> <ResourceBaseClass Value="Form"/>
<UnitName Value="sfCurrencyForm"/>
</Unit6> </Unit6>
<Unit7> <Unit7>
<Filename Value="..\..\fpscurrency.pas"/> <Filename Value="..\..\fpscurrency.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="fpsCurrency"/>
</Unit7> </Unit7>
</Units> </Units>
</ProjectOptions> </ProjectOptions>

View File

@ -12,8 +12,10 @@ type
TsCSVReader = class(TsCustomSpreadReader) TsCSVReader = class(TsCustomSpreadReader)
private private
FWorksheetName: String; FWorksheetName: String;
FFormatSettings: TFormatSettings;
function IsBool(AText: String; out AValue: Boolean): Boolean; function IsBool(AText: String; out AValue: Boolean): Boolean;
function IsDateTime(AText: String; out ADateTime: TDateTime): Boolean; function IsDateTime(AText: String; out ADateTime: TDateTime;
out ANumFormat: TsNumberFormat): Boolean;
function IsNumber(AText: String; out ANumber: Double; out ANumFormat: TsNumberFormat; function IsNumber(AText: String; out ANumber: Double; out ANumFormat: TsNumberFormat;
out ADecimals: Integer; out ACurrencySymbol, AWarning: String): Boolean; out ADecimals: Integer; out ACurrencySymbol, AWarning: String): Boolean;
function IsQuotedText(var AText: String): Boolean; function IsQuotedText(var AText: String): Boolean;
@ -34,6 +36,7 @@ type
private private
FCSVBuilder: TCSVBuilder; FCSVBuilder: TCSVBuilder;
FEncoding: String; FEncoding: String;
FFormatSettings: TFormatSettings;
protected protected
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override; ACell: PCell); override;
@ -197,7 +200,8 @@ end;
constructor TsCSVReader.Create(AWorkbook: TsWorkbook); constructor TsCSVReader.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
ReplaceFormatSettings(CSVParams.FormatSettings, AWorkbook.FormatSettings); FFormatSettings := CSVParams.FormatSettings;
ReplaceFormatSettings(FFormatSettings, AWorkbook.FormatSettings);
FWorksheetName := 'Sheet1'; // will be replaced by filename FWorksheetName := 'Sheet1'; // will be replaced by filename
end; end;
@ -216,9 +220,45 @@ begin
Result := false; Result := false;
end; end;
function TsCSVReader.IsDateTime(AText: String; out ADateTime: TDateTime): Boolean; function TsCSVReader.IsDateTime(AText: String; out ADateTime: TDateTime;
out ANumFormat: TsNumberFormat): Boolean;
{ Test whether the text is formatted according to a built-in date/time format.
Converts the obtained date/time value back to a string and compares. }
function TestFormat(lNumFmt: TsNumberFormat): Boolean;
var
fmt: string;
begin
fmt := BuildDateTimeFormatString(lNumFmt, FFormatSettings);
Result := FormatDateTime(fmt, ADateTime, FFormatSettings) = AText;
if Result then ANumFormat := lNumFmt;
end;
begin begin
Result := TryStrToDateTime(AText, ADateTime, CSVParams.FormatSettings); Result := TryStrToDateTime(AText, ADateTime, FFormatSettings);
if Result then
begin
ANumFormat := nfCustom;
if abs(ADateTime) > 1 then // this is most probably a date
begin
if TestFormat(nfShortDateTime) then
exit;
if TestFormat(nfLongDate) then
exit;
if TestFormat(nfShortDate) then
exit;
end else
begin // this case is time-only
if TestFormat(nfLongTimeAM) then
exit;
if TestFormat(nfLongTime) then
exit;
if TestFormat(nfShortTimeAM) then
exit;
if TestFormat(nfShortTime) then
exit;
end;
end;
end; end;
function TsCSVReader.IsNumber(AText: String; out ANumber: Double; function TsCSVReader.IsNumber(AText: String; out ANumber: Double;
@ -234,9 +274,7 @@ begin
// To detect whether the text is a currency value we look for the currency // To detect whether the text is a currency value we look for the currency
// string. If we find it, we delete it and convert the remaining string to // string. If we find it, we delete it and convert the remaining string to
// a number. // a number.
ACurrencySymbol := StrUtils.IfThen(CSVParams.FormatSettings.CurrencyString = '', ACurrencySymbol := FFormatSettings.CurrencyString;
FWorkbook.FormatSettings.CurrencyString,
CSVParams.FormatSettings.CurrencyString);
if RemoveCurrencySymbol(ACurrencySymbol, AText) then if RemoveCurrencySymbol(ACurrencySymbol, AText) then
begin begin
if IsNegative(AText) then if IsNegative(AText) then
@ -251,15 +289,15 @@ begin
if CSVParams.AutoDetectNumberFormat then if CSVParams.AutoDetectNumberFormat then
Result := TryStrToFloatAuto(AText, ANumber, DecSep, ThousSep, AWarning) Result := TryStrToFloatAuto(AText, ANumber, DecSep, ThousSep, AWarning)
else begin else begin
Result := TryStrToFloat(AText, ANumber, CSVParams.FormatSettings); Result := TryStrToFloat(AText, ANumber, FFormatSettings);
if Result then if Result then
begin begin
if pos(CSVParams.FormatSettings.DecimalSeparator, AText) = 0 if pos(FFormatSettings.DecimalSeparator, AText) = 0
then DecSep := #0 then DecSep := #0
else DecSep := CSVParams.FormatSettings.DecimalSeparator; else DecSep := FFormatSettings.DecimalSeparator;
if pos(CSVParams.FormatSettings.ThousandSeparator, AText) = 0 if pos(CSVParams.FormatSettings.ThousandSeparator, AText) = 0
then ThousSep := #0 then ThousSep := #0
else ThousSep := CSVParams.FormatSettings.ThousandSeparator; else ThousSep := FFormatSettings.ThousandSeparator;
end; end;
end; end;
@ -369,15 +407,8 @@ begin
// Check for a DATE/TIME cell // Check for a DATE/TIME cell
// No idea how to apply the date/time formatsettings here... // No idea how to apply the date/time formatsettings here...
if IsDateTime(AText, dtValue) then if IsDateTime(AText, dtValue, nf) then
begin begin
if dtValue < 1.0 then // this is a time alone
nf := nfLongTime
else
if frac(dtValue) = 0.0 then // this is a date alone
nf := nfShortDate
else // this is date + time
nf := nfShortDateTime;
FWorksheet.WriteDateTime(ARow, ACol, dtValue, nf); FWorksheet.WriteDateTime(ARow, ACol, dtValue, nf);
exit; exit;
end; end;
@ -473,7 +504,8 @@ end;
constructor TsCSVWriter.Create(AWorkbook: TsWorkbook); constructor TsCSVWriter.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
ReplaceFormatSettings(CSVParams.FormatSettings, FWorkbook.FormatSettings); FFormatSettings := CSVParams.FormatSettings;
ReplaceFormatSettings(FFormatSettings, FWorkbook.FormatSettings);
if CSVParams.Encoding = '' then if CSVParams.Encoding = '' then
FEncoding := 'utf8' FEncoding := 'utf8'
else else
@ -561,9 +593,9 @@ begin
if ACell = nil then if ACell = nil then
exit; exit;
if CSVParams.NumberFormat <> '' then if CSVParams.NumberFormat <> '' then
s := Format(CSVParams.NumberFormat, [AValue], CSVParams.FormatSettings) s := Format(CSVParams.NumberFormat, [AValue], FFormatSettings)
else else
s := FWorksheet.ReadAsUTF8Text(ACell, CSVParams.FormatSettings); s := FWorksheet.ReadAsUTF8Text(ACell, FFormatSettings);
s := ConvertEncoding(s, EncodingUTF8, FEncoding); s := ConvertEncoding(s, EncodingUTF8, FEncoding);
FCSVBuilder.AppendCell(s); FCSVBuilder.AppendCell(s);
end; end;

View File

@ -138,6 +138,7 @@ type
procedure TestWriteRead_OOXML_WordWrap; procedure TestWriteRead_OOXML_WordWrap;
{ CSV Tests } { CSV Tests }
procedure TestWriteRead_CSV_DateTimeFormats;
procedure TestWriteRead_CSV_NumberFormats_0; procedure TestWriteRead_CSV_NumberFormats_0;
procedure TestWriteRead_CSV_NumberFormats_1; procedure TestWriteRead_CSV_NumberFormats_1;
end; end;
@ -319,6 +320,9 @@ end;
procedure TSpreadWriteReadFormatTests.TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat; procedure TSpreadWriteReadFormatTests.TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat;
AVariant: Integer = 0); AVariant: Integer = 0);
{ AVariant specifies variants for csv:
0 = decimal and thousand separator as in workbook's FormatSettings,
1 = intercanged }
var var
MyWorksheet: TsWorksheet; MyWorksheet: TsWorksheet;
MyWorkbook: TsWorkbook; MyWorkbook: TsWorkbook;
@ -476,7 +480,7 @@ begin
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
MyWorkbook.ReadFromFile(TempFile, AFormat); MyWorkbook.ReadFromFile(TempFile, AFormat);
if AFormat = sfExcel2 then if AFormat in [sfExcel2, sfCSV] then
MyWorksheet := MyWorkbook.GetFirstWorksheet MyWorksheet := MyWorkbook.GetFirstWorksheet
else else
MyWorksheet := GetWorksheetByName(MyWorkbook, FmtDateTimesSheet); MyWorksheet := GetWorksheetByName(MyWorkbook, FmtDateTimesSheet);
@ -525,6 +529,12 @@ begin
TestWriteRead_DateTimeFormats(sfOOXML); TestWriteRead_DateTimeFormats(sfOOXML);
end; end;
procedure TSpreadWriteReadFormatTests.TestWriteRead_CSV_DateTimeFormats;
begin
TestWriteRead_DateTimeFormats(sfCSV);
end;
{ --- Alignment tests --- } { --- Alignment tests --- }
procedure TSpreadWriteReadFormatTests.TestWriteRead_Alignment(AFormat: TsSpreadsheetFormat); procedure TSpreadWriteReadFormatTests.TestWriteRead_Alignment(AFormat: TsSpreadsheetFormat);