fpspreadsheet: Fix some date/time format display issues for ods.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3202 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2014-06-19 17:56:29 +00:00
parent 0650a4a5b4
commit 888f605851
4 changed files with 134 additions and 54 deletions

View File

@ -19,6 +19,7 @@ var
MyDir: string;
number1, number2, number3, number4,
number5, number6, number7, number8: Double;
dt1, dt2: TDateTime;
row: Integer = 7;
begin
MyDir := ExtractFilePath(ParamStr(0));
@ -31,6 +32,9 @@ begin
number7 := 1/number3;
number8 := -1/number3;
dt1 := EncodeDate(2012, 1, 1) + EncodeTime(9, 1, 2, 12);
dt2 := EncodeDate(2012, 12, 1) + EncodeTime(21, 1, 2, 12);
// Create the spreadsheet
MyWorkbook := TsWorkbook.Create;
MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet');
@ -230,6 +234,65 @@ begin
MyWorksheet.WriteCurrency(row, 6, number6, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV);
MyWorksheet.WriteCurrency(row, 7, number7, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV);
MyWorksheet.WriteCurrency(row, 8, number8, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV);
inc(row,2);
MyWorksheet.WriteUTF8Text(row, 0, 'Some date/time values in various formats:');
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfShortDateTime');
MyWorksheet.WriteDateTime(row, 1, dt1, nfShortDateTime);
MyWorksheet.WriteDateTime(row, 2, dt2, nfShortDateTime);
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfShortDate');
MyWorksheet.WriteDateTime(row, 1, dt1, nfShortDate);
MyWorksheet.WriteDateTime(row, 2, dt2, nfShortDate);
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfLongDate');
MyWorksheet.WriteDateTime(row, 1, dt1, nfLongDate);
MyWorksheet.WriteDateTime(row, 2, dt2, nfLongDate);
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfShortTime');
MyWorksheet.WriteDateTime(row, 1, dt1, nfShortTime);
MyWorksheet.WriteDateTime(row, 2, dt2, nfShortTime);
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfLongTime');
MyWorksheet.WriteDateTime(row, 1, dt1, nfLongTime);
MyWorksheet.WriteDateTime(row, 2, dt2, nfLongTime);
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfShortTimeAM');
MyWorksheet.WriteDateTime(row, 1, dt1, nfShortTimeAM);
MyWorksheet.WriteDateTime(row, 2, dt2, nfShortTimeAM);
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfLongTimeAM');
MyWorksheet.WriteDateTime(row, 1, dt1, nfLongTimeAM);
MyWorksheet.WriteDateTime(row, 2, dt2, nfLongTimeAM);
inc(row);
// In order to use a semicolon as a date-time separator it must be escaped either by
// using the backslash or quotes (because the semicolon is the separator between sections)
MyWorksheet.WriteUTF8Text(row, 0, 'nfCustom, dddd, dd/mm/yyyy\; hh:nn');
MyWorksheet.WriteDateTime(row, 1, dt1, nfCustom, 'dddd, dd/mm/yyyy\; hh:nn');
MyWorksheet.WriteDateTime(row, 2, dt2, nfCustom, 'dddd, dd/mm/yyyy\; hh:nn');
MyWorksheet.WriteUTF8Text(row, 3, 'The semicolon must be escaped otherwise it would be misunderstood as a section separator.');
MyWorksheet.WriteUTF8Text(row, 4, 'This format is not displayed correctly by Open/LibreOffice.');
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfCustom, dddd, dd/mm/yyyy"; "hh:nn');
MyWorksheet.WriteDateTime(row, 1, dt1, nfCustom, 'dddd, dd/mm/yyyy"; "hh:nn');
MyWorksheet.WriteDateTime(row, 2, dt2, nfCustom, 'dddd, dd/mm/yyyy"; "hh:nn');
MyWorksheet.WriteUTF8Text(row, 3, 'The semicolon must be escaped otherwise it would be misunderstood as a section separator.');
MyWorksheet.WriteUTF8Text(row, 4, 'This format is not displayed correctly by Open/LibreOffice.');
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfCustom, dd/mmm');
MyWorksheet.WriteDateTime(row, 1, dt1, nfCustom, 'dd/mmm');
MyWorksheet.WriteDateTime(row, 2, dt2, nfCustom, 'dd/mmm');
MyWorksheet.WriteUTF8Text(row, 3, 'The slash is replaced by the date or time separator of the FormatSettings');
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfCustom, mmm/yy');
MyWorksheet.WriteDateTime(row, 1, dt1, nfCustom, 'mmm/yy');
MyWorksheet.WriteDateTime(row, 2, dt2, nfCustom, 'mmm/yy');
MyWorksheet.WriteUTF8Text(row, 3, 'The slash is replaced by the date or time separator of the FormatSettings');
inc(row);
MyWorksheet.WriteUTF8Text(row, 0, 'nfCustom, mmm-yy');
MyWorksheet.WriteDateTime(row, 1, dt1, nfCustom, 'mmm-yy');
MyWorksheet.WriteDateTime(row, 2, dt2, nfCustom, 'mmm-yy');
MyWorksheet.WriteUTF8Text(row, 3, 'The dash is used literally');
// Creates a new worksheet
MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet 2');

View File

@ -275,12 +275,16 @@ function TsNumFormatParser.AnalyzeCurrency(const AValue: String): Boolean;
var
uValue: String;
begin
uValue := Uppercase(AValue);
Result := (uValue = Uppercase(AnsiToUTF8(FWorkbook.FormatSettings.CurrencyString))) or
(uValue = '$') or (uValue = 'USD') or
(uValue = '€') or (uValue = 'EUR') or
(uValue = '£') or (uValue = 'GBP') or
(uValue = '¥') or (uValue = 'JPY');
if (FWorkbook = nil) or (FWorkbook.FormatSettings.CurrencyString = '') then
Result := false
else begin
uValue := Uppercase(AValue);
Result := (uValue = Uppercase(AnsiToUTF8(FWorkbook.FormatSettings.CurrencyString))) or
(uValue = '$') or (uValue = 'USD') or
(uValue = '€') or (uValue = 'EUR') or
(uValue = '£') or (uValue = 'GBP') or
(uValue = '¥') or (uValue = 'JPY');
end;
end;
{ Creates a formatstring for all sections.
@ -1528,6 +1532,11 @@ begin
end;
'A', 'a':
ScanAMPM;
',', '-':
begin
Addelement(nftText, FToken);
FToken := NextToken;
end
else
// char pointer must be at end of date/time mask.
FToken := PrevToken;

View File

@ -343,9 +343,9 @@ begin
AIsTimeOnly := false;
case Elements[el].IntValue of
1: s := '';
2: s := 'number:style="long"';
3: s := 'number:textual="true"';
4: s := 'number:style="long" number:textual="true"';
2: s := 'number:style="long" ';
3: s := 'number:textual="true" ';
4: s := 'number:style="long" number:textual="true" ';
end;
Result := result + AIndent +
' <number:month ' + s + '/>' + LineEnding;
@ -357,11 +357,9 @@ begin
AIsTimeOnly := false;
case Elements[el].IntValue of
1: s := 'day ';
2: s := 'day number:style="long"';
3: s := 'day number:textual="true"';
4: s := 'day number:style="long" number:textual="true"';
5: s := 'day-of-week ';
6: s := 'day-of-week number:style="long"';
2: s := 'day number:style="long" ';
3: s := 'day-of-week ';
4: s := 'day-of-week number:style="long" ';
end;
Result := Result + AIndent +
' <number:' + s + '/>' + LineEnding;
@ -1581,57 +1579,47 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
end else
if nodeName = 'number:year' then begin
s := GetAttrValue(node, 'number:style');
if s = 'long' then fmt := fmt + 'yyyy'
else if s = '' then fmt := fmt + 'yy';
fmt := fmt + IfThen(s = 'long', 'yyyy', 'yy');
end else
if nodeName = 'number:month' then begin
s := GetAttrValue(node, 'number:style');
stxt := GetAttrValue(node, 'number:textual');
if (stxt = 'true') then begin // Month as text
if (s = 'long') then fmt := fmt + 'mmmm' else fmt := fmt + 'mmm';
end else begin // Month as number
if (s = 'long') then fmt := fmt + 'mm' else fmt := fmt + 'm';
end;
if (stxt = 'true') then // Month as text
fmt := fmt + IfThen(s = 'long', 'mmmm', 'mmm')
else // Month as number
fmt := fmt + IfThen(s = 'long', 'mm', 'm');
end else
if nodeName = 'number:day' then begin
s := GetAttrValue(node, 'number:style');
stxt := GetAttrValue(node, 'number:textual');
if (stxt = 'true') then begin // day as text
if (s = 'long') then fmt := fmt + 'dddd' else fmt := fmt + 'ddd';
end else begin // day as number
if (s = 'long') then fmt := fmt + 'dd' else fmt := fmt + 'd';
end;
end;
fmt := fmt + IfThen(s = 'long', 'dd', 'd');
end else
if nodeName = 'number:day-of-week' then begin
s := GetAttrValue(node, 'number:stye');
if (s = 'long') then fmt := fmt + 'dddddd' else fmt := fmt + 'ddddd';
s := GetAttrValue(node, 'number:style');
fmt := fmt + IfThen(s = 'long', 'dddd', 'ddd');
end else
if nodeName = 'number:hours' then begin
s := GetAttrValue(node, 'number:style');
if (sovr = 'false') then begin
if (s = 'long') then fmt := fmt + '[hh]' else fmt := fmt + '[h]';
end else begin
if (s = 'long') then fmt := fmt + 'hh' else fmt := fmt + 'h';
end;
if (sovr = 'false') then
fmt := fmt + IfThen(s = 'long', '[hh]', '[h]')
else
fmt := fmt + IfThen(s = 'long', 'hh', 'h');
sovr := '';
end else
if nodeName = 'number:minutes' then begin
s := GetAttrValue(node, 'number:style');
if (sovr = 'false') then begin
if (s = 'long') then fmt := fmt + '[nn]' else fmt := fmt + '[n]';
end else begin
if (s = 'long') then fmt := fmt + 'nn' else fmt := fmt + 'n';
end;
if (sovr = 'false') then
fmt := fmt + IfThen(s = 'long', '[nn]', '[n]')
else
fmt := fmt + IfThen(s = 'long', 'nn', 'n');
sovr := '';
end else
if nodeName = 'number:seconds' then begin
s := GetAttrValue(node, 'number:style');
if (sovr = 'false') then begin
if (s = 'long') then fmt := fmt + '[ss]' else fmt := fmt + '[s]';
end else begin
if (s = 'long') then fmt := fmt + 'ss' else fmt := fmt + 's';
sovr := '';
end;
if (sovr = 'false') then
fmt := fmt + IfThen(s = 'long', '[ss]', '[s]')
else
fmt := fmt + IfThen(s = 'long', 'ss', 's');
sovr := '';
s := GetAttrValue(node, 'number:decimal-places');
if (s <> '') and (s <> '0') then
fmt := fmt + '.' + DupeString('0', StrToInt(s));
@ -1641,8 +1629,14 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
else
if nodeName = 'number:text' then begin
childnode := node.FirstChild;
if childnode <> nil then
fmt := fmt + childnode.NodeValue;
if childnode <> nil then begin
s := childNode.NodeValue;
if pos(';', s) > 0 then
fmt := fmt + '"' + s + '"'
// avoid "misunderstanding" the semicolon as a section separator!
else
fmt := fmt + childnode.NodeValue;
end;
end;
node := node.NextSibling;
end;

View File

@ -1007,6 +1007,16 @@ end;
procedure SplitFormatString(const AFormatString: String; out APositivePart,
ANegativePart, AZeroPart: String);
procedure AddToken(AToken: Char; AWhere:Byte);
begin
case AWhere of
0: APositivePart := APositivePart + AToken;
1: ANegativePart := ANegativePart + AToken;
2: AZeroPart := AZeroPart + AToken;
end;
end;
var
P, PStart, PEnd: PChar;
token: Char;
@ -1025,24 +1035,28 @@ begin
while P < PEnd do begin
token := P^;
case token of
'"': begin // Skip quoted strings
'"': begin // Let quoted text intact
AddToken(token, where);
inc(P);
token := P^;
while (P < PEnd) and (token <> '"') do begin
AddToken(token, where);
inc(P);
token := P^;
end;
AddToken(token, where);
end;
';': begin // Separator between parts
inc(where);
if where = 3 then
exit;
end
else case where of
0: APositivePart := APositivePart + token;
1: ANegativePart := ANegativePart + token;
2: AZeroPart := AZeroPart + token;
end;
'\': begin // Skip "Escape" character and add next char immediately
inc(P);
token := P^;
AddToken(token, where);
end;
else AddToken(token, where);
end;
inc(P);
end;