fpspreadsheet: Add writing of date/time formats to ods files and fix writing of time values

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3199 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-06-18 21:54:29 +00:00
parent 26d5923673
commit 9faf94ee3a
3 changed files with 183 additions and 5 deletions

View File

@ -147,6 +147,7 @@ type
procedure ClearAll;
function GetDateTimeCode(ASection: Integer): String;
function IsDateTimeFormat: Boolean;
function IsTimeFormat: Boolean;
procedure LimitDecimals;
procedure Localize;
@ -1184,6 +1185,26 @@ begin
Result := false;
end;
{ Returns true if the format elements contain only time, no date tokens. }
function TsNumFormatParser.IsTimeFormat: Boolean;
var
section: Integer;
elem: Integer;
begin
Result := false;
for section := 0 to High(FSections) do
for elem := 0 to High(FSections[section].Elements) do
if FSections[section].Elements[elem].Token in [nftHour, nftMinute, nftSecond]
then begin
Result := true;
end else
if FSections[section].Elements[elem].Token in [nftYear, nftMonth, nftDay, nftExpChar, nftCurrSymbol]
then begin
Result := false;
exit;
end;
end;
function TsNumFormatParser.IsTokenAt(AToken: TsNumFormatToken;
ASection, AIndex: Integer): Boolean;
begin

View File

@ -58,6 +58,9 @@ type
{ TsSpreadOpenDocNumFormatParser }
TsSpreadOpenDocNumFormatParser = class(TsNumFormatParser)
private
function BuildDateTimeXMLAsString(ASection: Integer; AIndent: String;
out AIsTimeOnly: Boolean): String;
protected
function BuildXMLAsStringFromSection(ASection: Integer;
AIndent,AFormatName: String): String;
@ -312,6 +315,104 @@ end;
{ TsSpreadOpenDocNumFormatParser }
function TsSpreadOpenDocNumFormatParser.BuildDateTimeXMLAsString(ASection: Integer;
AIndent: String; out AIsTimeOnly: boolean): String;
var
el: Integer;
s: String;
prevToken: TsNumFormatToken;
begin
Result := '';
AIsTimeOnly := true;
with FSections[ASection] do begin
el := 0;
while el < Length(Elements) do begin
case Elements[el].Token of
nftYear:
begin
prevToken := Elements[el].Token;
AIsTimeOnly := false;
s := IfThen(Elements[el].IntValue > 2, 'number:style="long" ', '');
Result := Result + AIndent +
' <number:year ' + s + '/>' + LineEnding;
end;
nftMonth:
begin
prevToken := Elements[el].Token;
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"';
end;
Result := result + AIndent +
' <number:month ' + s + '/>' + LineEnding;
end;
nftDay:
begin
prevToken := Elements[el].Token;
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"';
end;
Result := Result + AIndent +
' <number:' + s + '/>' + LineEnding;
end;
nftHour, nftMinute, nftSecond:
begin
prevToken := Elements[el].Token;
case Elements[el].Token of
nftHour : s := 'hours ';
nftMinute: s := 'minutes ';
nftSecond: s := 'seconds ';
end;
s := s + IfThen(abs(Elements[el].IntValue) = 1, '', 'number:style="long" ');
if Elements[el].IntValue < 0 then
s := s + 'number:truncate-on-overflow="false" ';
Result := Result + AIndent +
' <number:' + s + '/>' + LineEnding;
end;
nftMilliseconds:
begin
// ???
end;
nftDateTimeSep, nftText, nftEscaped, nftSpace:
begin
if Elements[el].TextValue = ' ' then
s := '<![CDATA[ ]]>'
else begin
s := Elements[el].TextValue;
if (s = '/') then begin
if prevToken in [nftYear, nftMonth, nftDay] then
s := FWorkbook.FormatSettings.DateSeparator
else
s := FWorkbook.FormatSettings.TimeSeparator;
end;
end;
Result := Result + AIndent +
' <number:text>' + s + '</number:text>' + LineEnding;
end;
nftAMPM:
Result := Result + AIndent +
' <number:am-pm />' + LineEnding;
end;
inc(el);
end;
end;
end;
function TsSpreadOpenDocNumFormatParser.BuildXMLAsString(AIndent,
AFormatName: String): String;
var
@ -340,6 +441,7 @@ var
clr: TsColorvalue;
el: Integer;
s: String;
isTimeOnly: Boolean;
begin
Result := '';
@ -528,6 +630,23 @@ begin
end; // while
Result := Result + sStyleMap + AIndent + '</number:currency-style>' + LineEnding;
end;
// date/time
nftYear, nftMonth, nftDay, nftHour, nftMinute, nftSecond:
begin
s := BuildDateTimeXMLAsString(ASection, AIndent, isTimeOnly);
if isTimeOnly then
Result := Result + AIndent +
'<number:time-style style:name="' + AFormatName + '">' + LineEnding +
s + AIndent +
'</number:time-style>' + LineEnding
else
Result := Result + AIndent +
'<number:date-style style:name="' + AFormatName + '">' + LineEnding +
s + AIndent +
'</number:date-style>' + LineEnding;
exit;
end;
end;
inc(el);
end;
@ -3035,20 +3154,36 @@ end;
*******************************************************************}
procedure TsSpreadOpenDocWriter.WriteDateTime(AStream: TStream;
const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell);
const
FMT: array[boolean] of string = (ISO8601FormatExtended, ISO8601FormatTimeOnly);
DT: array[boolean] of string = ('date', 'time');
// Index "boolean" is to be understood as "isTimeOnly"
var
lStyle: string = '';
lStyle: string;
strValue: String;
displayStr: String;
lIndex: Integer;
isTimeOnly: Boolean;
begin
if ACell^.UsedFormattingFields <> [] then begin
lIndex := FindFormattingInList(ACell);
lStyle := ' table:style-name="ce' + IntToStr(lIndex) + '" ';
lStyle := 'table:style-name="ce' + IntToStr(lIndex) + '" ';
end else
lStyle := '';
// The row should already be the correct one
FCellContent :=
' <table:table-cell office:value-type="date" office:date-value="' + FormatDateTime(ISO8601FormatExtended, AValue) + '"' + lStyle + '>' + LineEnding +
' </table:table-cell>' + LineEnding;
// We have to distinguish between time-only values and values that contain date parts.
isTimeOnly := IsTimeFormat(ACell^.NumberFormat) or IsTimeFormat(ACell^.NumberFormatStr);
strValue := FormatDateTime(FMT[isTimeOnly], AValue);
displayStr := FormatDateTime(ACell^.NumberFormatStr, AValue);
FCellContent := Format(
' <table:table-cell office:value-type="%s" office:%s-value="%s" %s>' + LineEnding +
' <text:p>%s</text:p> ' + LineEnding +
' </table:table-cell>' + LineEnding, [
DT[isTimeOnly], DT[isTimeOnly], strValue, lStyle, displayStr
]);
end;
{

View File

@ -29,6 +29,8 @@ const
ISO8601Format='yyyymmdd"T"hhmmss';
// Extended ISO 8601 date/time format, used in e.g. ODF/opendocument
ISO8601FormatExtended='yyyy"-"mm"-"dd"T"hh":"mm":"ss';
// ISO 8601 time-only format, used in ODF/opendocument
ISO8601FormatTimeOnly='"PT"hh"H"nn"M"ss"S"';
// Endianess helper functions
function WordToLE(AValue: Word): Word;
@ -71,6 +73,8 @@ function IfThen(ACondition: Boolean; AValue1,AValue2: TsNumberFormat): TsNumberF
function IsCurrencyFormat(AFormat: TsNumberFormat): Boolean;
function IsDateTimeFormat(AFormat: TsNumberFormat): Boolean; overload;
function IsDateTimeFormat(AFormatStr: String): Boolean; overload;
function IsTimeFormat(AFormat: TsNumberFormat): Boolean; overload;
function IsTimeFormat(AFormatStr: String): Boolean; overload;
function BuildCurrencyFormatString(ADialect: TsNumFormatDialect;
ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings;
@ -579,6 +583,24 @@ begin
end;
end;
function IsTimeFormat(AFormat: TsNumberFormat): boolean;
begin
Result := AFormat in [nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM,
nfTimeInterval];
end;
function IsTimeFormat(AFormatStr: String): Boolean;
var
parser: TsNumFormatParser;
begin
parser := TsNumFormatParser.Create(nil, AFormatStr);
try
Result := parser.IsTimeFormat;
finally
parser.Free;
end;
end;
{ Builds a date/time format string from the numberformat code. If the format code
is nfFmtDateTime the given AFormatString is used. AFormatString can use the
abbreviations "dm" (for "d/mmm"), "my" (for "mmm/yy"), "ms" (for "mm:ss")