Windows – Win32:如何将字符串转换为日期?
|
在 Windows中,我想使用精确格式字符串将字符串解析为日期. 例如,给定字符串 "6/12/2010" 和格式: "M/d/yyyy" 我想将字符串转换为日期,同时确保日期与格式匹配. 我还需要能够指定Y2K滑动窗口,枢轴.这意味着如果(正确)输入了2位数年份,我将指定将来考虑年份的年数.例如.: Two-digit Year Pivot Four-digit year ============== ===== =============== 30 +0 1929 30 +18 1929 30 +19 1929 30 +20 2029 30 +21 2029 30 +100 2029 .NET已经提供了一个DateTime.ParseExact函数,它几乎完全符合我的需要: date = DateTime.ParseExact("6/12/2010",DateTimeFormatInfo.ShortDatePattern,Thread.CurrentThread.CurrentCulture);
除了我不能告诉它100年的支点价值. 更多例子: String Format Specifier Date "6/7/2029" "M/d/yyyy" 6/7/2029 "6/7/29" "M/d/yyyy" (invalid,year too short) "6/7/29" "M/d/yy" 6/7/1929 (+0 pivot) "6/7/29" "M/d/yy" 6/7/2029 (+100 pivot "6/7/29" "M/d/yy" 6/7/2029 (+50 pivot) "6/7/29" "M/d/yy" 6/7/2029 "6/7/2029" "M.d.yyyy" (invalid,incorrect separators) "6.7.2029" "M.d.yyyy" 6/7/2029 "6.7.2029" "M-d-yyyy" (invalid,incorrect separators) "6/7/2029" "M/dd/yyyy" (invalid,days requires leading zero) "6/07/2029" "M/dd/yyyy" (invalid,days requires leading zero) "6/07/2029" "MM/dd/yyyy" (invalid,months requires leading zero) "06/07/2029 "MM/dd/yyyy" 6/7/2029 "06/07/2029" "MM/d/yyyy" (invalid,days should not have leading zero) "06/7/2029" "MM/d/yyyy" 6/7/2029 我知道Windows没有将字符串转换为日期的原生API. 是否有任何已建立的代码可以使用格式说明符将字符串转换为日期?电脑已经存在了一段时间了;有人必须已经解决了这个问题. 以下是您可能希望在Windows中看到的一些示例格式说明符的列表: > M / d / yyyy 也可以看看 > MSDN:Day,Month,Year,and Era Format Pictures class function TDateTimeUtils.TryStrToDateExact(const S,DateFormat: string; out Value: TDateTime): Boolean;
begin
{
Assume Microsoft's de-facto standard for y2k fixup: 2029
1930-2029
}
Result := TDateTimeUtils.TryStrToDateExact(S,DateFormat,2029,{out}Value);
end;
class function TDateTimeUtils.TryStrToDateExact(const S,DateFormat: string; PivotYear: Integer;
out Value: TDateTime): Boolean;
var
Month,Day,Year: Integer;
Tokens: TStringDynArray;
CurrentToken: string;
i,n: Integer;
Partial: string;
MaxValue: Integer;
nCurrentYear: Integer;
function GetCurrentYear: Word;
var
y,m,d: Word;
begin
DecodeDate(Now,y,d);
Result := y;
end;
begin
Result := False;
{
M/dd/yy
Valid pictures codes are
d Day of the month as digits without leading zeros for single-digit days.
dd Day of the month as digits with leading zeros for single-digit days.
ddd Abbreviated day of the week as specified by a LOCALE_SABBREVDAYNAME* value,for example,"Mon" in English (United States).
Windows Vista and later: If a short version of the day of the week is required,your application should use the LOCALE_SSHORTESTDAYNAME* constants.
dddd Day of the week as specified by a LOCALE_SDAYNAME* value.
M Month as digits without leading zeros for single-digit months.
MM Month as digits with leading zeros for single-digit months.
MMM Abbreviated month as specified by a LOCALE_SABBREVMONTHNAME* value,"Nov" in English (United States).
MMMM Month as specified by a LOCALE_SMONTHNAME* value,"November" for English (United States),and "Noviembre" for Spanish (Spain).
y Year represented only by the last digit.
yy Year represented only by the last two digits. A leading zero is added for single-digit years.
yyyy Year represented by a full four or five digits,depending on the calendar used. Thai Buddhist and Korean calendars have five-digit years. The "yyyy" pattern shows five digits for these two calendars,and four digits for all other supported calendars. Calendars that have single-digit or two-digit years,such as for the Japanese Emperor era,are represented differently. A single-digit year is represented with a leading zero,"03". A two-digit year is represented with two digits,"13". No additional leading zeros are displayed.
yyyyy Behaves identically to "yyyy".
g,gg Period/era string formatted as specified by the CAL_SERASTRING value.
The "g" and "gg" format pictures in a date string are ignored if there is no associated era or period string.
PivotYear
The maximum year that a 1 or 2 digit year is assumed to be.
The Microsoft de-factor standard for y2k is 2029. Any value greater
than 29 is assumed to be 1930 or higher.
e.g. 2029:
1930,...,2000,2001,2029
If the PivotYear is between 0 and 99,then PivotYear is assumed to be
a date range in the future. e.g. (assuming this is currently 2010):
Pivot Range
0 1911..2010 (no future years)
1 1912..2011
...
98 2009..2108
99 2010..2099 (no past years)
0 ==> no years in the future
99 ==> no years in the past
}
if Length(S) = 0 then
Exit;
if Length(DateFormat) = 0 then
Exit;
Month := -1;
Day := -1;
Year := -1;
Tokens := TDateTimeUtils.TokenizeFormat(DateFormat);
n := 1; //input string index
for i := Low(Tokens) to High(Tokens) do
begin
CurrentToken := Tokens[i];
if CurrentToken = 'MMMM' then
begin
//Long month names,we don't support yet (you're free to write it)
Exit;
end
else if CurrentToken = 'MMM' then
begin
//Short month names,we don't support yet (you're free to write it)
Exit;
end
else if CurrentToken = 'MM' then
begin
//Month,with leading zero if needed
if not ReadDigitString(S,n,2{MinDigits},2{MaxDigits},1{MinValue},12{MaxValue},{var}Month) then Exit;
end
else if CurrentToken = 'M' then
begin
//months
if not ReadDigitString(S,1{MinDigits},{var}Month) then Exit;
end
else if CurrentToken = 'dddd' then
begin
Exit; //Long day names,we don't support yet (you're free to write it)
end
else if CurrentToken = 'ddd' then
begin
Exit; //Short day names,we don't support yet (you're free to write it);
end
else if CurrentToken = 'dd' then
begin
//If we know what month it is,and even better if we know what year it is,limit the number of valid days to that
if (Month >= 1) and (Month <= 12) then
begin
if Year > 0 then
MaxValue := MonthDays[IsLeapYear(Year),Month]
else
MaxValue := MonthDays[True,Month]; //we don't know the year,assume it's a leap year to be more generous
end
else
MaxValue := 31; //we don't know the month,so assume it's the largest
if not ReadDigitString(S,MaxValue{MaxValue},{var}Day) then Exit;
end
else if CurrentToken = 'd' then
begin
//days
//If we know what month it is,{var}Day) then Exit;
end
else if (CurrentToken = 'yyyy') or (CurrentToken = 'yyyyy') then
begin
//Year represented by a full four or five digits,depending on the calendar used.
{
Thai Buddhist and Korean calendars have five-digit years.
The "yyyy" pattern shows five digits for these two calendars,and four digits for all other supported calendars.
Calendars that have single-digit or two-digit years,such as for
the Japanese Emperor era,are represented differently.
A single-digit year is represented with a leading zero,for
example,"13". No additional leading zeros are displayed.
}
if not ReadDigitString(S,4{MinDigits},4{MaxDigits},0{MinValue},9999{MaxValue},{var}Year) then Exit;
end
else if CurrentToken = 'yyy' then
begin
//i'm not sure what this would look like,so i'll ignore it
Exit;
end
else if CurrentToken = 'yy' then
begin
//Year represented only by the last two digits. A leading zero is added for single-digit years.
if not ReadDigitString(S,99{MaxValue},{var}Year) then Exit;
nCurrentYear := GetCurrentYear;
Year := (nCurrentYear div 100 * 100)+Year;
if (PivotYear < 100) and (PivotYear >= 0) then
begin
//assume pivotyear is a delta from this year,not an absolute value
PivotYear := nCurrentYear+PivotYear;
end;
//Check the pivot year value
if Year > PivotYear then
Year := Year - 100;
end
else if CurrentToken = 'y' then
begin
//Year represented only by the last digit.
if not ReadDigitString(S,1{MaxDigits},9{MaxValue},{var}Year) then Exit;
nCurrentYear := GetCurrentYear;
Year := (nCurrentYear div 10 * 10)+Year;
if (PivotYear < 100) and (PivotYear >= 0) then
begin
//assume pivotyear is a delta from this year,not an absolute value
PivotYear := nCurrentYear+PivotYear;
end;
//Check the pivot year value
if Year > PivotYear then
Year := Year - 100;
end
else
begin
//The input string should contains CurrentToken starting at n
Partial := Copy(S,Length(CurrentToken));
Inc(n,Length(CurrentToken));
if Partial <> CurrentToken then
Exit;
end;
end;
//If there's still stuff left over in the string,then it's not valid
if n <> Length(s)+1 then
begin
Result := False;
Exit;
end;
if Day > MonthDays[IsLeapYear(Year),Month] then
begin
Result := False;
Exit;
end;
try
Value := EncodeDate(Year,Day);
except
Result := False;
Exit;
end;
Result := True;
end;
class function TDateTimeUtils.TokenizeFormat(fmt: string): TStringDynArray;
var
i: Integer;
partial: string;
function IsDateFormatPicture(ch: AnsiChar): Boolean;
begin
case ch of
'M','d','y': Result := True;
else Result := False;
end;
end;
begin
SetLength(Result,0);
if Length(fmt) = 0 then
Exit;
//format is only one character long? If so then that's the tokenized entry
if Length(fmt)=1 then
begin
SetLength(Result,1);
Result[0] := fmt;
end;
partial := fmt[1];
i := 2;
while i <= Length(fmt) do
begin
//If the characters in partial are a format picture,and the character in fmt is not the same picture code then write partial to result,and reset partial
if IsDateFormatPicture(partial[1]) then
begin
//if the current fmt character is different than the running partial picture
if (partial[1] <> fmt[i]) then
begin
//Move the current partial to the output
//and start a new partial
SetLength(Result,Length(Result)+1);
Result[High(Result)] := partial;
Partial := fmt[i];
end
else
begin
//the current fmt character is more of the same format picture in partial
//Add it to the partial
Partial := Partial + fmt[i];
end;
end
else
begin
//The running partial is not a format picture.
//If the current fmt character is a picture code,then write out the partial and start a new partial
if IsDateFormatPicture(fmt[i]) then
begin
//Move the current partial to the output
//and start a new partial
SetLength(Result,Length(Result)+1);
Result[High(Result)] := partial;
Partial := fmt[i];
end
else
begin
//The current fmt character is another non-picture code. Add it to the running partial
Partial := Partial + fmt[i];
end;
end;
Inc(i);
Continue;
end;
//If we have a running partial,then add it to the output
if partial <> '' then
begin
SetLength(Result,Length(Result)+1);
Result[High(Result)] := partial;
end;
end;
class function TDateTimeUtils.ReadDigitString(const S: string; var Pos: Integer;
MinDigits,MaxDigits: Integer; MinValue,MaxValue: Integer;
var Number: Integer): Boolean;
var
Digits: Integer;
Value: Integer;
Partial: string;
CandidateNumber: Integer;
CandidateDigits: Integer;
begin
Result := False;
CandidateNumber := -1;
CandidateDigits := 0;
Digits := MinDigits;
while Digits <= MaxDigits do
begin
Partial := Copy(S,Pos,Digits);
if Length(Partial) < Digits then
begin
//we couldn't get all we wanted. We're done; use whatever we've gotten already
Break;
end;
//Check that it's still a number
if not TryStrToInt(Partial,Value) then
Break;
//Check that it's not too big - meaning that getting anymore wouldn't work
if (Value > MaxValue) then
Break;
if (Value >= MinValue) then
begin
//Hmm,looks good. Keep it as our best possibility
CandidateNumber := Value;
CandidateDigits := Digits;
end;
Inc(Digits); //try to be greedy,grabbing even *MORE* digits
end;
if (CandidateNumber >= 0) or (CandidateDigits > 0) then
begin
Inc(Pos,CandidateDigits);
Number := CandidateNumber;
Result := True;
end;
end; (编辑:鄂州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
