我需要将Windows上文件的上次写访问时间存储为字符串。需要避免任何夏令时或用户在不同时区的问题。我认为我有一个解决方案,但是日期似乎有很多问题。
我不需要将日期与以前存储的日期进行比较。只需要知道它是否改变。
存储原始TFileTime记录(表示为Int64)似乎是最好的,因为这实际上是Windows实际使用的2个DWORDS。 Delphi似乎要使用TDateTime(FileAge)或整数(FileSetDate)。这两个似乎都转换为本地时间,并且仅使用32位和64位。
我确实需要显示“用户友好”字符串,并做了UTC显示字符串来仔细检查存储的值。对于这些,我确实使用了TDateTime来转换TFileTime格式的 out 。
帮助器单元如下:
unit FileTimeHelperUnt;
interface
uses
Winapi.Windows, System.SysUtils;
type
TFileTimeHelper = record helper for TFileTime
function ToString: String; //Use to export TFileTime as Int64 String.
function FromString( AString: String ): Boolean; //Use to restore
TFileTime from Int64 String
function GetLastWriteTime( AFilePathStr: String ): Boolean;
function SetLastWriteTime( AFilePathStr: String ): Boolean;
function UTCString: String;
function UserFriendlyString: String; //Like Windows Explorer and Local.
end;
implementation
{ TFileTimeHelper }
function TFileTimeHelper.ToString: String;
var
TmpInt64: Int64 absolute Self;
begin
Result := TmpInt64.ToString;
end;
function TFileTimeHelper.FromString(AString: String): Boolean;
begin
Result := False;
try
Int64(Self) := StrToInt64( AString );
Result := True;
except on E: Exception do
end;
end;
function TFileTimeHelper.GetLastWriteTime(AFilePathStr: String): Boolean;
var
TmpSearchRec: TSearchRec;
begin
Result := False;
if FileExists( AFilePathStr )=False then
Exit;
if FindFirst( AFilePathStr, faAnyFile, TmpSearchRec )=0 then
begin
Self := TmpSearchRec.FindData.ftLastWriteTime;
Result := True;
end;
end;
function TFileTimeHelper.SetLastWriteTime(AFilePathStr: String): Boolean;
var
TmpHandle: THandle;
begin
Result := False;
if FileExists( AFilePathStr )=False then
Exit;
try
TmpHandle := FileOpen(AFilePathStr, fmOpenWrite);
if TmpHandle = THandle(-1) then
Exit;
try
SetFileTime(TmpHandle, nil, nil, @Self);
Result := (GetLastError=0);
finally
FileClose( TmpHandle );
end;
except on E: Exception do
end;
end;
function TFileTimeHelper.UTCString: String;
var
TmpSystemTime: TSystemTime;
TmpDateTime: TDateTime;
begin
FileTimeToSystemTime( Self, TmpSystemTime );
TmpDateTime := SystemTimeToDateTime(TmpSystemTime);
Result := DateTimeToStr( TmpDateTime );
end;
function TFileTimeHelper.UserFriendlyString: String;
var
TmpSystemTime: TSystemTime;
TmpLocalLastWriteFileTime: TFileTime;
TmpDateTime: TDateTime;
begin
FileTimeToLocalFileTime( Self, TmpLocalLastWriteFileTime );
FileTimeToSystemTime( TmpLocalLastWriteFileTime, TmpSystemTime );
TmpDateTime := SystemTimeToDateTime(TmpSystemTime);
Result := FormatDateTime( 'm/d/yyyy h:nn ampm', TmpDateTime );
end;
end.
呼叫方如下:
procedure TForm12.btnGetFileDate2Click(Sender: TObject);
var
TmpFileTime: TFileTime;
begin
TmpFileTime.GetLastWriteTime( 'File.txt' );
edtFileDateTime.Text := TmpFileTime.ToString;
edtLocalFileDateTime.Text := TmpFileTime.UserFriendlyString;
edtUTCDateTime.Text := TmpFileTime.UTCString;
end;
procedure TForm12.btnSetFileDate2Click(Sender: TObject);
var
TmpFileTime: TFileTime;
begin
TmpFileTime.FromString( edtFileDateTime.Text );
TmpFileTime.SetLastWriteTime( 'File.txt' );
end;
一切似乎都正常。我现在不担心TFileTime从64位更改。希望我不会错过任何可能引起问题的情况。
此外,希望如果问题不多,其他人可能会觉得有用。
问题是:该代码是否会遇到任何时区或夏令时问题?我认为这段代码应避免出现“现在保存并在夏令时更改后加载”的问题。或出现“保存在我的时区,然后在另一个时区被其他人加载”的问题。 TFileTime结构应保持不变,并且我的程序将识别出它没有变化。不确定我是否列出了所有潜在问题。基本上,在任何情况下存储字符串并在以后或在其他位置加载都会使我的程序认为有更改吗?
谢谢。
答案 0 :(得分:0)
无论出于何种原因,我最初的研究都没有发表这篇文章:MS FileTime Structure
2件事:
为了完整起见,这是最终的生产代码:
unit FileTimeHelperUnt;
interface
uses
Winapi.Windows, System.SysUtils;
type
TFileTimeCompare = ( ftNewer, ftOlder, ftEqual );
TFileTimeHelper = record helper for TFileTime
function ToString: String;
function FromString( AString: String ): Boolean;
function GetLastWriteTime( AFilePathStr: String ): Boolean;
function SetLastWriteTime( AFilePathStr: String ): Boolean;
function Compare( AFileTime: TFileTime ): TFileTimeCompare;
function UTCString: String;
function UserFriendlyString: String; //Like Windows Explorer and Local.
end;
implementation
{ TFileTimeHelper }
function TFileTimeHelper.ToString: String;
begin
Result := IntToStr( Integer(Self.dwLowDateTime) ) + ',' + IntToStr( Integer(Self.dwHighDateTime) );
end;
function TFileTimeHelper.FromString( AString: String ): Boolean;
var
TmpLowDateTimeStr: String;
TmpHighDateTimeStr: String;
TmpPos: Integer;
begin
Result := False;
try
if AString.IsEmpty then
begin
Exit;
end;
TmpPos := Pos( ',', AString );
if TmpPos=0 then
begin
Exit;
end;
TmpLowDateTimeStr := Copy( AString, 1, Pos( ',', AString )-1 );
TmpHighDateTimeStr := Copy( AString, Pos( ',', AString )+1, MaxInt );
Self.dwLowDateTime := DWORD( StrToInt( TmpLowDateTimeStr ) );
Self.dwHighDateTime := DWORD( StrToInt( TmpHighDateTimeStr ) );
Result := True;
except on E: Exception do
end;
end;
function TFileTimeHelper.GetLastWriteTime(AFilePathStr: String): Boolean;
var
TmpSearchRec: TSearchRec;
begin
Result := False;
if FileExists( AFilePathStr )=False then
Exit;
if FindFirst( AFilePathStr, faAnyFile, TmpSearchRec )=0 then
begin
try
Self.dwLowDateTime := TmpSearchRec.FindData.ftLastWriteTime.dwLowDateTime;
Self.dwHighDateTime := TmpSearchRec.FindData.ftLastWriteTime.dwHighDateTime;
Result := True;
finally
FindClose( TmpSearchRec );
end;
end;
end;
function TFileTimeHelper.SetLastWriteTime(AFilePathStr: String): Boolean;
var
TmpHandle: THandle;
begin
Result := False;
if FileExists( AFilePathStr )=False then
Exit;
try
TmpHandle := FileOpen(AFilePathStr, fmOpenWrite);
if TmpHandle = THandle(-1) then
Exit;
try
SetFileTime(TmpHandle, nil, nil, @Self);
Result := (GetLastError=0);
finally
FileClose( TmpHandle );
end;
except on E: Exception do
end;
end;
//Given the imprecision of certain file systems, only compare to the minute.
function TFileTimeHelper.Compare( AFileTime: TFileTime ): TFileTimeCompare;
var
TmpSelfSystemTime: TSystemTime;
TmpArgSystemTime: TSystemTime;
function CompareWord( A, B: Word ): TFileTimeCompare;
begin
if A = B then
Result := ftEqual
else if A < B then
Result := ftNewer
else
Result := ftOlder;
end;
begin
Result := ftEqual;
FileTimeToSystemTime( Self, TmpSelfSystemTime );
FileTimeToSystemTime( AFileTime, TmpArgSystemTime );
//Compare Year
case CompareWord( TmpSelfSystemTime.wYear, TmpArgSystemTime.wYear ) of
ftNewer: Exit( ftNewer );
ftOlder: Exit( ftOlder );
end;
//Compare Month
case CompareWord( TmpSelfSystemTime.wMonth, TmpArgSystemTime.wMonth ) of
ftNewer: Exit( ftNewer );
ftOlder: Exit( ftOlder );
end;
//Compare Day
case CompareWord( TmpSelfSystemTime.wMonth, TmpArgSystemTime.wMonth ) of
ftNewer: Exit( ftNewer );
ftOlder: Exit( ftOlder );
end;
//Compare Hour
case CompareWord( TmpSelfSystemTime.wHour, TmpArgSystemTime.wHour ) of
ftNewer: Exit( ftNewer );
ftOlder: Exit( ftOlder );
end;
//Compare Minute
case CompareWord( TmpSelfSystemTime.wMinute, TmpArgSystemTime.wMinute ) of
ftNewer: Exit( ftNewer );
ftOlder: Exit( ftOlder );
end;
end;
function TFileTimeHelper.UTCString: String;
var
TmpSystemTime: TSystemTime;
TmpDateTime: TDateTime;
begin
FileTimeToSystemTime( Self, TmpSystemTime );
TmpDateTime := SystemTimeToDateTime(TmpSystemTime);
Result := DateTimeToStr( TmpDateTime );
end;
function TFileTimeHelper.UserFriendlyString: String;
var
TmpSystemTime: TSystemTime;
TmpLocalLastWriteFileTime: TFileTime;
TmpDateTime: TDateTime;
begin
try
FileTimeToLocalFileTime( Self, TmpLocalLastWriteFileTime );
FileTimeToSystemTime( TmpLocalLastWriteFileTime, TmpSystemTime );
TmpDateTime := SystemTimeToDateTime(TmpSystemTime);
Result := FormatDateTime( 'm/d/yyyy h:nn ampm', TmpDateTime );
except on E: Exception do
Result := 'Unknown.';
end;
end;
end.
感谢您的帮助。我将其作为答案进行核对,主要是因为这是所使用的解决方案。