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:
parent
a1fe949908
commit
6d388ffc1a
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user