Copy file without changing creation date

I want to be able to copy files without changing the creation date. With pictures, I want to retain the date they were taken. Is there a direct way to do this. Or can I get the creation date before copying and then put it back afterward ? How do I do it ?
ChLaAsked:
Who is Participating?
 
systanCommented:
Does NOT work; ? (file created date_time remains the same)
var
     SourceFileAge: Integer;
  begin
     SourceFileAge := FileAge('c:\bdlog.txt');
     CopyFile('c:\bdlog.txt','d:\bdlog.txt',false);
     FileSetDate('d:\bdlog.txt', SourceFileAge);
 end;

Open in new window


This works; (corrected)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function GetCreationDate(fn:string):TDateTime;
var
  FHandle : THandle;
  A,C,W : TFileTime;
  Bias : double;
  SysTimeStruct: SYSTEMTIME;
  TimeZoneInfo: TTimeZoneInformation;
begin
    Bias :=0;
    Fhandle:=createfile(PChar(Fn), 0,FILE_SHARE_READ, nil, OPEN_EXISTING, SECURITY_ANONYMOUS, 0);
    if GetTimeZoneInformation(TimeZoneInfo) <> $FFFFFFFF then
    Bias := TimeZoneInfo.Bias / 1440;
    if GetFileTime(FHandle,@C, @A, @W) then
      begin
        FileTimeToSystemTime(C, SysTimeStruct);
        result := SystemTimeToDateTime(SysTimeStruct) - Bias;
      end;
    FileClose(FHandle);
end;

function SetCreationDate(FileName: string; dtCreation: TDateTime): Boolean;
var
  hHandle: THandle;
  ftCreation: TFiletime;

  function DTtoFT(dt: TDateTime): TFiletime;
  var
    dwft: DWORD;
    ft: TFiletime;
  begin
    dwft := DateTimeToFileDate(dt);
    DosDateTimeToFileTime(LongRec(dwft).Hi, LongRec(dwft).Lo, ft);
    LocalFileTimeToFileTime(ft, Result);
  end;

begin
  hhandle := CreateFile(PChar(FileName),
  GENERIC_READ or GENERIC_WRITE,0,nil,
  OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS, 0);

  if hHandle <> INVALID_HANDLE_VALUE then
  begin
    try
      ftCreation       := DTtoFT(dtCreation);
      Result := SetFileTime(hHandle, @ftCreation, nil, nil);
    finally
      CloseHandle(hHandle);
    end;
  end
  else
    Result := False;
end;


procedure TForm1.Button1Click(Sender: TObject);
var getdate: tdatetime;
sor,des: string;
begin
sor :='c:\bdlog.txt';
des := 'd:\bdlog.txt';
getdate := GetCreationDate(sor);
CopyFile(PChar(sor),PChar(des),false);
SetCreationDate(des, getdate);
ShowMessage(DateTimeToStr(getdate));
end;

end.

Open in new window

0
 
systanCommented:
the code;
function GetCreationDate(fn:string):TDateTime;
var
  FHandle : THandle;
  AT,CT,WT : TSystemTime;
  A,C,W : TFileTime;
begin
    Fhandle:=createfile(Pchar(fn), 0,FILE_SHARE_READ, nil, OPEN_EXISTING, SECURITY_ANONYMOUS, 0);
    if GetFileTime(FHandle,@C,@A,@W) then
      begin
        FileTimeToSystemTime(C,CT);
        result := SystemTimeToDateTime(CT);
      end;
    FileClose(FHandle);
end;



procedure SetCreationDate(fn: string; NewDateTime: TDateTime);
var
  FileHandle: Integer;
  FileTime: TFileTime;
  LFT: TFileTime;
  LST: TSystemTime;
begin
    DecodeDate(NewDateTime, LST.wYear, LST.wMonth, LST.wDay);
    DecodeTime(NewDateTime, LST.wHour, LST.wMinute, LST.wSecond, LST.wMilliSeconds);
    if SystemTimeToFileTime(LST, LFT) then
    begin
      if LocalFileTimeToFileTime(LFT, FileTime) then
      begin
        FileHandle := FileOpen(fn, fmOpenReadWrite or fmShareExclusive);
        SetFileTime(FileHandle, @FileTime, @FileTime, nil);
      end;
    end;
    FileClose(FileHandle);
end;




procedure TForm1.Button1Click(Sender: TObject);
var getdate: tdatetime;
begin
getdate := GetCreationDate('c:\autoexec.bat');
copyfile('c:\autoexec.bat','d:\autoexec.dat',false);
SetCreationDate('d:\autoexec.dat', getdate);
end;

Open in new window

0
 
OrcbighterCommented:
All you need is the FileInfo and File classes
Below is a simple function to retrieve all the files from a directory and copy them to a new location.
it then modifies the dates of the file to the values they originally had.
Note, I wrote this as a recursive function to loop down through all the sub-directories of my origibnal starting directory. If you want to process the files one at a time, then just rip out the functionality inside the recursive function and use that.

I hope it helps

// inside main program
string rootdir = "c:\\temp";
DirSearch( rootdir);

//----


private void DirSearch( string searchDir )
{
    bool bAll_OK = true;
    string[] dirFileList = null;

    try
    {
        // find all files first
        dirFileList = Directory.GetFiles( searchDir );
    }
    catch ( UnauthorizedAccessException ex )
    {
        // NTFS file systems can contain reparse points in the form of junction points, symbolic links, 
        // and hard links. 
        // The .NET Framework methods such as GetFiles and GetDirectories will not return any subdirectories 
        // under a reparse point. This behavior guards against the risk of entering into an infinite loop when 
        // two reparse points refer to each other.
        bAll_OK = false;
        Console.WriteLine( "Unauthorised access exception: {0}", ex.Message );
    }
    catch ( Exception ex )
    {
        bAll_OK = false;
        Console.WriteLine( "Unexpected Exception in Directory.GetFiles(): {0}", ex.Message );
    }

    if ( bAll_OK )
    {
        // we have some files to check in this directory
        string fileItem = "";

        try
        {
            foreach ( string item in dirFileList )
            {
                fileItem = item;
                FileAttributes attr = File.GetAttributes( item );
                string ModificationDateAsString = "";
                string CreationDateAsString = "";
                string LastAccessedDateAsString = "";
                string filename = "";

                //make sure its a file and not a sub-directory
                if ( ( attr & FileAttributes.Directory ) != FileAttributes.Directory )
                {
                    FileInfo fileInfo = new FileInfo( item );
                  
                    filename = item;
                    ModificationDateAsString = fileInfo.LastWriteTime.ToString();
                    CreationDateAsString = fileInfo.CreationTime.ToString();
                    LastAccessedDateAsString = fileInfo.LastAccessTime.ToString();

                    //...
                    // copy this file to your new destination
                    //...

                    // ... Need Newfilername to hold path of new location and filename
                    string newFilename = ""; // extract filename from old path, append new path to name
                    //...

                    // now change the date back to original
                    File.SetCreationTime( newFilename, DateTime.Parse( CreationDateAsString ) );
                    File.SetLastAccessTime( newFilename, DateTime.Parse( ModificationDateAsString ) );
                    File.SetLastWriteTime( newFilename, DateTime.Parse( LastAccessedDateAsString ) );

                }
            } // end-foreach
        }
        catch ( Exception ex )
        {
            Console.WriteLine( "DirSearch: Unexpected error for file {0} in dir {1} - {2}",
                               fileItem, searchDir, ex.Message );
        }


        try
        {
            // find all the subdirectories in this directory
            string[] subDirs = Directory.GetDirectories( searchDir );

            foreach ( string folder in subDirs )
            {
                DirSearch( folder );
            }
        }
        catch ( Exception ex )
        {
            Console.WriteLine( "DirSearch: Unexpected exception for {0} - {1}", searchDir, ex.Message );
        }
    } // end-if
} // end-function

Open in new window

0
Cloud Class® Course: C++ 11 Fundamentals

This course will introduce you to C++ 11 and teach you about syntax fundamentals.

 
bakry99Commented:
listening
0
 
Ephraim WangoyaCommented:

Hi Systan
You know there is an easier way to do it

  var
     SourceFileAge: Integer;
  begin
     SourceFileAge := FileAge('c:\source.dat');

     FileSetDate('c:\destination.dat', SourceFileAge);
0
 
systanCommented:
thanks, didn't think of it.  Good.
0
 
ChLaAuthor Commented:
How does that work, Ewangoya ? Sorry for my slow response. I have had a busy last couple of days.How does it save the time and date as an integer ?
0
 
Ephraim WangoyaCommented:

The function FileAge returns an integer which represents the system time

The method FileSetDate() takes in an integer value which it then converts to the corresponding FileTime internally

Since both functions work with integers, you dont have to do any conversions yourself which make the two of them convenient to use

0
 
ChLaAuthor Commented:
Hi everyone...sorry for the delay...couldn't work on this yester
day.
I just tried the last thing above from systan
It doesn't seem to be working. The copied files still have the current time/date.

I copied and pasted the two procedures get and set. Here is my implementation of the third procedure:

See code box

The procedure in the code box does successfully copy the selected files.

Did I leave something out ?
procedure TMainForm.CopyLtoR(Sender: TObject);
var
I: Integer;
FileName: String;
SourceFilePath: String;
DestFilePath: String;
GetDate: tDateTime;
begin
  I := 0;
  While I <= JamShellList1.SelectedFiles.Count - 1 do       
  Begin
    SourceFilePath := JamShellList1.Path + JamShellList1.SelectedFiles[I];
    DestFilePath := JamShellList2.Path + JamShellList1.SelectedFiles[I];
    GetDate := GetCreationDate(SourceFilePath);
    //  add something to check that the file soesn't already exist...if file 
    //doesn't exist then
      CopyFile(PWideChar(SourceFilePath), PWideChar(DestFilePath),False);
    SetCreationDate(SourceFilePath, GetDate);
    I := I + 1;
  End;;
end;

Open in new window

0
 
Geert GOracle dbaCommented:
wouldn't you want to change the destination file date ?

 
procedure TMainForm.CopyLtoR(Sender: TObject);
var
I: Integer;
FileName: String;
SourceFilePath: String;
DestFilePath: String;
GetDate: tDateTime;
begin
  I := 0;
  While I <= JamShellList1.SelectedFiles.Count - 1 do       
  Begin
    SourceFilePath := JamShellList1.Path + JamShellList1.SelectedFiles[I];
    DestFilePath := JamShellList2.Path + JamShellList1.SelectedFiles[I];
    GetDate := GetCreationDate(SourceFilePath);
    //  add something to check that the file soesn't already exist...if file 
    //doesn't exist then
    // if not FileExists(DestFilePath) then 
      CopyFile(PWideChar(SourceFilePath), PWideChar(DestFilePath),False);
    SetCreationDate(DestFilePath, GetDate);
    I := I + 1;
  End;;
end;

Open in new window

0
 
ChLaAuthor Commented:
I'm trying to keep the original creation date so it continues to reflect the date an image was taken. This allows a collection of images to later be put into the order they were taken, even though they may have been copied instead of moved. Doesn't the proc above get the existing creation date with GetCreationDate, put it into GetDate, before copying the file, and then put it back after copying it, with SetCreationDate ? That's what I was hoping, but the creation date is still changing to the date of the copy, like windows.
0
 
systanCommented:
I don't know how you test it, but here's the complete project,
I just test it, it really copies all the datetime properties from datecreated, datemodified, dateaccessed.
please try it too.

setdatetime.zip
0
 
ChLaAuthor Commented:
You are right...stupid mistake on my part. I had put the source file path into setdate instead of the destination file path. I don't know how I missed it when I re-checked it after Geert Gruwez's comment. Sorry.
0
 
ChLaAuthor Commented:
Thank you
0
 
ChLaAuthor Commented:
Sorry for this late comment. Now that I have had time to more fully test the solution from Systan, There is an anomaly. It seems the copied files all have a new time which is 1 hour earlier that the original time. Does this have something to do with that insanity Daylight Savings Time ? Is there a fix ?
It's also not changing every file. Some copied files appear in the destination directory with the current time/date in Creation Date. Can't understand why. I'll attach my code.

If this case is reopened, I request that the last six comments before this, and this line, be removed.
procedure TMainForm.CopyLtoR(Sender: TObject); // this is the procedure that copies files from left list to right
var
  I: Integer;
  FileName: String;
  SourceFilePath: String;
  DestFilePath: String;
  GetDate: TDateTime;

begin
  Memo1.Color := clYellow;
  Memo1.Lines.Add('Copying Left to Right..Please Stand By');
  Application.ProcessMessages;
  I := 0;
  While I <= JamShellList1.SelectedFiles.Count - 1 do // the actual copying loop
  Begin
    SourceFilePath := JamShellList1.Path + JamShellList1.SelectedFiles[I];
    DestFilePath := JamShellList2.Path + JamShellList1.SelectedFiles[I];

    GetDate := GetCreationDate(SourceFilePath);

    if FileExists(DestFilePath) then
      Memo1.Lines.Add('File Exists ' + DestFilePath + ' ...Not Copied')
    else
      CopyFile(PWideChar(SourceFilePath), PWideChar(DestFilePath), False);

    SetCreationDate(DestFilePath, GetDate);
    
    I := I + 1;
  End;
  Stage := 5;
  Memo1.Color := clCream;
  Memo1.Lines.Add('Done ' + IntToStr(I) + ' Files Copied');
  JamShellList2.FullRefresh;
end;

Open in new window

0
 
ChLaAuthor Commented:
Does the time information saved with a file include seconds ? This would be a great thing when many digital stills are taken in a minute.
0
 
Ephraim WangoyaCommented:

Thats because she adjusted the time using TimeBias, remove that piece of code, you don't need to adjust it yourself, windows will compensate for that
function GetCreationDate(fn:string):TDateTime;
var
  FHandle : THandle;
  A,C,W : TFileTime;
  SysTimeStruct: SYSTEMTIME;
begin
    Fhandle:=createfile(PChar(Fn), 0,FILE_SHARE_READ, nil, OPEN_EXISTING, SECURITY_ANONYMOUS, 0);
    if GetTimeZoneInformation(TimeZoneInfo) <> $FFFFFFFF then
      if GetFileTime(FHandle,@C, @A, @W) then
      begin
        FileTimeToSystemTime(C, SysTimeStruct);
        Result := SystemTimeToDateTime(SysTimeStruct);
      end;
    FileClose(FHandle);
end;

Open in new window

0
 
ChLaAuthor Commented:
I just copied and tested your new set creation date procedure above, I tested it by copying 17 MIDI files. All these files have 12-14-2010 4:58PM as the creation date/time. The first of the copied files has the same date/time as the source. The next three have todays date/time for creation date. The next four have the correct date, but the time is 8:58 PM, four hours later than it should be. The next four have todays date/time. The next two have the correct date, but the time is 8:58. The next one has todays date/time. The last two have the correct date, but 8:58PM. It doesn't make any sense to me. My procedure copies all the selected files correctly. Why did it reset the date for the first file correctly and then set the time 4 hours later for the others. And why are some files being skipped ? You can see my copy procedure is simple. The only option is it doesn't copy if the file already exists.
0
 
ChLaAuthor Commented:
I copied your file above except had to add  this variable declaration:   TimeZoneInfo: TTimeZoneInformation;

I just ran another test of the new GetCreationDate function. I copied seven image files of various types. The first original file creation date/time was 5-17-2011 9:10 PM. the copy has 5-18-2011 1:10 AM
The second file should be 12:08PM, but is 4:08 PM.
The third file 11:08AM is 3:08 PM
The fourth  12:08PM is 4:08 PM
The fifth  11:08 AM is 3:08 PM
The sixth 12:08 PM is 4:08 PM
and  finally the seventh file 3-14-2011 8:27PM turned into 3-15-2011 12"27 AM
0
 
ChLaAuthor Commented:
I went back to the last version of Get Creation Date and tested. It is perfect except for the hours being one hour early. Dates not changed. I removed " minus Bias ". It got worse again. Some dates 1 day earlier. Times four hours earlier..
0
 
ChLaAuthor Commented:
oops, I meant times four hours later, at the end of the last comment
0
 
systanCommented:
hi
please really check the code carefully.

in my test here, it does really change all the data and time, but 1 thing i recognize that in the seconds it change plus 1

i think because of the time bias
0
 
ChLaAuthor Commented:
JamShellList doesn't show me seconds. I have been copying and pasting your functions. I don't see how I screwed it up. But I will look more closely. It seems it only skips redating files when I am copying tiny MIDI files. Why would it skip some ?
0
 
ChLaAuthor Commented:
Can this be a Windows 7 or Delphi 2010 anomaly ?
0
 
ChLaAuthor Commented:
I should mentio nI tried the code from Orcbighter and had troubles with it such as unrecognized commands or properties.
0
 
ChLaAuthor Commented:
Hi everyone,
I had asked another question about how to also get the last modified date. Ewangoya give me some code for that that also seems to work perfectly to solve this question. So I am now using that solution and now consider this question closed. Thank you everyone.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.