You've already forked lazarus-ccr
+ Add support for text/number/date formats in BIFF8/xls.
+ Add support for writing out date/time to non-BIFF8 xls (though as text only for now) Slightly modified patch by wp, thanks a lot. See http://forum.lazarus.freepascal.org/index.php/topic,22940.msg136761.html#msg136761 Still to do: add test cases for invalid format strings git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2863 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -55,8 +55,11 @@ begin
|
||||
MyWorksheet.WriteUTF8Text(1, 2, 'Third');
|
||||
MyWorksheet.WriteUTF8Text(1, 3, 'Fourth');
|
||||
|
||||
// Write current date/time
|
||||
MyWorksheet.WriteDateTime(2, 0, now);
|
||||
|
||||
// Save the spreadsheet to a file
|
||||
MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2);
|
||||
MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2, true);
|
||||
MyWorkbook.Free;
|
||||
end.
|
||||
|
||||
|
@ -10,8 +10,7 @@ program excel5write;
|
||||
{$mode delphi}{$H+}
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpspreadsheet, xlsbiff5,
|
||||
laz_fpspreadsheet, fpsconvencoding;
|
||||
Classes, SysUtils, fpspreadsheet, xlsbiff5;
|
||||
|
||||
const
|
||||
Str_First = 'First';
|
||||
@ -40,9 +39,12 @@ begin
|
||||
MyWorksheet.WriteNumber(0, 2, 3.0);// C1
|
||||
MyWorksheet.WriteNumber(0, 3, 4.0);// D1
|
||||
MyWorksheet.WriteUTF8Text(4, 2, Str_Total);// C5
|
||||
MyWorksheet.WriteNumber(4, 3, 10); // D5
|
||||
MyWorksheet.WriteNumber(4, 3, 10); // D5
|
||||
|
||||
{ Uncommend this to test large XLS files
|
||||
// Write current date/time
|
||||
MyWorksheet.WriteDateTime(5, 0, now);
|
||||
|
||||
{ Uncomment this to test large XLS files
|
||||
for i := 2 to 20 do
|
||||
begin
|
||||
MyWorksheet.WriteAnsiText(i, 0, ParamStr(0));
|
||||
@ -83,7 +85,7 @@ begin
|
||||
MyWorksheet.WriteUTF8Text(0, 3, Str_Fourth);
|
||||
|
||||
// Save the spreadsheet to a file
|
||||
MyWorkbook.WriteToFile(MyDir + 'test.xls', sfExcel5, False);
|
||||
MyWorkbook.WriteToFile(MyDir + 'test.xls', sfExcel5, true);
|
||||
MyWorkbook.Free;
|
||||
end.
|
||||
|
||||
|
@ -28,6 +28,7 @@ var
|
||||
MyDir: string;
|
||||
i: Integer;
|
||||
lCell: PCell;
|
||||
number: Double;
|
||||
begin
|
||||
MyDir := ExtractFilePath(ParamStr(0));
|
||||
|
||||
@ -41,7 +42,7 @@ begin
|
||||
MyWorksheet.WriteNumber(0, 2, 3.0);// C1
|
||||
MyWorksheet.WriteNumber(0, 3, 4.0);// D1
|
||||
MyWorksheet.WriteUTF8Text(4, 2, Str_Total);// C5
|
||||
MyWorksheet.WriteNumber(4, 3, 10); // D5
|
||||
MyWorksheet.WriteNumber(4, 3, 10); // D5
|
||||
|
||||
// D6 number with background color
|
||||
MyWorksheet.WriteNumber(5, 3, 10);
|
||||
@ -49,7 +50,7 @@ begin
|
||||
lCell^.BackgroundColor := scPURPLE;
|
||||
lCell^.UsedFormattingFields := [uffBackgroundColor];
|
||||
|
||||
{ Uncommend this to test large XLS files
|
||||
{ Uncomment this to test large XLS files
|
||||
for i := 2 to 20 do
|
||||
begin
|
||||
MyWorksheet.WriteAnsiText(i, 0, ParamStr(0));
|
||||
@ -80,6 +81,63 @@ begin
|
||||
|
||||
//MyFormula.FormulaStr := '';
|
||||
|
||||
// Write current date/time to cells B11:B16
|
||||
MyWorksheet.WriteUTF8Text(10, 0, 'nfShortDate');
|
||||
MyWorksheet.WriteDateTime(10, 1, now, nfShortDate);
|
||||
MyWorksheet.WriteUTF8Text(11, 0, 'nfShortTime');
|
||||
MyWorksheet.WriteDateTime(11, 1, now, nfShortTime);
|
||||
MyWorksheet.WriteUTF8Text(12, 0, 'nfLongTime');
|
||||
MyWorksheet.WriteDateTime(12, 1, now, nfLongTime);
|
||||
MyWorksheet.WriteUTF8Text(13, 0, 'nfShortDateTime');
|
||||
MyWorksheet.WriteDateTime(13, 1, now, nfShortDateTime);
|
||||
MyWorksheet.WriteUTF8Text(14, 0, 'nfFmtDateTime, DM');
|
||||
MyWorksheet.WriteDateTime(14, 1, now, nfFmtDateTime, 'DM');
|
||||
MyWorksheet.WriteUTF8Text(15, 0, 'nfFmtDateTime, MY');
|
||||
MyWorksheet.WriteDateTime(15, 1, now, nfFmtDateTime, 'MY');
|
||||
MyWorksheet.WriteUTF8Text(16, 0, 'nfShortTimeAM');
|
||||
MyWorksheet.WriteDateTime(16, 1, now, nfShortTimeAM);
|
||||
MyWorksheet.WriteUTF8Text(17, 0, 'nfLongTimeAM');
|
||||
MyWorksheet.WriteDateTime(17, 1, now, nfLongTimeAM);
|
||||
MyWorksheet.WriteUTF8Text(18, 0, 'nfFmtDateTime, MS');
|
||||
MyWorksheet.WriteDateTime(18, 1, now, nfFmtDateTime, 'MS');
|
||||
MyWorksheet.WriteUTF8Text(19, 0, 'nfFmtDateTime, MSZ');
|
||||
MyWorksheet.WriteDateTime(19, 1, now, nfFmtDateTime, 'MSZ');
|
||||
|
||||
// Write formatted numbers
|
||||
number := 12345.67890123456789;
|
||||
MyWorksheet.WriteUTF8Text(24, 1, '12345.67890123456789');
|
||||
MyWorksheet.WriteUTF8Text(24, 2, '-12345.67890123456789');
|
||||
MyWorksheet.WriteUTF8Text(25, 0, 'nfFixed, 0 decs');
|
||||
MyWorksheet.WriteNumber(25, 1, number, nfFixed, 0);
|
||||
MyWorksheet.WriteNumber(25, 2, -number, nfFixed, 0);
|
||||
MyWorksheet.WriteUTF8Text(26, 0, 'nfFixed, 2 decs');
|
||||
MyWorksheet.WriteNumber(26, 1, number, nfFixed, 2);
|
||||
MyWorksheet.WriteNumber(26, 2, -number, nfFixed, 2);
|
||||
MyWorksheet.WriteUTF8Text(27, 0, 'nfFixedTh, 0 decs');
|
||||
MyWorksheet.WriteNumber(27, 1, number, nfFixedTh, 0);
|
||||
MyWorksheet.WriteNumber(27, 2, -number, nfFixedTh, 0);
|
||||
MyWorksheet.WriteUTF8Text(28, 0, 'nfFixedTh, 2 decs');
|
||||
MyWorksheet.WriteNumber(28, 1, number, nfFixedTh, 2);
|
||||
MyWorksheet.WriteNumber(28, 2, -number, nfFixedTh, 2);
|
||||
MyWorksheet.WriteUTF8Text(29, 0, 'nfSci, 1 dec');
|
||||
MyWorksheet.WriteNumber(29, 1, number, nfSci);
|
||||
MyWorksheet.WriteNumber(29, 2, -number, nfSci);
|
||||
MyWorksheet.WriteNumber(29, 3, 1.0/number, nfSci);
|
||||
MyWorksheet.WriteNumber(29, 4, -1.0/number, nfSci);
|
||||
MyWorksheet.WriteUTF8Text(30, 0, 'nfExp, 2 decs');
|
||||
MyWorksheet.WriteNumber(30, 1, number, nfExp, 2);
|
||||
MyWorksheet.WriteNumber(30, 2, -number, nfExp, 2);
|
||||
MyWorksheet.WriteNumber(30, 3, 1.0/number, nfExp, 2);
|
||||
MyWorksheet.WriteNumber(30, 4, -1.0/number, nfExp, 2);
|
||||
|
||||
number := 1.333333333;
|
||||
MyWorksheet.WriteUTF8Text(35, 0, 'nfPercent, 0 decs');
|
||||
MyWorksheet.WriteNumber(35, 1, number, nfPercent, 0);
|
||||
MyWorksheet.WriteUTF8Text(36, 0, 'nfPercent, 2 decs');
|
||||
MyWorksheet.WriteNumber(36, 1, number, nfPercent, 2);
|
||||
MyWorksheet.WriteUTF8Text(37, 0, 'nfTimeInterval');
|
||||
MyWorksheet.WriteDateTime(37, 1, number, nfTimeInterval);
|
||||
|
||||
// Creates a new worksheet
|
||||
MyWorksheet := MyWorkbook.AddWorksheet(Str_Worksheet2);
|
||||
|
||||
@ -92,7 +150,7 @@ begin
|
||||
MyWorksheet.WriteUsedFormatting(0, 1, [uffBold]);
|
||||
|
||||
// Save the spreadsheet to a file
|
||||
MyWorkbook.WriteToFile(MyDir + 'test2.xls', sfExcel8, False);
|
||||
MyWorkbook.WriteToFile(MyDir + 'test.xls', sfExcel8, true);
|
||||
MyWorkbook.Free;
|
||||
end.
|
||||
|
||||
|
@ -33,7 +33,7 @@ begin
|
||||
MyWorksheet.WriteNumber(0, 2, 3.0);
|
||||
MyWorksheet.WriteNumber(0, 3, 4.0);
|
||||
|
||||
// Uncommend this to test large XLS files
|
||||
// Uncomment this to test large XLS files
|
||||
for i := 2 to 2{20} do
|
||||
begin
|
||||
MyWorksheet.WriteUTF8Text(i, 0, ParamStr(0));
|
||||
@ -61,6 +61,9 @@ begin
|
||||
MyWorksheet.WriteUTF8Text(0, 2, 'Third');
|
||||
MyWorksheet.WriteUTF8Text(0, 3, 'Fourth');
|
||||
|
||||
// Write current date/time
|
||||
MyWorksheet.WriteDateTime(0, 5, now);
|
||||
|
||||
// Save the spreadsheet to a file
|
||||
MyWorkbook.WriteToFile(MyDir + 'test.xlsx', sfOOXML);
|
||||
MyWorkbook.Free;
|
||||
|
@ -31,6 +31,7 @@ begin
|
||||
MyWorksheet.WriteNumber(0, 3, 4.0);// D1
|
||||
MyWorksheet.WriteUTF8Text(4, 2, 'Total:');// C5
|
||||
MyWorksheet.WriteNumber(4, 3, 10); // D5
|
||||
MyWorksheet.WriteDateTime(5, 0, now);
|
||||
// Add some formatting
|
||||
MyWorksheet.WriteUsedFormatting(0, 0, [uffBold]);
|
||||
|
||||
|
@ -89,6 +89,7 @@ type
|
||||
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); override;
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override;
|
||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override;
|
||||
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@ -709,6 +710,16 @@ begin
|
||||
' </table:table-cell>' + LineEnding;
|
||||
end;
|
||||
|
||||
{
|
||||
Writes the date/time as a text to the sheet.
|
||||
Currently, no formatting code is written.
|
||||
}
|
||||
procedure TsSpreadOpenDocWriter.WriteDateTime(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
|
||||
begin
|
||||
WriteLabel(AStream, ARow, ACol, FormatDateTime('c', AValue), ACell);
|
||||
end;
|
||||
|
||||
{
|
||||
Registers this reader / writer on fpSpreadsheet
|
||||
}
|
||||
|
@ -102,7 +102,9 @@ type
|
||||
enough to be able to read/write date values.
|
||||
}
|
||||
|
||||
TsNumberFormat = (nfGeneral, nfShortDate, nfShortDateTime);
|
||||
TsNumberFormat = (nfGeneral, nfFixed, nfFixedTh, nfExp, nfSci, nfPercentage,
|
||||
nfShortDateTime, nfFmtDateTime, nfShortDate, nfShortTime, nfLongTime,
|
||||
nfShortTimeAM, nfLongTimeAM, nfTimeInterval);
|
||||
|
||||
{@@ Text rotation formatting. The text is rotated relative to the standard
|
||||
orientation, which is from left to right horizontal: --->
|
||||
@ -187,6 +189,8 @@ type
|
||||
Border: TsCellBorders;
|
||||
BackgroundColor: TsColor;
|
||||
NumberFormat: TsNumberFormat;
|
||||
NumberFormatStr: String;
|
||||
NumberDecimals: Word;
|
||||
RGBBackgroundColor: TFPColor; // only valid if BackgroundColor=scRGBCOLOR
|
||||
end;
|
||||
|
||||
@ -242,8 +246,10 @@ type
|
||||
function ReadBackgroundColor(ARow, ACol: Cardinal): TsColor;
|
||||
procedure RemoveAllCells;
|
||||
procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring);
|
||||
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double);
|
||||
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime);
|
||||
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double;
|
||||
AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2);
|
||||
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
|
||||
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
|
||||
procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula);
|
||||
procedure WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat);
|
||||
procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula);
|
||||
@ -378,10 +384,14 @@ procedure RegisterSpreadFormat(
|
||||
AWriterClass: TsSpreadWriterClass;
|
||||
AFormat: TsSpreadsheetFormat);
|
||||
|
||||
function SciFloat(AValue: Double; ADecimals: Word): String;
|
||||
function TimeIntervalToString(AValue: TDateTime): String;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math;
|
||||
Math, StrUtils;
|
||||
|
||||
var
|
||||
{ Translatable strings }
|
||||
@ -405,6 +415,51 @@ begin
|
||||
GsSpreadFormats[len].Format := AFormat;
|
||||
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.
|
||||
}
|
||||
function SciFloat(AValue: Double; ADecimals: Word): String;
|
||||
var
|
||||
m: Double;
|
||||
ex: Integer;
|
||||
begin
|
||||
if AValue = 0 then
|
||||
Result := '0.0'
|
||||
else begin
|
||||
ex := floor(log10(abs(AValue))); // exponent
|
||||
// round exponent to multiples of 3
|
||||
ex := (ex div 3) * 3;
|
||||
if ex < 0 then dec(ex, 3);
|
||||
m := AValue * Power(10, -ex); // mantisse
|
||||
Result := Format('%.*fE%d', [ADecimals, m, ex]);
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@
|
||||
Formats the number AValue as a time string with hours, minutes and seconds.
|
||||
Unlike TimeToStr there can be more than 24 hours.
|
||||
}
|
||||
function TimeIntervalToString(AValue: TDateTime): String;
|
||||
var
|
||||
hrs: Integer;
|
||||
diff: Double;
|
||||
h,m,s,z: Word;
|
||||
ts: String;
|
||||
begin
|
||||
ts := DefaultFormatSettings.TimeSeparator;
|
||||
DecodeTime(frac(abs(AValue)), h, m, s, z);
|
||||
hrs := h + trunc(abs(AValue))*24;
|
||||
if z > 499 then inc(s);
|
||||
if hrs > 0 then
|
||||
Result := Format('%d%s%.2d%s%.2d', [hrs, ts, m, ts, s])
|
||||
else
|
||||
Result := Format('%d%s%.2d', [m, ts, s]);
|
||||
if AValue < 0.0 then Result := '-' + Result;
|
||||
end;
|
||||
|
||||
|
||||
{ TsWorksheet }
|
||||
|
||||
{@@
|
||||
@ -673,12 +728,42 @@ end;
|
||||
@return The text representation of the cell
|
||||
}
|
||||
function TsWorksheet.ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring;
|
||||
|
||||
function FloatToStrNoNaN(const Value: Double;
|
||||
ANumberFormat: TsNumberFormat; ANumberFormatStr: ansistring): ansistring;
|
||||
begin
|
||||
if IsNan(Value) then
|
||||
Result := ''
|
||||
else
|
||||
if ANumberFormat = nfSci then
|
||||
Result := SciFloat(Value, 1)
|
||||
else
|
||||
if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then
|
||||
Result := FloatToStr(Value)
|
||||
else
|
||||
if (ANumberFormat = nfPercentage) then
|
||||
Result := FormatFloat(ANumberFormatStr, Value*100) + '%'
|
||||
else
|
||||
Result := FormatFloat(ANumberFormatStr, Value);
|
||||
end;
|
||||
|
||||
function DateTimeToStrNoNaN(const Value: Double;
|
||||
ANumberFormat: TsNumberFormat; ANumberFormatStr: String): ansistring;
|
||||
begin
|
||||
Result := '';
|
||||
if not IsNaN(Value) then begin
|
||||
if ANumberFormat = nfTimeInterval then
|
||||
Result := TimeIntervalToString(Value)
|
||||
else
|
||||
if ANumberFormatStr = '' then
|
||||
Result := FormatDateTime('c', Value)
|
||||
else
|
||||
Result := FormatDateTime(ANumberFormatStr, Value);
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
ACell: PCell;
|
||||
function FloatToStrNoNaN(const Value: Double): ansistring;
|
||||
begin
|
||||
if IsNan(Value) then Result:='' else Result:=FloatToStr(Value);
|
||||
end;
|
||||
begin
|
||||
ACell := FindCell(ARow, ACol);
|
||||
|
||||
@ -690,17 +775,9 @@ begin
|
||||
|
||||
case ACell^.ContentType of
|
||||
//cctFormula
|
||||
cctNumber: Result := FloatToStrNoNaN(ACell^.NumberValue);
|
||||
cctNumber: Result := FloatToStrNoNaN(ACell^.NumberValue, ACell^.NumberFormat, ACell^.NumberFormatStr);
|
||||
cctUTF8String: Result := ACell^.UTF8StringValue;
|
||||
cctDateTime:
|
||||
begin
|
||||
Result := SysUtils.DateToStr(ACell^.DateTimeValue);
|
||||
// User can have specified custom date/time format or one of the other built
|
||||
// in formats that include time. We can't parse all of them so just return
|
||||
// time as well unless absolutely sure we only want a date
|
||||
if ACell^.NumberFormat<>nfShortDate then
|
||||
Result := Result+' ' + SysUtils.TimeToStr(ACell^.DateTimeValue);
|
||||
end;
|
||||
cctDateTime: Result := DateTimeToStrNoNaN(ACell^.DateTimeValue, ACell^.NumberFormat, ACell^.NumberFormatStr);
|
||||
else
|
||||
Result := '';
|
||||
end;
|
||||
@ -839,35 +916,63 @@ end;
|
||||
@param ARow The row of the cell
|
||||
@param ACol The column of the cell
|
||||
@param ANumber The number to be written
|
||||
@param AFormat The format identifier, e.g. nfFixed (optional)
|
||||
@param ADecimals The number of decimals used for formatting (optional)
|
||||
}
|
||||
procedure TsWorksheet.WriteNumber(ARow, ACol: Cardinal; ANumber: double);
|
||||
procedure TsWorksheet.WriteNumber(ARow, ACol: Cardinal; ANumber: double;
|
||||
AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2);
|
||||
var
|
||||
ACell: PCell;
|
||||
decs: String;
|
||||
begin
|
||||
ACell := GetCell(ARow, ACol);
|
||||
|
||||
ACell^.ContentType := cctNumber;
|
||||
ACell^.NumberValue := ANumber;
|
||||
ACell^.NumberDecimals := ADecimals;
|
||||
if AFormat <> nfGeneral then begin
|
||||
Include(ACell^.UsedFormattingFields, uffNumberFormat);
|
||||
ACell^.NumberFormat := AFormat;
|
||||
decs := DupeString('0', ADecimals);
|
||||
if ADecimals > 0 then decs := '.' + decs;
|
||||
case AFormat of
|
||||
nfFixed:
|
||||
ACell^.NumberFormatStr := '0' + decs;
|
||||
nfFixedTh:
|
||||
ACell^.NumberFormatStr := '#,##0' + decs;
|
||||
nfExp:
|
||||
ACell^.NumberFormatStr := '0' + decs + 'E+00';
|
||||
nfSci:
|
||||
ACell^.NumberFormatStr := '';
|
||||
nfPercentage:
|
||||
ACell^.NumberFormatStr := '0' + decs;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@
|
||||
Writes a date/time value to a determined cell
|
||||
|
||||
@param ARow The row of the cell
|
||||
@param ACol The column of the cell
|
||||
@param AValue The date/time/datetime to be written
|
||||
@param ARow The row of the cell
|
||||
@param ACol The column of the cell
|
||||
@param AValue The date/time/datetime to be written
|
||||
@param AFormat The format specifier, e.g. nfShortDate (optional)
|
||||
@param AFormatStr Format string, used only for nfFmtDateTime.
|
||||
Must follow the rules for "FormatDateTime", or use
|
||||
"dm" as abbreviation for "d/mmm", "my" for "mmm/yy",
|
||||
"ms" for "nn:ss", "msz" for "nn:ss.z" (optional)
|
||||
|
||||
Note: at least Excel xls does not recognize a separate datetime cell type:
|
||||
a datetime is stored as a (floating point) Number, and the cell is formatted
|
||||
as a date (either built-in or a custom format).
|
||||
This procedure automatically sets the cell format to short date/time. You may
|
||||
change this format to another date/time format, but changing it to another
|
||||
format (e.g. General) will likely lead to the cell being written out as a
|
||||
plain number.
|
||||
|
||||
Note: custom formats are currently not supported by the writer.
|
||||
}
|
||||
procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime);
|
||||
procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
|
||||
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
|
||||
var
|
||||
ACell: PCell;
|
||||
fmt: String;
|
||||
begin
|
||||
ACell := GetCell(ARow, ACol);
|
||||
|
||||
@ -876,11 +981,32 @@ begin
|
||||
// Date/time is actually a number field in Excel.
|
||||
// To make sure it gets saved correctly, set a date format (instead of General).
|
||||
// The user can choose another date format if he wants to
|
||||
if not(uffNumberFormat in ACell^.UsedFormattingFields) or
|
||||
((uffNumberFormat in ACell^.UsedFormattingFields) and (ACell^.NumberFormat = nfGeneral)) then
|
||||
begin
|
||||
Include(ACell^.UsedFormattingFields, uffNumberFormat);
|
||||
ACell^.NumberFormat := nfShortDateTime;
|
||||
Include(ACell^.UsedFormattingFields, uffNumberFormat);
|
||||
ACell^.NumberFormat := AFormat;
|
||||
case AFormat of
|
||||
nfShortDateTime:
|
||||
ACell^.NumberFormatStr := FormatSettings.ShortDateFormat + ' ' + FormatSettings.ShortTimeFormat;
|
||||
nfShortDate:
|
||||
ACell^.NumberFormatStr := FormatSettings.ShortDateFormat;
|
||||
nfShortTime:
|
||||
ACell^.NumberFormatStr := 't';
|
||||
nfLongTime:
|
||||
ACell^.NumberFormatStr := 'tt';
|
||||
nfShortTimeAM:
|
||||
ACell^.NumberFormatStr := 't am/pm';
|
||||
nfLongTimeAM:
|
||||
ACell^.NumberFormatStr := 'tt am/pm';
|
||||
nfFmtDateTime:
|
||||
begin
|
||||
fmt := lowercase(AFormatStr);
|
||||
if fmt = 'dm' then ACell^.NumberFormatStr := 'd/mmm'
|
||||
else if fmt = 'my' then ACell^.NumberFormatSTr := 'mmm/yy'
|
||||
else if fmt = 'ms' then ACell^.NumberFormatStr := 'nn:ss'
|
||||
else if fmt = 'msz' then ACell^.NumberFormatStr := 'nn:ss.z'
|
||||
else ACell^.NumberFormatStr := AFormatStr;
|
||||
end;
|
||||
nfTimeInterval:
|
||||
ACell^.NumberFormatStr := '';
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -1503,8 +1629,16 @@ begin
|
||||
if uffBackgroundColor in AFormat^.UsedFormattingFields then
|
||||
if (FFormattingStyles[i].BackgroundColor <> AFormat^.BackgroundColor) then Continue;
|
||||
|
||||
if uffNumberFormat in AFormat^.UsedFormattingFields then
|
||||
if uffNumberFormat in AFormat^.UsedFormattingFields then begin
|
||||
if (FFormattingStyles[i].NumberFormat <> AFormat^.NumberFormat) then Continue;
|
||||
case AFormat^.NumberFormat of
|
||||
nfFixed, nfFixedTh, nfPercentage, nfExp:
|
||||
if (FFormattingStyles[i].NumberDecimals <> AFormat^.NumberDecimals) then Continue;
|
||||
nfShortDate, nfShortDateTime, nfShortTime, nfLongTime, nfShortTimeAM,
|
||||
nfLongTimeAM, nfFmtDateTime, nfTimeInterval:
|
||||
if (FFormattingstyles[i].NumberFormatStr <> AFormat^.NumberFormatStr) then Continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
// If we arrived here it means that the styles match
|
||||
Exit(i);
|
||||
@ -1735,6 +1869,7 @@ begin
|
||||
// Silently dump the formula; child classes should implement their own support
|
||||
end;
|
||||
|
||||
|
||||
finalization
|
||||
|
||||
SetLength(GsSpreadFormats, 0);
|
||||
|
@ -22,7 +22,7 @@ uses
|
||||
|
||||
var
|
||||
// Norm to test against - list of dates/times that should occur in spreadsheet
|
||||
SollDates: array[0..11] of TDateTime; //"Soll" is a German word in Dutch accountancy circles meaning "normative value to check against". There ;)
|
||||
SollDates: array[0..37] of TDateTime; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;)
|
||||
// Initializes Soll*/normative variables.
|
||||
// Useful in test setup procedures to make sure the norm is correct.
|
||||
procedure InitSollDates;
|
||||
@ -53,6 +53,32 @@ type
|
||||
procedure TestReadDate9;
|
||||
procedure TestReadDate10;
|
||||
procedure TestReadDate11;
|
||||
procedure TestReadDate12;
|
||||
procedure TestReadDate13;
|
||||
procedure TestReadDate14;
|
||||
procedure TestReadDate15;
|
||||
procedure TestReadDate16;
|
||||
procedure TestReadDate17;
|
||||
procedure TestReadDate18;
|
||||
procedure TestReadDate19;
|
||||
procedure TestReadDate20;
|
||||
procedure TestReadDate21;
|
||||
procedure TestReadDate22;
|
||||
procedure TestReadDate23;
|
||||
procedure TestReadDate24;
|
||||
procedure TestReadDate25;
|
||||
procedure TestReadDate26;
|
||||
procedure TestReadDate27;
|
||||
procedure TestReadDate28;
|
||||
procedure TestReadDate29;
|
||||
procedure TestReadDate30;
|
||||
procedure TestReadDate31;
|
||||
procedure TestReadDate32;
|
||||
procedure TestReadDate33;
|
||||
procedure TestReadDate34;
|
||||
procedure TestReadDate35;
|
||||
procedure TestReadDate36;
|
||||
procedure TestReadDate37;
|
||||
procedure TestReadDate1899_0; //same as above except with the 1899/1900 date system set
|
||||
procedure TestReadDate1899_1;
|
||||
procedure TestReadDate1899_2;
|
||||
@ -65,6 +91,32 @@ type
|
||||
procedure TestReadDate1899_9;
|
||||
procedure TestReadDate1899_10;
|
||||
procedure TestReadDate1899_11;
|
||||
procedure TestReadDate1899_12;
|
||||
procedure TestReadDate1899_13;
|
||||
procedure TestReadDate1899_14;
|
||||
procedure TestReadDate1899_15;
|
||||
procedure TestReadDate1899_16;
|
||||
procedure TestReadDate1899_17;
|
||||
procedure TestReadDate1899_18;
|
||||
procedure TestReadDate1899_19;
|
||||
procedure TestReadDate1899_20;
|
||||
procedure TestReadDate1899_21;
|
||||
procedure TestReadDate1899_22;
|
||||
procedure TestReadDate1899_23;
|
||||
procedure TestReadDate1899_24;
|
||||
procedure TestReadDate1899_25;
|
||||
procedure TestReadDate1899_26;
|
||||
procedure TestReadDate1899_27;
|
||||
procedure TestReadDate1899_28;
|
||||
procedure TestReadDate1899_29;
|
||||
procedure TestReadDate1899_30;
|
||||
procedure TestReadDate1899_31;
|
||||
procedure TestReadDate1899_32;
|
||||
procedure TestReadDate1899_33;
|
||||
procedure TestReadDate1899_34;
|
||||
procedure TestReadDate1899_35;
|
||||
procedure TestReadDate1899_36;
|
||||
procedure TestReadDate1899_37;
|
||||
end;
|
||||
|
||||
{ TSpreadWriteReadDateTests }
|
||||
@ -109,6 +161,36 @@ begin
|
||||
SollDates[9]:=EncodeTime(18,0,0,0);
|
||||
SollDates[10]:=EncodeTime(23,59,0,0);
|
||||
SollDates[11]:=EncodeTime(23,59,59,0);
|
||||
|
||||
SollDates[12]:=SollDates[1]; // #1 formatted as nfShortDateTime
|
||||
SollDates[13]:=SollDates[1]; // #1 formatted as nfShortTime
|
||||
SollDates[14]:=SollDates[1]; // #1 formatted as nfLongTime
|
||||
SollDates[15]:=SollDates[1]; // #1 formatted as nfShortTimeAM
|
||||
SollDates[16]:=SollDates[1]; // #1 formatted as nfLongTimeAM
|
||||
SollDates[17]:=SollDates[1]; // #1 formatted as nfFmtDateTime dm
|
||||
SollDates[18]:=SollDates[1]; // #1 formatted as nfFmtDateTime my
|
||||
SollDates[19]:=SollDates[1]; // #1 formatted as nfFmtDateTime ms
|
||||
|
||||
SollDates[20]:=SollDates[5]; // #5 formatted as nfShortDateTime
|
||||
SollDates[21]:=SollDates[5]; // #5 formatted as nfShortTime
|
||||
SollDates[22]:=SollDates[5]; // #5 formatted as nfLongTime
|
||||
SollDates[23]:=SollDates[5]; // #5 formatted as nfShortTimeAM
|
||||
SollDates[24]:=SollDates[5]; // #5 formatted as nfLongTimeAM
|
||||
SollDates[25]:=SollDates[5]; // #5 formatted as nfFmtDateTime dm
|
||||
SollDates[26]:=SollDates[5]; // #5 formatted as nfFmtDateTime my
|
||||
SollDates[27]:=SollDates[5]; // #5 formatted as nfFmtDateTime ms
|
||||
|
||||
SollDates[28]:=SollDates[11]; // #11 formatted as nfShortDateTime
|
||||
SollDates[29]:=SollDates[11]; // #11 formatted as nfShortTime
|
||||
SollDates[30]:=SollDates[11]; // #11 formatted as nfLongTime
|
||||
SollDates[31]:=SollDates[11]; // #11 formatted as nfShortTimeAM
|
||||
SollDates[32]:=SollDates[11]; // #11 formatted as nfLongTimeAM
|
||||
SollDates[33]:=SollDates[11]; // #11 formatted as nfFmtDateTime dm
|
||||
SollDates[34]:=SollDates[11]; // #11 formatted as nfFmtDateTime my
|
||||
SollDates[35]:=SollDates[11]; // #11 formatted as nfFmtDateTime ms
|
||||
|
||||
SollDates[36]:=EncodeTime(3,45,12,0); // formatted as nfTimeDuration
|
||||
SollDates[37]:=EncodeTime(3,45,12,0) + 1 // formatted as nfTimeDuration
|
||||
end;
|
||||
|
||||
{ TSpreadWriteReadDateTests }
|
||||
@ -270,6 +352,136 @@ begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,11);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate12;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,12);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate13;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,13);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate14;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,14);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate15;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,15);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate16;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,16);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate17;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,17);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate18;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,18);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate19;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,19);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate20;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,20);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate21;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,21);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate22;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,22);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate23;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,23);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate24;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,24);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate25;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,25);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate26;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,26);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate27;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,27);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate28;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,28);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate29;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,29);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate30;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,30);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate31;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,31);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate32;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,32);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate33;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,33);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate34;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,34);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate35;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,35);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate36;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,36);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate37;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,37);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_0;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,0);
|
||||
@ -330,6 +542,136 @@ begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,11);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_12;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,12);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_13;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,13);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_14;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,14);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_15;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,15);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_16;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,16);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_17;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,17);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_18;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,18);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_19;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,19);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_20;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,20);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_21;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,21);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_22;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,22);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_23;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,23);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_24;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,24);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_25;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,25);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_26;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,26);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_27;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,27);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_28;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,28);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_29;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,29);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_30;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,30);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_31;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,31);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_32;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,32);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_33;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,33);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_34;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,34);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_35;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,35);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_36;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,36);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadDateTests.TestReadDate1899_37;
|
||||
begin
|
||||
TestReadDate(ExtractFilePath(ParamStr(0)) + TestFileBIFF8_1899,37);
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
// Register so these tests are included in a full run
|
||||
|
225
components/fpspreadsheet/tests/formattests.pas
Normal file
225
components/fpspreadsheet/tests/formattests.pas
Normal file
@ -0,0 +1,225 @@
|
||||
unit formattests;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
// Not using lazarus package as the user may be working with multiple versions
|
||||
// Instead, add .. to unit search path
|
||||
Classes, SysUtils, fpcunit, testutils, testregistry,
|
||||
fpsallformats, fpspreadsheet, xlsbiff8 {and a project requirement for lclbase for utf8 handling},
|
||||
testsutility;
|
||||
|
||||
var
|
||||
// Norm to test against - list of strings that should occur in spreadsheet
|
||||
SollNumberStrings: array[0..6, 0..8] of string;
|
||||
SollNumbers: array[0..6] of Double;
|
||||
SollNumberFormats: array[0..8] of TsNumberFormat;
|
||||
SollNumberDecimals: array[0..8] of word;
|
||||
|
||||
SollDateTimeStrings: array[0..4, 0..9] of string;
|
||||
SollDateTimes: array[0..4] of TDateTime;
|
||||
SollDateTimeFormats: array[0..9] of TsNumberFormat;
|
||||
SollDateTimeFormatStrings: array[0..9] of String;
|
||||
|
||||
procedure InitSollFmtData;
|
||||
|
||||
type
|
||||
{ TSpreadWriteReadFormatTests }
|
||||
//Write to xls/xml file and read back
|
||||
TSpreadWriteReadFormatTests = class(TTestCase)
|
||||
private
|
||||
protected
|
||||
// Set up expected values:
|
||||
procedure SetUp; override;
|
||||
procedure TearDown; override;
|
||||
published
|
||||
// Writes out numbers & reads back.
|
||||
// If previous read tests are ok, this effectively tests writing.
|
||||
procedure TestWriteReadNumberFormats;
|
||||
// Repeat with date/times
|
||||
procedure TestWriteReadDateTimeFormats;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
const
|
||||
FmtNumbersSheet = 'Numbers';
|
||||
FmtDateTimesSheet = 'Date/Times';
|
||||
|
||||
// Initialize array with variables that represent the values
|
||||
// we expect to be in the test spreadsheet files.
|
||||
//
|
||||
// When adding tests, add values to this array
|
||||
// and increase array size in variable declaration
|
||||
procedure InitSollFmtData;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
// Set up norm - MUST match spreadsheet cells exactly
|
||||
|
||||
// Numbers
|
||||
SollNumbers[0] := 0.0;
|
||||
SollNumbers[1] := 1.0;
|
||||
SollNumbers[2] := -1.0;
|
||||
SollNumbers[3] := 1.2345E6;
|
||||
SollNumbers[4] := -1.23456E6;
|
||||
SollNumbers[5] := 1.23456E-6;
|
||||
SollNumbers[6] := -1.23456E-6;
|
||||
|
||||
SollNumberFormats[0] := nfGeneral; SollNumberDecimals[0] := 0;
|
||||
SollNumberFormats[1] := nfFixed; SollNumberDecimals[1] := 0;
|
||||
SollNumberFormats[2] := nfFixed; SollNumberDecimals[2] := 2;
|
||||
SollNumberFormats[3] := nfFixedTh; SollNumberDecimals[3] := 0;
|
||||
SollNumberFormats[4] := nfFixedTh; SollNumberDecimals[4] := 2;
|
||||
SollNumberFormats[5] := nfExp; SollNumberDecimals[5] := 2;
|
||||
SollNumberFormats[6] := nfSci; SollNumberDecimals[6] := 1;
|
||||
SollNumberFormats[7] := nfPercentage; SollNumberDecimals[7] := 0;
|
||||
SollNumberFormats[8] := nfPercentage; SollNumberDecimals[8] := 2;
|
||||
|
||||
for i:=Low(SollNumbers) to High(SollNumbers) do begin
|
||||
SollNumberStrings[i, 0] := FloatToStr(SollNumbers[i]);
|
||||
SollNumberStrings[i, 1] := FormatFloat('0', SollNumbers[i]);
|
||||
SollNumberStrings[i, 2] := FormatFloat('0.00', SollNumbers[i]);
|
||||
SollNumberStrings[i, 3] := FormatFloat('#,##0', SollNumbers[i]);
|
||||
SollNumberStrings[i, 4] := FormatFloat('#,##0.00', SollNumbers[i]);
|
||||
SollNumberStrings[i, 5] := FormatFloat('0.00E+00', SollNumbers[i]);
|
||||
SollNumberStrings[i, 6] := SciFloat(SollNumbers[i], 1);
|
||||
SollNumberStrings[i, 7] := FormatFloat('0', SollNumbers[i]*100) + '%';
|
||||
SollNumberStrings[i, 8] := FormatFloat('0.00', SollNumbers[i]*100) + '%';
|
||||
end;
|
||||
|
||||
// Date/time values
|
||||
SollDateTimes[0] := EncodeDate(2012, 1, 12) + EncodeTime(13, 14, 15, 567);
|
||||
SolLDateTimes[1] := EncodeDate(2012, 2, 29) + EncodeTime(0, 0, 0, 1);
|
||||
SollDateTimes[2] := EncodeDate(2040, 12, 31) + EncodeTime(12, 0, 0, 0);
|
||||
SollDateTimes[3] := 1 + EncodeTime(3,45, 0, 0);
|
||||
SollDateTimes[4] := EncodeTime(12, 0, 0, 0);
|
||||
|
||||
SollDateTimeFormats[0] := nfShortDateTime; SollDateTimeFormatStrings[0] := '';
|
||||
SollDateTimeFormats[1] := nfShortDate; SollDateTimeFormatStrings[1] := '';
|
||||
SollDateTimeFormats[2] := nfShortTime; SollDateTimeFormatStrings[2] := '';
|
||||
SollDateTimeFormats[3] := nfLongTime; SollDateTimeFormatStrings[3] := '';
|
||||
SollDateTimeFormats[4] := nfShortTimeAM; SollDateTimeFormatStrings[4] := '';
|
||||
SollDateTimeFormats[5] := nfLongTimeAM; SollDateTimeFormatStrings[5] := '';
|
||||
SollDateTimeFormats[6] := nfFmtDateTime; SollDateTimeFormatStrings[6] := 'dm';
|
||||
SolLDateTimeFormats[7] := nfFmtDateTime; SollDateTimeFormatStrings[7] := 'my';
|
||||
SollDateTimeFormats[8] := nfFmtDateTime; SollDateTimeFormatStrings[8] := 'ms';
|
||||
SollDateTimeFormats[9] := nfTimeInterval; SollDateTimeFormatStrings[9] := '';
|
||||
|
||||
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('t', SollDateTimes[i]);
|
||||
SolLDateTimeStrings[i, 3] := FormatDateTime('tt', SollDateTimes[i]);
|
||||
SollDateTimeStrings[i, 4] := FormatDateTime('t am/pm', SollDateTimes[i]);
|
||||
SollDateTimeStrings[i, 5] := FormatDateTime('tt 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, 9] := TimeIntervalToString(SollDateTimes[i]);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TSpreadWriteReadFormatTests }
|
||||
|
||||
procedure TSpreadWriteReadFormatTests.SetUp;
|
||||
begin
|
||||
inherited SetUp;
|
||||
InitSollFmtData; //just for security: make sure the variables are reset to default
|
||||
end;
|
||||
|
||||
procedure TSpreadWriteReadFormatTests.TearDown;
|
||||
begin
|
||||
inherited TearDown;
|
||||
end;
|
||||
|
||||
procedure TSpreadWriteReadFormatTests.TestWriteReadNumberFormats;
|
||||
var
|
||||
MyWorksheet: TsWorksheet;
|
||||
MyWorkbook: TsWorkbook;
|
||||
ActualString: String;
|
||||
Row, Col: Integer;
|
||||
TempFile: string; //write xls/xml to this file and read back from it
|
||||
begin
|
||||
TempFile:=GetTempFileName;
|
||||
{// Not needed: use workbook.writetofile with overwrite=true
|
||||
if fileexists(TempFile) then
|
||||
DeleteFile(TempFile);
|
||||
}
|
||||
// Write out all test values
|
||||
MyWorkbook := TsWorkbook.Create;
|
||||
MyWorkSheet:= MyWorkBook.AddWorksheet(FmtNumbersSheet);
|
||||
for Row := Low(SollNumbers) to High(SollNumbers) do
|
||||
for Col := ord(Low(SollNumberFormats)) to ord(High(SollNumberFormats)) do begin
|
||||
MyWorksheet.WriteNumber(Row, Col, SollNumbers[Row], SollNumberFormats[Col], SollNumberDecimals[Col]);
|
||||
ActualString := MyWorksheet.ReadAsUTF8Text(Row, Col);
|
||||
CheckEquals(SollNumberStrings[Row, Col], ActualString, 'Test unsaved string mismatch cell ' + CellNotation(MyWorksheet,Row,Col));
|
||||
end;
|
||||
MyWorkBook.WriteToFile(TempFile,sfExcel8,true);
|
||||
MyWorkbook.Free;
|
||||
|
||||
// Open the spreadsheet, as biff8
|
||||
MyWorkbook := TsWorkbook.Create;
|
||||
MyWorkbook.ReadFromFile(TempFile, sfExcel8);
|
||||
MyWorksheet:=GetWorksheetByName(MyWorkBook, FmtNumbersSheet);
|
||||
if MyWorksheet=nil then
|
||||
fail('Error in test code. Failed to get named worksheet');
|
||||
for Row := Low(SollNumbers) to High(SollNumbers) do
|
||||
for Col := Low(SollNumberFormats) to High(SollNumberFormats) do begin
|
||||
ActualString := MyWorkSheet.ReadAsUTF8Text(Row,Col);
|
||||
CheckEquals(SollNumberStrings[Row,Col],ActualString,'Test saved string mismatch cell '+CellNotation(MyWorkSheet,Row,Col));
|
||||
end;
|
||||
|
||||
// Finalization
|
||||
MyWorkbook.Free;
|
||||
|
||||
DeleteFile(TempFile);
|
||||
end;
|
||||
|
||||
procedure TSpreadWriteReadFormatTests.TestWriteReadDateTimeFormats;
|
||||
var
|
||||
MyWorksheet: TsWorksheet;
|
||||
MyWorkbook: TsWorkbook;
|
||||
ActualString: String;
|
||||
Row,Col: Integer;
|
||||
TempFile: string; //write xls/xml to this file and read back from it
|
||||
begin
|
||||
TempFile:=GetTempFileName;
|
||||
// Write out all test values
|
||||
MyWorkbook := TsWorkbook.Create;
|
||||
MyWorksheet := MyWorkbook.AddWorksheet(FmtDateTimesSheet);
|
||||
for Row := Low(SollDateTimes) to High(SollDateTimes) do
|
||||
for Col := Low(SollDateTimeFormats) to High(SollDateTimeFormats) do begin
|
||||
MyWorksheet.WriteDateTime(Row, Col, SollDateTimes[Row], SollDateTimeFormats[Col], SollDateTimeFormatStrings[Col]);
|
||||
ActualString := MyWorksheet.ReadAsUTF8Text(Row, Col);
|
||||
CheckEquals(SollDateTimeStrings[Row, Col], ActualString, 'Test unsaved string mismatch cell ' + CellNotation(MyWorksheet,Row,Col));
|
||||
end;
|
||||
MyWorkBook.WriteToFile(TempFile,sfExcel8,true);
|
||||
MyWorkbook.Free;
|
||||
|
||||
// Open the spreadsheet, as biff8
|
||||
MyWorkbook := TsWorkbook.Create;
|
||||
MyWorkbook.ReadFromFile(TempFile, sfExcel8);
|
||||
MyWorksheet := GetWorksheetByName(MyWorkbook, FmtDateTimesSheet);
|
||||
if MyWorksheet = nil then
|
||||
fail('Error in test code. Failed to get named worksheet');
|
||||
for Row := Low(SollDateTimes) to High(SollDateTimes) do
|
||||
for Col := Low(SollDateTimeFormats) to High(SollDateTimeFormats) do begin
|
||||
ActualString := myWorksheet.ReadAsUTF8Text(Row,Col);
|
||||
CheckEquals(SollDateTimeStrings[Row, Col], ActualString, 'Test saved string mismatch cell '+CellNotation(MyWorksheet,Row,Col));
|
||||
end;
|
||||
|
||||
// Finalization
|
||||
MyWorkbook.Free;
|
||||
|
||||
DeleteFile(TempFile);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TSpreadWriteReadFormatTests);
|
||||
InitSollFmtData;
|
||||
|
||||
end.
|
||||
|
@ -26,7 +26,7 @@ uses
|
||||
|
||||
var
|
||||
// Norm to test against - list of dates/times that should occur in spreadsheet
|
||||
SollColors: array[0..22] of tsColor; //"Soll" is a German word in Dutch accountancy circles meaning "normative value to check against". There ;)
|
||||
SollColors: array[0..22] of tsColor; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;)
|
||||
SollColorNames: array[0..22] of string; //matching names for SollColors
|
||||
// Initializes Soll*/normative variables.
|
||||
// Useful in test setup procedures to make sure the norm is correct.
|
||||
|
@ -22,7 +22,7 @@ uses
|
||||
|
||||
var
|
||||
// Norm to test against - list of numbers/times that should occur in spreadsheet
|
||||
SollNumbers: array[0..12] of double; //"Soll" is a German word in Dutch accountancy circles meaning "normative value to check against". There ;)
|
||||
SollNumbers: array[0..20] of double; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;)
|
||||
// Initializes Soll*/normative variables.
|
||||
// Useful in test setup procedures to make sure the norm is correct.
|
||||
procedure InitSollNumbers;
|
||||
@ -53,6 +53,14 @@ type
|
||||
procedure TestReadNumber9;
|
||||
procedure TestReadNumber10;
|
||||
procedure TestReadNumber11;
|
||||
procedure TestReadNumber12;
|
||||
procedure TestReadNumber13;
|
||||
procedure TestReadNumber14;
|
||||
procedure TestReadNumber15;
|
||||
procedure TestReadNumber16;
|
||||
procedure TestReadNumber17;
|
||||
procedure TestReadNumber18;
|
||||
procedure TestReadNumber19;
|
||||
end;
|
||||
|
||||
{ TSpreadWriteReadNumberTests }
|
||||
@ -98,6 +106,14 @@ begin
|
||||
SollNumbers[10]:=3.14159265358979; //some parts of pi
|
||||
SollNumbers[11]:=59000000; //59 million
|
||||
SollNumbers[12]:=59000000.1; //same + a tenth
|
||||
SollNumbers[13]:=0.3536; // 0.3536 formatted as percentage, no decimals
|
||||
SollNumbers[14]:=0.3536; // 0.3536 formatted as percentage, 2 decimals
|
||||
SollNumbers[15]:=59000000.1234; // 59 million + 0.1234 formatted with thousand separator, no decimals
|
||||
SollNumbers[16]:=59000000.1234; // 59 million + 0.1234 formatted with thousand separator, 2 decimals
|
||||
SollNumbers[17]:=-59000000.1234; // minus 59 million + 0.1234, formatted as "scientific" with 1 decimal
|
||||
SollNumbers[18]:=-59000000.1234; // minus 59 million + 0.1234, formatted as "exp" with 2 decimals
|
||||
SollNumbers[19]:=59000000.1234; // 59 million + 0.1234 formatted as currrency (EUROs), 2 decimals
|
||||
SollNumbers[20]:=59000000.1234; // 59 million + 0.1234 formatted as currrency (Dollars), 2 decimals
|
||||
end;
|
||||
|
||||
{ TSpreadWriteReadNumberTests }
|
||||
@ -252,6 +268,46 @@ begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,11);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber12;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,12);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber13;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,13);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber14;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,14);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber15;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,15);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber16;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,16);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber17;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,17);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber18;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,16);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadNumberTests.TestReadNumber19;
|
||||
begin
|
||||
TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,17);
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
// Register so these tests are included in a full run
|
||||
|
@ -10,7 +10,7 @@ uses
|
||||
testregistry,
|
||||
testdbwriter {used to get results into db},
|
||||
datetests, manualtests, numberstests, stringtests, internaltests,
|
||||
testsutility, testutils {the actual tests};
|
||||
testsutility, testutils, formattests {the actual tests};
|
||||
|
||||
const
|
||||
ShortOpts = 'ac:dhlpr:x';
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CONFIG>
|
||||
<ProjectOptions>
|
||||
<Version Value="9"/>
|
||||
@ -21,9 +21,13 @@
|
||||
<CompilerOptions>
|
||||
<Version Value="11"/>
|
||||
<PathDelim Value="\"/>
|
||||
<Target>
|
||||
<Filename Value="spreadtestgui"/>
|
||||
</Target>
|
||||
<SearchPaths>
|
||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||
<OtherUnitFiles Value=".."/>
|
||||
<UnitOutputDirectory Value="units\$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
<Parsing>
|
||||
<SyntaxOptions>
|
||||
@ -75,7 +79,7 @@
|
||||
<PackageName Value="FCL"/>
|
||||
</Item4>
|
||||
</RequiredPackages>
|
||||
<Units Count="7">
|
||||
<Units Count="8">
|
||||
<Unit0>
|
||||
<Filename Value="spreadtestgui.lpr"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
@ -111,6 +115,11 @@
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="internaltests"/>
|
||||
</Unit6>
|
||||
<Unit7>
|
||||
<Filename Value="formattests.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="formattests"/>
|
||||
</Unit7>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
@ -140,7 +149,7 @@
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
<Debugging>
|
||||
<Exceptions Count="3">
|
||||
<Exceptions Count="4">
|
||||
<Item1>
|
||||
<Name Value="EAbort"/>
|
||||
</Item1>
|
||||
@ -150,6 +159,9 @@
|
||||
<Item3>
|
||||
<Name Value="EFOpenError"/>
|
||||
</Item3>
|
||||
<Item4>
|
||||
<Name Value="EAssertionFailedError"/>
|
||||
</Item4>
|
||||
</Exceptions>
|
||||
</Debugging>
|
||||
</CONFIG>
|
||||
|
@ -5,7 +5,7 @@ program spreadtestgui;
|
||||
uses
|
||||
Interfaces, Forms, GuiTestRunner,
|
||||
datetests, stringtests,
|
||||
numberstests, manualtests, testsutility, internaltests;
|
||||
numberstests, manualtests, testsutility, internaltests, formattests;
|
||||
|
||||
begin
|
||||
Application.Initialize;
|
||||
|
@ -22,7 +22,7 @@ uses
|
||||
|
||||
var
|
||||
// Norm to test against - list of strings that should occur in spreadsheet
|
||||
SollStrings: array[0..6] of string; //"Soll" is a German word in Dutch accountancy circles meaning "normative value to check against". There ;)
|
||||
SollStrings: array[0..12] of string; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;)
|
||||
// Initializes Soll*/normative variables.
|
||||
// Useful in test setup procedures to make sure the norm is correct.
|
||||
procedure InitSollStrings;
|
||||
@ -48,6 +48,12 @@ type
|
||||
procedure TestReadString4;
|
||||
procedure TestReadString5;
|
||||
procedure TestReadString6;
|
||||
procedure TestReadString7;
|
||||
procedure TestReadString8;
|
||||
procedure TestReadString9;
|
||||
procedure TestReadString10;
|
||||
procedure TestReadString11;
|
||||
procedure TestReadString12;
|
||||
end;
|
||||
|
||||
{ TSpreadWriteReadStringTests }
|
||||
@ -84,6 +90,12 @@ begin
|
||||
SollStrings[4]:='café au lait'; //accent aigue on the e
|
||||
SollStrings[5]:='водка'; //cyrillic
|
||||
SollStrings[6]:='wódka'; //Polish o accent aigue
|
||||
SollStrings[7]:='35%'; // 0.3536 formatted as percentage, no decimals
|
||||
SollStrings[8]:=FormatFloat('0.00', 35.36)+'%'; // 0.3536 formatted as percentage, 2 decimals
|
||||
SollStrings[9]:=FormatFloat('#,##0', 59000000.1234); // 59 million + 0.1234 formatted with thousand separator, no decimals
|
||||
SollStrings[10]:=FormatFloat('#,##0.00', 59000000.1234); // 59 million + 0.1234 formatted with thousand separator, 2 decimals
|
||||
SollStrings[11]:=SciFloat(-59000000.1234, 1); // minus 59 million + 0.1234, formatted as "scientific" with 1 decimal
|
||||
SollStrings[12]:=FormatFloat('0.00E+00', -59000000.1234); // minus 59 million + 0.1234, formatted as "exp" with 2 decimals
|
||||
end;
|
||||
|
||||
{ TSpreadWriteReadStringTests }
|
||||
@ -318,6 +330,37 @@ begin
|
||||
TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,6);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadStringTests.TestReadString7;
|
||||
begin
|
||||
TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,7);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadStringTests.TestReadString8;
|
||||
begin
|
||||
TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,8);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadStringTests.TestReadString9;
|
||||
begin
|
||||
TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,9);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadStringTests.TestReadString10;
|
||||
begin
|
||||
TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,10);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadStringTests.TestReadString11;
|
||||
begin
|
||||
TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,11);
|
||||
end;
|
||||
|
||||
procedure TSpreadReadStringTests.TestReadString12;
|
||||
begin
|
||||
TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,12);
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
// Register so these tests are included in a full run
|
||||
RegisterTest(TSpreadReadStringTests);
|
||||
|
Binary file not shown.
Binary file not shown.
@ -70,6 +70,7 @@ type
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override;
|
||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override;
|
||||
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@ -407,6 +408,21 @@ begin
|
||||
AStream.WriteBuffer(AValue, 8);
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF2Writer.WriteDateTime ()
|
||||
*
|
||||
* DESCRIPTION: Writes a date/time value as a text
|
||||
*
|
||||
* No further formatting applied.
|
||||
*
|
||||
*******************************************************************}
|
||||
procedure TsSpreadBIFF2Writer.WriteDateTime(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
|
||||
begin
|
||||
WriteLabel(AStream, ARow, ACol, FormatDateTime('c', AValue), ACell);
|
||||
end;
|
||||
|
||||
|
||||
{ TsSpreadBIFF2Reader }
|
||||
|
||||
procedure TsSpreadBIFF2Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
|
||||
|
@ -118,6 +118,7 @@ type
|
||||
procedure WriteBOF(AStream: TStream; ADataType: Word);
|
||||
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
|
||||
//procedure WriteCodepage(AStream: TStream; AEncoding: TsEncoding); this is in xlscommon
|
||||
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override;
|
||||
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
|
||||
procedure WriteEOF(AStream: TStream);
|
||||
procedure WriteFont(AStream: TStream; AFont: TFPCustomFont);
|
||||
@ -875,6 +876,20 @@ begin
|
||||
AStream.WriteBuffer(AValue, 8);
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF5Writer.WriteDateTime ()
|
||||
*
|
||||
* DESCRIPTION: Writes a date/time value as a string
|
||||
*
|
||||
* No further formatting of the date
|
||||
*
|
||||
*******************************************************************}
|
||||
procedure TsSpreadBIFF5Writer.WriteDateTime(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
|
||||
begin
|
||||
WriteLabel(AStream, ARow, ACol, FormatDateTime('c', AValue), ACell);
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF5Writer.WriteStyle ()
|
||||
*
|
||||
|
@ -87,10 +87,12 @@ type
|
||||
FXFList: TFPList; // of TXFRecordData
|
||||
FFormatList: TFPList; // of TFormatRecordData
|
||||
function DecodeRKValue(const ARK: DWORD): Double;
|
||||
procedure ExtractNumberFormat(AXFIndex: WORD;
|
||||
out ANumberFormat: TsNumberFormat; out ADecimals: Word;
|
||||
out ANumberFormatStr: String);
|
||||
// Tries to find if a number cell is actually a date/datetime/time cell
|
||||
// and retrieve the value
|
||||
function IsDate(Number: Double; ARow: WORD;
|
||||
ACol: WORD; AXFIndex: WORD; var ADateTime: TDateTime): boolean;
|
||||
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat; var ADateTime: TDateTime): Boolean;
|
||||
function ReadWideString(const AStream: TStream; const ALength: WORD): WideString; overload;
|
||||
function ReadWideString(const AStream: TStream; const AUse8BitLength: Boolean): WideString; overload;
|
||||
procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook);
|
||||
@ -350,6 +352,7 @@ var
|
||||
lBorders: TsCellBorders;
|
||||
lAddBackground: Boolean;
|
||||
lBackgroundColor: TsColor;
|
||||
fmt: String;
|
||||
begin
|
||||
// The first 4 styles were already added
|
||||
for i := 4 to Length(FFormattingStyles) - 1 do
|
||||
@ -364,13 +367,63 @@ begin
|
||||
|
||||
// Now apply the modifications.
|
||||
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then
|
||||
begin
|
||||
case FFormattingStyles[i].NumberFormat of
|
||||
nfGeneral: lFormatIndex := FORMAT_GENERAL;
|
||||
nfShortDate: lFormatIndex := FORMAT_SHORT_DATE;
|
||||
nfShortDateTime: lFormatIndex := FORMAT_SHORT_DATETIME;
|
||||
nfFixed:
|
||||
case FFormattingStyles[i].NumberDecimals of
|
||||
0: lFormatIndex := FORMAT_FIXED_0_DECIMALS;
|
||||
2: lFormatIndex := FORMAT_FIXED_2_DECIMALS;
|
||||
end;
|
||||
nfFixedTh:
|
||||
case FFormattingStyles[i].NumberDecimals of
|
||||
0: lFormatIndex := FORMAT_FIXED_THOUSANDS_0_DECIMALS;
|
||||
2: lFormatIndex := FORMAT_FIXED_THOUSANDS_2_DECIMALS;
|
||||
end;
|
||||
nfExp:
|
||||
lFormatIndex := FORMAT_EXP_2_DECIMALS;
|
||||
nfSci:
|
||||
lFormatIndex := FORMAT_SCI_1_DECIMAL;
|
||||
nfPercentage:
|
||||
case FFormattingStyles[i].NumberDecimals of
|
||||
0: lFormatIndex := FORMAT_PERCENT_0_DECIMALS;
|
||||
2: lFormatIndex := FORMAT_PERCENT_2_DECIMALS;
|
||||
end;
|
||||
{
|
||||
nfCurrency:
|
||||
case FFormattingStyles[i].NumberDecimals of
|
||||
0: lFormatIndex := FORMAT_CURRENCY_0_DECIMALS;
|
||||
2: lFormatIndex := FORMAT_CURRENCY_2_DECIMALS;
|
||||
end;
|
||||
}
|
||||
nfShortDate:
|
||||
lFormatIndex := FORMAT_SHORT_DATE;
|
||||
nfShortTime:
|
||||
lFormatIndex := FORMAT_SHORT_TIME;
|
||||
nfLongTime:
|
||||
lFormatIndex := FORMAT_LONG_TIME;
|
||||
nfShortTimeAM:
|
||||
lFormatIndex := FORMAT_SHORT_TIME_AM;
|
||||
nfLongTimeAM:
|
||||
lFormatIndex := FORMAT_LONG_TIME_AM;
|
||||
nfShortDateTime:
|
||||
lFormatIndex := FORMAT_SHORT_DATETIME;
|
||||
nfFmtDateTime:
|
||||
begin
|
||||
fmt := lowercase(FFormattingStyles[i].NumberFormatStr);
|
||||
if (fmt = 'dm') or (fmt = 'd-mmm') or (fmt = 'd mmm') or (fmt = 'd. mmm') or (fmt = 'd/mmm') then
|
||||
lFormatIndex := FORMAT_DATE_DM
|
||||
else
|
||||
if (fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy') then
|
||||
lFormatIndex := FORMAT_DATE_MY
|
||||
else
|
||||
if (fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss') then
|
||||
lFormatIndex := FORMAT_TIME_MS
|
||||
else
|
||||
if (fmt = 'msz') or (fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or (fmt = 'mm:ss.0') or (fmt = 'mm:ss.z') or (fmt = 'nn:ss.z') then
|
||||
lFormatIndex := FORMAT_TIME_MSZ
|
||||
end;
|
||||
nfTimeInterval:
|
||||
lFormatIndex := FORMAT_TIME_INTERVAL;
|
||||
end;
|
||||
end;
|
||||
|
||||
if uffBorder in FFormattingStyles[i].UsedFormattingFields then
|
||||
lBorders := FFormattingStyles[i].Border;
|
||||
@ -1451,47 +1504,127 @@ begin
|
||||
Result:=Number;
|
||||
end;
|
||||
|
||||
function TsSpreadBIFF8Reader.IsDate(Number: Double;
|
||||
ARow: WORD; ACol: WORD; AXFIndex: WORD; var ADateTime: TDateTime): boolean;
|
||||
// Try to find out if a cell has a date/time and return
|
||||
// TheDate if it is
|
||||
procedure TsSpreadBIFF8Reader.ExtractNumberFormat(AXFIndex: WORD;
|
||||
out ANumberFormat: TsNumberFormat; out ADecimals: Word;
|
||||
out ANumberFormatStr: String);
|
||||
const
|
||||
{ see ➜ 5.49 }
|
||||
NOT_USED = nfGeneral;
|
||||
fmts: array[1..58] of TsNumberFormat = (
|
||||
nfFixed, nfFixed, nfFixedTh, nfFixedTh, nfFixedTh, // 1..5
|
||||
nfFixedTh, nfFixedTh, nfFixedTh, nfPercentage, nfPercentage, // 6..10
|
||||
nfExp, NOT_USED, NOT_USED, nfShortDate, nfShortDate, // 11..15
|
||||
nfFmtDateTime, nfFmtDateTime, nfShortTimeAM, nfLongTimeAM, nfShortTime, // 16..20
|
||||
nfLongTime, nfShortDateTime, NOT_USED, NOT_USED, NOT_USED, // 21..25
|
||||
NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 26..30
|
||||
NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 31..35
|
||||
NOT_USED, nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, // 36..40
|
||||
nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, nfFmtDateTime, // 41..45
|
||||
nfTimeInterval, nfFmtDateTime, nfSci, NOT_USED, NOT_USED, // 46..50
|
||||
NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 51..55
|
||||
NOT_USED, NOT_USED, NOT_USED // 56..58
|
||||
);
|
||||
decs: array[1..58] of word = (
|
||||
0, 2, 0, 2, 0, 0, 2, 2, 0, 2, // 1..10
|
||||
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 11..20
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21..30
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 2, 2, // 31..40
|
||||
0, 0, 2, 2, 0, 3, 0, 1, 0, 0, // 41..50 #48 is "scientific", use "exponential" instead
|
||||
0, 0, 0, 0, 0, 0, 0, 0); // 51..58
|
||||
var
|
||||
lFormatData: TFormatRecordData;
|
||||
lXFData: TXFRecordData;
|
||||
isAMPM: Boolean;
|
||||
isLongTime: Boolean;
|
||||
isMilliSec: Boolean;
|
||||
t,d: Boolean;
|
||||
begin
|
||||
result := false;
|
||||
// Try to figure out if the number is really a number of a date or time value
|
||||
// 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
|
||||
// Note FindFormatRecordForCell will not retrieve default format numbers
|
||||
ANumberFormat := nfGeneral;
|
||||
ANumberFormatStr := '';
|
||||
ADecimals := 0;
|
||||
|
||||
lFormatData := FindFormatRecordForCell(AXFIndex);
|
||||
{Record FORMAT, BIFF8 (5.49):
|
||||
Offset Size Contents
|
||||
0 2 Format index used in other records
|
||||
}
|
||||
|
||||
if lFormatData=nil then
|
||||
begin
|
||||
// No custom format, so first test for default formats
|
||||
lXFData := TXFRecordData(FXFList.Items[AXFIndex]);
|
||||
if (lXFData.FormatIndex in [14..22, 27..36, 45, 46, 47, 50..58]) then
|
||||
begin
|
||||
ADateTime := ConvertExcelDateTimeToDateTime(Number, FDateMode);
|
||||
Exit(true);
|
||||
if lFormatData = nil then begin
|
||||
// no custom format, so first test for default formats
|
||||
lXFData := TXFRecordData (FXFList.Items[AXFIndex]);
|
||||
case lXFData.FormatIndex of
|
||||
FORMAT_DATE_DM:
|
||||
begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'DM'; end;
|
||||
FORMAT_DATE_MY:
|
||||
begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'MY'; end;
|
||||
FORMAT_TIME_MS:
|
||||
begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'MS'; end;
|
||||
FORMAT_TIME_MSZ:
|
||||
begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'MSZ'; end;
|
||||
else
|
||||
if (lXFData.FormatIndex > 0) and (lXFData.FormatIndex <= 58) then begin
|
||||
ANumberFormat := fmts[lXFData.FormatIndex];
|
||||
ADecimals := decs[lXFData.FormatIndex];
|
||||
end;
|
||||
end;
|
||||
end
|
||||
end else
|
||||
// Check custom formats if they have / in format string (this can fail for
|
||||
// custom text formats)
|
||||
if IsPercentNumberFormat(lFormatData.FormatString, ADecimals) then
|
||||
ANumberFormat := nfPercentage
|
||||
else
|
||||
begin
|
||||
// Check custom formats if they
|
||||
// have / in format string (this can fail for custom text formats)
|
||||
if (Pos('/', lFormatData.FormatString) > 0) then
|
||||
begin
|
||||
ADateTime := ConvertExcelDateTimeToDateTime(Number, FDateMode);
|
||||
Exit(true);
|
||||
if IsExpNumberFormat(lFormatData.Formatstring, ADecimals) then
|
||||
ANumberFormat := nfExp
|
||||
else
|
||||
if IsThousandSepNumberFormat(lFormatData.FormatString, ADecimals) then
|
||||
ANumberFormat := nfFixedTh
|
||||
else
|
||||
if IsFixedNumberFormat(lFormatData.FormatString, ADecimals) then
|
||||
ANumberFormat := nfFixed
|
||||
else begin
|
||||
t := IsTimeFormat(lFormatData.FormatString, isLongTime, isAMPM, isMilliSec);
|
||||
d := IsDateFormat(lFormatData.FormatString);
|
||||
if d and t then
|
||||
ANumberFormat := nfShortDateTime
|
||||
else
|
||||
if d then
|
||||
ANumberFormat := nfShortDate
|
||||
else
|
||||
if t then begin
|
||||
if isAMPM then begin
|
||||
if isLongTime then
|
||||
ANumberFormat := nfLongTimeAM
|
||||
else
|
||||
ANumberFormat := nfShortTimeAM;
|
||||
end else begin
|
||||
if isLongTime then
|
||||
ANumberFormat := nfLongTime
|
||||
else
|
||||
ANumberFormat := nfShortTime;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
ADateTime := 0;
|
||||
end;
|
||||
|
||||
function TsSpreadBIFF8Reader.IsDateTime(Number: Double;
|
||||
ANumberFormat: TsNumberFormat; var ADateTime: TDateTime): boolean;
|
||||
// Convert the number to a date/time and return that if it is
|
||||
begin
|
||||
if ANumberFormat in [
|
||||
nfShortDateTime, nfFmtDateTime, nfShortDate,
|
||||
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM] then
|
||||
begin
|
||||
ADateTime := ConvertExcelDateTimeToDateTime(Number, FDateMode);
|
||||
Result := true;
|
||||
end else
|
||||
if ANumberFormat = nfTimeInterval then begin
|
||||
ADateTime := Number;
|
||||
Result := true;
|
||||
end else
|
||||
begin
|
||||
ADateTime := 0;
|
||||
Result := false;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TsSpreadBIFF8Reader.ReadWideString(const AStream: TStream;
|
||||
@ -1718,6 +1851,9 @@ var
|
||||
ARow, ACol, XF: WORD;
|
||||
lDateTime: TDateTime;
|
||||
Number: Double;
|
||||
nf: TsNumberFormat; // Number format
|
||||
nd: word; // decimals
|
||||
nfs: String; // Number format string
|
||||
begin
|
||||
{Retrieve XF record, row and column}
|
||||
ReadRowColXF(AStream,ARow,ACol,XF);
|
||||
@ -1729,10 +1865,11 @@ begin
|
||||
Number:=DecodeRKValue(RK);
|
||||
|
||||
{Find out what cell type, set contenttype and value}
|
||||
if IsDate(Number, ARow, ACol, XF, lDateTime) then
|
||||
FWorksheet.WriteDateTime(ARow, ACol, lDateTime)
|
||||
ExtractNumberFormat(XF, nf, nd, nfs);
|
||||
if IsDateTime(Number, nf, lDateTime) then
|
||||
FWorksheet.WriteDateTime(ARow, ACol, lDateTime, nf, nfs)
|
||||
else
|
||||
FWorksheet.WriteNumber(ARow,ACol,Number);
|
||||
FWorksheet.WriteNumber(ARow, ACol, Number, nf);
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF8Reader.ReadMulRKValues(const AStream: TStream);
|
||||
@ -1742,6 +1879,9 @@ var
|
||||
Pending: integer;
|
||||
RK: DWORD;
|
||||
Number: Double;
|
||||
nf: TsNumberFormat;
|
||||
nd: word;
|
||||
nfs: String;
|
||||
begin
|
||||
ARow:=WordLEtoN(AStream.ReadWord);
|
||||
fc:=WordLEtoN(AStream.ReadWord);
|
||||
@ -1751,10 +1891,11 @@ begin
|
||||
RK:=DWordLEtoN(AStream.ReadDWord);
|
||||
Number:=DecodeRKValue(RK);
|
||||
{Find out what cell type, set contenttype and value}
|
||||
if IsDate(Number, ARow, fc, XF, lDateTime) then
|
||||
FWorksheet.WriteDateTime(ARow, fc, lDateTime)
|
||||
ExtractNumberFormat(XF, nf, nd, nfs);
|
||||
if IsDateTime(Number, nf, lDateTime) then
|
||||
FWorksheet.WriteDateTime(ARow, fc, lDateTime, nf, nfs)
|
||||
else
|
||||
FWorksheet.WriteNumber(ARow,fc,Number);
|
||||
FWorksheet.WriteNumber(ARow, fc, Number, nf, nd);
|
||||
inc(fc);
|
||||
dec(Pending,(sizeof(XF)+sizeof(RK)));
|
||||
end;
|
||||
@ -1941,6 +2082,9 @@ var
|
||||
ARow, ACol, XF: Word;
|
||||
AValue: Double;
|
||||
lDateTime: TDateTime;
|
||||
nf: TsNumberFormat;
|
||||
nd: word;
|
||||
nfs: String;
|
||||
begin
|
||||
{Retrieve XF record, row and column}
|
||||
ReadRowColXF(AStream,ARow,ACol,XF);
|
||||
@ -1949,10 +2093,11 @@ begin
|
||||
AStream.ReadBuffer(AValue, 8);
|
||||
|
||||
{Find out what cell type, set contenttype and value}
|
||||
if IsDate(AValue, ARow, ACol, XF, lDateTime) then
|
||||
FWorksheet.WriteDateTime(ARow, ACol, lDateTime)
|
||||
ExtractNumberFormat(XF, nf, nd, nfs);
|
||||
if IsDateTime(AValue, nf, lDateTime) then
|
||||
FWorksheet.WriteDateTime(ARow, ACol, lDateTime, nf, nfs)
|
||||
else
|
||||
FWorksheet.WriteNumber(ARow,ACol,AValue);
|
||||
FWorksheet.WriteNumber(ARow,ACol,AValue,nf,nd);
|
||||
end;
|
||||
|
||||
procedure TsSpreadBIFF8Reader.ReadRichString(const AStream: TStream);
|
||||
|
@ -137,10 +137,30 @@ const
|
||||
DATEMODE_1904_BASE=1462; //1/1/1904 in FPC TDateTime
|
||||
|
||||
{ FORMAT record constants }
|
||||
// Just a subset; needed for output to date/time records
|
||||
FORMAT_GENERAL = 0; //general/default format
|
||||
FORMAT_SHORT_DATE = 14; //short date
|
||||
FORMAT_SHORT_DATETIME = 22; //short date+time
|
||||
// Subset of the built-in formats for US Excel,
|
||||
// including those needed for date/time output
|
||||
FORMAT_GENERAL = 0; //general/default format
|
||||
FORMAT_FIXED_0_DECIMALS = 1; //fixed, 0 decimals
|
||||
FORMAT_FIXED_2_DECIMALS = 2; //fixed, 2 decimals
|
||||
FORMAT_FIXED_THOUSANDS_0_DECIMALS = 3; //fixed, w/ thousand separator, 0 decs
|
||||
FORMAT_FIXED_THOUSANDS_2_DECIMALS = 4; //fixed, w/ thousand separator, 2 decs
|
||||
FORMAT_CURRENCY_0_DECIMALS = 5; //currency (with currency symbol), 0 decs
|
||||
FORMAT_CURRENCY_2_DECIMALS = 7; //currency (with currency symbol), 2 decs
|
||||
FORMAT_PERCENT_0_DECIMALS = 9; //percent, 0 decimals
|
||||
FORMAT_PERCENT_2_DECIMALS = 10; //percent, 2 decimals
|
||||
FORMAT_EXP_2_DECIMALS = 11; //exponent, 2 decimals
|
||||
FORMAT_SCI_1_DECIMAL = 48; //scientific, 1 decimal
|
||||
FORMAT_SHORT_DATE = 14; //short date
|
||||
FORMAT_DATE_DM = 16; //date D-MMM
|
||||
FORMAT_DATE_MY = 17; //date MMM-YYYY
|
||||
FORMAT_SHORT_TIME_AM = 18; //short time H:MM with AM
|
||||
FORMAT_LONG_TIME_AM = 19; //long time H:MM:SS with AM
|
||||
FORMAT_SHORT_TIME = 20; //short time H:MM
|
||||
FORMAT_LONG_TIME = 21; //long time H:MM:SS
|
||||
FORMAT_SHORT_DATETIME = 22; //short date+time
|
||||
FORMAT_TIME_MS = 45; //time MM:SS
|
||||
FORMAT_TIME_MSZ = 47; //time MM:SS.0
|
||||
FORMAT_TIME_INTERVAL = 46; //time [hh]:mm:ss, hh can be >24
|
||||
|
||||
|
||||
type
|
||||
@ -191,6 +211,15 @@ type
|
||||
constructor Create; override;
|
||||
end;
|
||||
|
||||
function IsExpNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
function IsFixedNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
function IsPercentNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
function IsThousandSepNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
|
||||
function IsDateFormat(s: String): Boolean;
|
||||
function IsTimeFormat(s: String; out isLong, isAMPM, isMillisec: Boolean): Boolean;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
function ConvertExcelDateTimeToDateTime(
|
||||
@ -467,5 +496,201 @@ begin
|
||||
FDateMode := dm1900;
|
||||
end;
|
||||
|
||||
|
||||
{ Format checking procedures }
|
||||
|
||||
{ This simple parsing procedure of the Excel format string checks for a fixed
|
||||
float format s, i.e. s can be '0', '0.00', '000', '0,000', and returns the
|
||||
number of decimals, i.e. number of zeros behind the decimal point }
|
||||
function IsFixedNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
p: Integer;
|
||||
decs: String;
|
||||
begin
|
||||
Decimals := 0;
|
||||
|
||||
// Check if s is a valid format mask.
|
||||
try
|
||||
FormatFloat(s, 1.0);
|
||||
except
|
||||
on EConvertError do begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
// If it is count the zeros - each one is a decimal.
|
||||
if s = '0' then
|
||||
Result := true
|
||||
else begin
|
||||
p := pos('.', s); // position of decimal point;
|
||||
if p = 0 then begin
|
||||
Result := false;
|
||||
end else begin
|
||||
Result := true;
|
||||
for i:= p+1 to Length(s) do
|
||||
if s[i] = '0' then begin
|
||||
inc(Decimals)
|
||||
end
|
||||
else
|
||||
exit; // ignore characters after the last 0
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ This function checks whether the format string corresponds to a thousand
|
||||
separator format like "#,##0.000' and returns the number of fixed decimals
|
||||
(i.e. zeros after the decimal point) }
|
||||
function IsThousandSepNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
var
|
||||
i, p: Integer;
|
||||
begin
|
||||
Decimals := 0;
|
||||
|
||||
// Check if s is a valid format string
|
||||
try
|
||||
FormatFloat(s, 1.0);
|
||||
except
|
||||
on EConvertError do begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
// If it is look for the thousand separator. If found count decimals.
|
||||
Result := (Pos(',', s) > 0);
|
||||
if Result then begin
|
||||
p := pos('.', s);
|
||||
if p > 0 then
|
||||
for i := p+1 to Length(s) do
|
||||
if s[i] = '0' then
|
||||
inc(Decimals)
|
||||
else
|
||||
exit; // ignore format characters after the last 0
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ This function checks whether the format string corresponds to percent
|
||||
formatting and determines the number of decimals }
|
||||
function IsPercentNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
var
|
||||
i, p: Integer;
|
||||
begin
|
||||
Decimals := 0;
|
||||
// The signature of the percent format is a percent sign at the end of the
|
||||
// format string.
|
||||
Result := (s <> '') and (s[Length(s)] = '%');
|
||||
if Result then begin
|
||||
// Check for a valid format string
|
||||
Delete(s, Length(s), 1);
|
||||
try
|
||||
FormatDateTime(s, 1.0);
|
||||
except
|
||||
on EConvertError do begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
// Count decimals
|
||||
p := pos('.', s);
|
||||
if p > 0 then
|
||||
for i := p+1 to Length(s)-1 do
|
||||
if s[i] = '0' then
|
||||
inc(Decimals)
|
||||
else
|
||||
exit; // ignore characters after last 0
|
||||
end;
|
||||
end;
|
||||
|
||||
{ This function checks whether the format string corresponds to exponential
|
||||
formatting and determines the number decimals }
|
||||
function IsExpNumberFormat(s: String; out Decimals: Word): Boolean;
|
||||
var
|
||||
i, p, pe: Integer;
|
||||
begin
|
||||
Decimals := 0;
|
||||
|
||||
// Check for a valid format string
|
||||
try
|
||||
FormatDateTime(s, 1.0);
|
||||
except
|
||||
on EConvertError do begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Count decimals
|
||||
pe := pos('e', lowercase(s));
|
||||
result := pe > 0;
|
||||
if Result then begin
|
||||
p := pos('.', s);
|
||||
if (p > 0) then begin
|
||||
if p < pe then
|
||||
for i:=1 to pe-1 do
|
||||
if s[i] = '0' then
|
||||
inc(Decimals)
|
||||
else
|
||||
exit; // ignore characters after last 0
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ IsDateFormat checks if the format string s corresponds to a date format }
|
||||
function IsDateFormat(s: String): Boolean;
|
||||
begin
|
||||
// Day, month, year are separated by a slash
|
||||
Result := (pos('/', s) > 0);
|
||||
if Result then
|
||||
// Check validity of format string
|
||||
try
|
||||
FormatDateTime(s, now);
|
||||
except on EConvertError do
|
||||
Result := false;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ IsTimeFormat checks if the format string s is a time format. isLong is
|
||||
true if the string contains hours, minutes and seconds (two colons).
|
||||
isAMPM is true if the string contains "AM/PM", "A/P" or "AMPM".
|
||||
isMilliSec is true if the string ends with a "z". }
|
||||
function IsTimeFormat(s: String; out isLong, isAMPM, isMillisec: Boolean): Boolean;
|
||||
var
|
||||
p, i, count: Integer;
|
||||
begin
|
||||
// Time parts are separated by a colon
|
||||
p := pos(':', s);
|
||||
isLong := false;
|
||||
isAMPM := false;
|
||||
result := p > 0;
|
||||
|
||||
if Result then begin
|
||||
count := 1;
|
||||
s := Uppercase(s);
|
||||
|
||||
// If there are is a second colon s is a "long" time format
|
||||
for i:=p+1 to Length(s) do
|
||||
if s[i] = ':' then begin
|
||||
isLong := true;
|
||||
break;
|
||||
end;
|
||||
|
||||
// Seek for "AM/PM" etc to detect that specific format
|
||||
isAMPM := (pos('AM/PM', s) > 0) or (pos('A/P', s) > 0) or (pos('AMPM', s) > 0);
|
||||
|
||||
// Look for the "milliseconds" character z
|
||||
isMilliSec := (s[Length(s)] = 'Z');
|
||||
|
||||
// Check validity of format string
|
||||
try
|
||||
FormatDateTime(s, now);
|
||||
except on EConvertError do
|
||||
Result := false;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -75,6 +75,7 @@ type
|
||||
//todo: add WriteDate
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override;
|
||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override;
|
||||
procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@ -517,6 +518,16 @@ begin
|
||||
Format(' <c r="%s" s="0" t="n"><v>%s</v></c>', [CellPosText, CellValueText]) + LineEnding;
|
||||
end;
|
||||
|
||||
{
|
||||
Writes the date/time as a text to the sheet.
|
||||
No further formatting applied.
|
||||
}
|
||||
procedure TsSpreadOOXMLWriter.WriteDateTime(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
|
||||
begin
|
||||
WriteLabel(AStream, ARow, ACol, FormatDateTime('c', AValue), ACell);
|
||||
end;
|
||||
|
||||
{
|
||||
Registers this reader / writer on fpSpreadsheet
|
||||
}
|
||||
|
Reference in New Issue
Block a user