3

i am downloading a file from my server (i only get the bytes and a DateTime for the lastwritetime attribute) and after downloading the data i create a new file on my local machine and want to set the lastwritetime attribute. For this i am using the following method:

procedure SetFileDate(const FileName: string; NewDate: TDateTime);
var
    FileDate, FileHandle: Integer;
begin
    try
        FileDate := DateTimeToFileDate(NewDate);

        FileHandle := FileOpen(FileName, fmOpenReadWrite or fmShareDenyWrite);
        if FileHandle > 0 then
            begin
                FileSetDate(FileHandle, FileDate);
                FileClose(FileHandle);
            end;
    except
        begin
            // ERROR Log
            err.Msg('FileReqThrd.SetFileDate');
        end;
    end;
end;

For the 'NewDate' parameter i use the DateTime which i get from my server. I tried to convert the DateTime from the server like this to get the valid lastwritetime (i am requesting the data from a WCF this is why i am converting it to UTCDateTime, the untouched data from the WCF service is TXSDateTime):

TDateTime cloudFileDateTime := StrToDateTime(DateTimeToStr(cloudDownloadResult.FileCloudData.Lastwritetime.AsUTCDateTime));

But in the end my lastwritetime attribute from files which have a lastwritetime in the wintertime period are wrong with -1h.

I hope you understand my problem and can give me an idea how to solve it.

Best regards

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
TryToSolveItSimple
  • 873
  • 1
  • 12
  • 23

2 Answers2

5

The easiest way to do this is to call TFile.SetLastWriteTimeUtc from the System.IOUtils unit.

TFile.SetLastWriteTimeUtc(FileName, 
    DateTimeUtc);

If this function is not available use the Win32 API function SetFileTime.

You'll also need DateTimeToSystemTime and then SystemTimeToFileTime in that scenario.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • didn't know this procedure - thanks for that! TFile.SetLastWriteTimeUTC needs a TDateTime parameter so i have to reconvert from filetilme to datetime? after converting my datetime to systemtime to filetime? – TryToSolveItSimple Aug 25 '14 at 13:13
  • `cloudDownloadResult.FileCloudData.Lastwritetime.AsUTCDateTime` appears to be what you need – David Heffernan Aug 25 '14 at 13:14
  • this is what i am using at this moment but it still seems that there is something not working as expected. My files with wintertime are now correct but the summertime files are +2h. – TryToSolveItSimple Aug 25 '14 at 13:23
  • it is not working for me. Yes it sets the lastwritetime but not the correct one. If i go to the debugger i see the parameter has the correct DateTime but the time the file has after setting it via TFile.SetLastWriteTimeUtc is for summertime files +2h and for wintertime files +0h (correct for winter). I used several convertions of my source lastwritetime DateTime. I am trying this now since days... The reason why my upon posted source code includes the DateTimeToStr and StrToDateTime convertion is that i thought there would be any info about winter/summertime in the DateTime and ignore this. – TryToSolveItSimple Aug 25 '14 at 13:47
  • The code works correctly. Windows displays dates and times in the prevailing dst. Not the that was active at that date. TDateTime has no dst/time zone info. The code in my answer works correctly. The problem is your expectations. We can't work out what you got wrong. We don't know what your data is. We don't know how you are reading file date times. – David Heffernan Aug 25 '14 at 14:05
  • @Askinator: If you are dealing with Tdatetime in UTC format end to end, you don't need to worry about DST. You can feed the `cloudDownloadResult.FileCloudData.Lastwritetime.AsUTCDateTime` value directly to the `TFile.SetLastWriteTimeUtc`function. – whosrdaddy Aug 25 '14 at 14:10
  • Right. I assume your code now reads `TFile.SetLastWriteTimeUtc(cloudDownloadResult.FileCloudData.Lastwritetime.AsUTCDateTime)` – David Heffernan Aug 25 '14 at 14:14
  • Yes i think i've got it now and it should work with the utc format - the failure was that my uploaded files (lastwritetime information) was not in UTC format and because this the lastwritetime from the download was different from the upload. Your code works. Thanks alot! – TryToSolveItSimple Aug 25 '14 at 14:33
  • Is there a bug with the `TFile.GetLastWriteTimeUtc(source: string)` function? I get wrong times but with the WinApi `GetFileAttributesEx()` i get the correct time (utc). Using Delphi XE2. My Code is working now thanks to you but i wonder why i can't use the easy writing function from the TFile Unit. – TryToSolveItSimple Aug 26 '14 at 07:51
  • I see no evidence of any bug. `TFile.GetLastWriteTimeUtc` calls `GetFileAttributesEx`, and then converts from local time to UTC. Most likely it is your use of `GetFileAttributesEx` that is wrong. How do you convert from local to UTC? – David Heffernan Sep 04 '14 at 12:27
0

The answer provided by David (to use TFile.SetLastWriteTimeUtc) is correct. However, there was some discussion in the comments about bugs. As I am unable to comment (due to lack of rep), I'll add this here for anyone who comes across this problem in future.

While TFile.SetLastWriteTimeUtc works correctly, TFile.GetLastWriteTimeUtc does indeed have a bug relating to daylight saving time. There is a bug report filed with Embarcadero, and it looks like they've now fixed it in Delphi 10.3 Rio (though I haven't tried it yet).

If you are working with an older version of Delphi, you will have to work around the problem via use of the Windows API. e.g. GetFileAttributesEx:

function GetFileModTimeUtc(filePath: string): TDateTime;
var data: TWin32FindData;
var sysTime: TSystemTime;
begin
  if GetFileAttributesEx(PChar(filePath), GetFileExInfoStandard, @data) and
      FileTimeToSystemTime(data.ftLastWriteTime, sysTime) then begin
    Result := SystemTimeToDateTime(sysTime);
  end else begin
    raise Exception.Create('Unable to get last file write time for ' + filePath);
  end;
end;
Narotak
  • 41
  • 2