Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: bonmario - Aprile 02, 2019, 08:15:09 am

Titolo: [RISOLTO] Data ultima modifica di un file su rete Microsoft
Inserito da: bonmario - Aprile 02, 2019, 08:15:09 am
Ciao a tutti,
ieri mi sono imbattuto in un problema "strano". Non credo che sia a causa di Lazarus, perché lo steso programma su rete Novell non aveva questo problema. Purtroppo non conosco i dettagli delle 2 reti.
Provo a spiegarmi ...
Ho un programma che funziona da anni che, tra le altre cose, deve determinare la data/ora di creazione di parecchi files. Per ognuno, se la data/ora di creazione è maggiore di una certa data di riferimento, deve fare certe operazioni.

- Il 25 marzo, alle 12.00, ho creato un file.
- Il mio programma, quando è girato il 26 marzo, vedeva che quel file era stato creato il 25/03/2019 alle 12.00
- nel weekend c'è stato il cambio dell'ora
- Il mio programma, quando è girato ieri, vedeva che quel file era stato creato il 25/03/2019 alle 13.00, quindi un'ora dopo rispetto a quella effettiva

Qualcuno di voi si è mai imbattuto in questa "anomalia"?

Grazie in anticipo, Mario

P.S. Il mio programma determina la data di creazione, usando "FileDateToDateTime(DirInfo.Time);"
Ho provato anche usando la procedura qui sotto, ma il risultato è il medesimo

Codice: [Seleziona]
  function  DataUltimaModifica(const NomeFile:String;TipoRitorno:TipoData):TDateTime;
  var hFile:Integer;
      ftModifica, ftCreazione, ftAccesso:TFileTime;
      sysDateTime:_SYSTEMTIME;
  begin
    Result:=DimFileNonTrov;

    hFile:=0;
    try
      hFile:=CreateFile(PChar(NomeFile),
                        GENERIC_READ ,
                        FILE_SHARE_READ or FILE_SHARE_WRITE, //Faccio in modo di poter reperire la data
                        nil,                                 //anche dai files vincolati
                        OPEN_EXISTING,
                        FILE_FLAG_BACKUP_SEMANTICS,
                        0);
      if (GetLastError = 0) then begin
        GetFileTime(hFile, @ftCreazione, @ftAccesso, @ftModifica);
        if (GetLastError = 0) then begin
          FileTimeToLocalFileTime(ftModifica, ftModifica);
          FileTimeToSystemTime(ftModifica, sysDateTime);
          Result:=SystemTimeToDateTime(sysDateTime);

          Result:=FormattaData(Result, TipoRitorno);
        end;
      end;
    finally
      if (hFile <> 0) then begin
        CloseHandle(hFile);
      end;
    end;
  end;

Grazie in anticipo, Mario
Titolo: Re:Data ultima modifica di un file su rete Microsoft
Inserito da: nomorelogic - Aprile 02, 2019, 08:53:36 am
in NTFS le date sono memorizzate nel formato UTC che è un formato che non tiene conto dei fusi orari

se usi FileTimeToLocalFileTime o FileTimeToSystemTime questi orari ti verranno convertiti tenendo conto del fuso orario del paese dove ti trovi

non mi è mai capitato un problema del genere ma penso che dovresti utilizzare la funzione GetLocalTimeOffset che trovi in
https://www.freepascal.org/docs-html/rtl/sysutils/getlocaltimeoffset.html
in questo modo dovresti ottenere la data UTC con la quale fare i controlli che ti servono

Edit:
c'è anche questa unit che può essere utile
http://wiki.freepascal.org/PascalTZ
Titolo: Re:Data ultima modifica di un file su rete Microsoft
Inserito da: bonmario - Aprile 03, 2019, 08:08:44 am
Ciao,
grazie per la risposta, ma credo che il problema sia già a monte.

Ti faccio un esempio su un file preso a campione:
- se faccio la dir dal prompt di DOS mi da: 25/03/2019 13:43
- visto da Esplora risorse mi da: 25/03/2019 12:43
- visto con un altro file manager (Free Commander XE) mi da: 25/03/2019 13:43
- tutti i metodi che ho provato con Lazarus, mi danno: 25/03/2019 11:43

Credo però che "GetLocalTimeOffset" potrebbe tornarmi utile: al momento mi da come risultato -120. Al prossimo cambio dell'ora mi devo ricordare di riverificare il suo valore, e farci sopra qualche ragionamento.

Grazie, Mario
Titolo: Re:Data ultima modifica di un file su rete Microsoft
Inserito da: nomorelogic - Aprile 03, 2019, 05:23:51 pm
se sommi 120 minuti alle 11:43 ottieni 13:43

se sei sicuro che l'ora 13:43 è quella giusta sai che GetLocalTimeOffset funziona bene
Titolo: Re:Data ultima modifica di un file su rete Microsoft
Inserito da: bonmario - Aprile 04, 2019, 08:17:25 am
Purtroppo non è così ....

Sono riuscito a recuperare i log del programma che ha creato quel file: è stato creato alle 12.43.

Credo che se i test che sto facendo oggi, li avessi fatti prima del cambio dell'ora, mi avrebbe dato 11:43, con un offset di -60 minuti.

Quindi, in merito al mio post precedente, l'unico che da una risposta corretta sembra essere "Esplora Risorse".

Ciao, Mario
Titolo: Re:Data ultima modifica di un file su rete Microsoft
Inserito da: bonmario - Aprile 05, 2019, 02:27:11 pm
Ciao,
ho trovato la soluzione cercando su San Google "dst delphi", senza le virgolette.
dst sta per "Daylight Savings Time".


Codice: [Seleziona]
function  DataUltimaModifica(const NomeFile:String; TipoRitorno:TipoData):TDateTime;
var DirInfo: TSearchRec;
    UTCTime: TFileTime;
    GMTST: TSystemTime;
    LocalST: TSystemTime;
    ModifyDT: TDateTime;
    TZ: _TIME_ZONE_INFORMATION;
begin
  Result:=DimFileNonTrov;

  try
    if FindFirstUTF8(NomeFile, faAnyFile, DirInfo) = 0 then begin
      UTCTime:=DirInfo.FindData.ftLastWriteTime;
      if FileTimeToSystemTime(UTCTime, GMTST) then begin
         // Get Timezone Information
        if GetTimeZoneInformation(TZ) <> 0 then begin
          if SystemTimeToTzSpecificLocalTime(@TZ, GMTST, LocalST) then begin
            ModifyDT:=SystemTimeToDateTime(LocalST);
            Result:=FormattaData(ModifyDT, TipoRitorno);
          end;
        end;
      end;
    end;
  finally
    FindCloseUTF8(DirInfo);
  end;
end;

Ciao, Mario
Titolo: Re:[RISOLTO] Data ultima modifica di un file su rete Microsoft
Inserito da: xinyiman - Aprile 05, 2019, 04:55:22 pm
Ottimo, grazie della condivisione.
Titolo: Re:[RISOLTO] Data ultima modifica di un file su rete Microsoft
Inserito da: bonmario - Aprile 10, 2019, 01:48:16 pm
Ciao,
alla fine ho messo tutto in una unit, in modo da poterla usare ovunque mi dovesse servire.

Eccola:
Codice: [Seleziona]
unit BonMarGestDataOra;

{$mode objfpc}{$H+}

interface

uses LazFileUtils, LazUTF8, SysUtils, dateutils
  {$IFDEF UNIX}
    , Unix, unixutil
  {$ELSE}
    , windows
  {$ENDIF}
  ;

const CstConvDataErr=-1;

  function  UnixTimeToLocalTime(UnixFileTime: Int64; var WrkDateTime:TDateTime):Boolean;
  {$IFDEF MSWINDOWS}
  function  UTCFileTimeToLocalTime(UTCFileTime: TFileTime; var WrkDateTime:TDateTime):Boolean;
  {$ENDIF}
  function  DataUltimaModifica(const NomeFile:String):TDateTime;
  function  DataDiCreazione(const NomeFile: String): TDateTime;

implementation

var
    {$IFDEF UNIX}
      SegnaPosto:Integer; //Messa solo per non adre errori di compilazione in Linux
    {$ELSE}
      TZ: _TIME_ZONE_INFORMATION;
    {$ENDIF}



{$IFDEF UNIX}
  function  UnixTimeToLocalTime(UnixFileTime: Int64; var WrkDateTime:TDateTime):Boolean;
  var DiffTimeLinux:Longint;
  begin
    //Inizializzazione output
    Result:=False;
    WrkDateTime:=CstConvDataErr;

    //Trasformo la Data/Ora Unix in una data normale
    WrkDateTime:=UnixToDateTime(UnixFileTime);

    //Determino il DST tra la data che devo gestire ed oggi
    GetLocalTimezone(UnixFileTime);
    DiffTimeLinux:=Tzseconds;

    //Rettifico l'orario ed emetto il risultato
    WrkDateTime:=IncSecond(WrkDateTime, DiffTimeLinux);
    Result:=True;
  end;
{$ELSE}
  function  UnixTimeToLocalTime(UnixFileTime: Int64; var WrkDateTime:TDateTime):Boolean;
  var GMTST: TSystemTime;
      LocalST: TSystemTime;
      ModifyDT: TDateTime;
  begin
    //Inizializzazione output
    Result:=False;
    WrkDateTime:=CstConvDataErr;

    //Trasformo la Data/Ora Unix in una data normale
    WrkDateTime:=UnixToDateTime(UnixFileTime);

    //Trasformo la data/ora appena trovata in una di sistema
    DateTimeToSystemTime(WrkDateTime, GMTST);

    //Converto l'orario in base ai dati della TimeZone
    if SystemTimeToTzSpecificLocalTime(@TZ, GMTST, LocalST) then begin
      //Converto la data nel formato corretto
      ModifyDT:=SystemTimeToDateTime(LocalST);

      //Comunico al chiamante che la conversione è andata a buon fine,
      //e ladata/ora corretta
      WrkDateTime:=ModifyDT;
      Result:=True;
    end;
  end;
{$ENDIF}


{$IFDEF MSWINDOWS}
function  UTCFileTimeToLocalTime(UTCFileTime: TFileTime; var WrkDateTime:TDateTime):Boolean;
var GMTST: TSystemTime;
    LocalST: TSystemTime;
    ModifyDT: TDateTime;
begin
  //Inizializzazione output
  Result:=False;
  WrkDateTime:=CstConvDataErr;

  //Converto la data di un file in una data di sistema
  if FileTimeToSystemTime(UTCFileTime, GMTST) then begin
    //Converto l'orario in base ai dati della TimeZone
    if SystemTimeToTzSpecificLocalTime(@TZ, GMTST, LocalST) then begin
      //Converto la data nel formato corretto
      ModifyDT:=SystemTimeToDateTime(LocalST);

      //Comunico al chiamante che la conversione è andata a buon fine,
      //e ladata/ora corretta
      WrkDateTime:=ModifyDT;
      Result:=True;
    end;
  end;
end;
{$ENDIF}




{Restituisco la data & ora di ultima modifica.
 ATTENZIONE: DirInfo.Time restituisce la data di creazione, non modifica !!!}
{$IFDEF MSWINDOWS}
  {
  function  DataUltimaModifica(const NomeFile:String;TipoRitorno:TipoData):TDateTime;
  var hFile:Integer;
      ftModifica, ftCreazione, ftAccesso:TFileTime;
      sysDateTime:_SYSTEMTIME;
  begin
    Result:=CstConvDataErr;

    hFile:=0;
    try
      hFile:=CreateFile(PChar(NomeFile),
                        GENERIC_READ ,
                        FILE_SHARE_READ or FILE_SHARE_WRITE, //Faccio in modo di poter reperire la data
                        nil,                                 //anche dai files vincolati
                        OPEN_EXISTING,
                        FILE_FLAG_BACKUP_SEMANTICS,
                        0);
      if (GetLastError = 0) then begin
        GetFileTime(hFile, @ftCreazione, @ftAccesso, @ftModifica);
        if (GetLastError = 0) then begin
          FileTimeToLocalFileTime(ftModifica, ftModifica);
          FileTimeToSystemTime(ftModifica, sysDateTime);
          Result:=SystemTimeToDateTime(sysDateTime);
        end;
      end;
    finally
      if (hFile <> 0) then begin
        CloseHandle(hFile);
      end;
    end;
  end;
  }
  function  DataUltimaModifica(const NomeFile:String):TDateTime;
  var DirInfo: TSearchRec;
      UTCFileTime: TFileTime;
      WrkDateTime:TDateTime;
  begin
    Result:=CstConvDataErr;

    try
      if FindFirstUTF8(NomeFile, faAnyFile, DirInfo) = 0 then begin
        UTCFileTime:=DirInfo.FindData.ftLastWriteTime;

        if UTCFileTimeToLocalTime(UTCFileTime, WrkDateTime) then begin
          Result:=WrkDateTime;
        end;
      end;
    finally
      FindCloseUTF8(DirInfo);
    end;
  end;



  function  DataDiCreazione(const NomeFile: String): TDateTime;
  var DirInfo:TSearchRec;
      ftCreazione:TFileTime;
      WrkDateTime:TDateTime;
  begin
    Result:=CstConvDataErr;

    try
      if (FindFirstUTF8(NomeFile, faAnyFile, DirInfo) = 0) then begin
        ftCreazione:=DirInfo.FindData.ftCreationTime;

        if UTCFileTimeToLocalTime(ftCreazione, WrkDateTime) then begin
          Result:=WrkDateTime;
        end;

        {
        FileTimeToLocalFileTime(ftCreazione, ftCreazione);
        FileTimeToSystemTime(ftCreazione, sysDateTime);
        Result:=SystemTimeToDateTime(sysDateTime);
        }
      end;
    finally
      LazFileUtils.FindCloseUTF8(DirInfo);
    end;
  end;
{$ELSE}
  function  DataUltimaModifica(const NomeFile:String):TDateTime;
  var DataFile:Longint;
  begin
    DataFile:=FileAgeUTF8(NomeFile);

    //In caso di file non trovato, DataFile vale -1
    if (DataFile = -1) then begin
      Result:=CstConvDataErr;
    end else begin
      UnixTimeToLocalTime(DataFile, Result);
    end;
  end;



  function  DataDiCreazione(const NomeFile: String): TDateTime;
  var DataFile:Longint;
      DirInfo:TSearchRec;
  begin
    {
    //In linux la data di creazione di un file non viene salvata
    //https://www.cyberciti.biz/tips/understanding-unixlinux-filesystem-inodes.html

    => File type (executable, block special etc)
    => Permissions (read, write etc)
    => Owner
    => Group
    => File Size
    => File access, change and modification time (remember UNIX or Linux never stores
       file creation time, this is favorite question asked in UNIX/Linux sys admin job interview)
    => File deletion time
    => Number of links (soft/hard)
    => Extended attribute such as append only or no one can delete file including root
       user (immutability)
    => Access Control List (ACLs)

    All the above information stored in an inode.
    In short the inode identifies the file and its attributes (as above).
    Each inode is identified by a unique inode number within the file system.
    Inode is also know as index number.    }
    Result:=DataUltimaModifica(NomeFile);
  end;
{$ENDIF}






initialization
  //Questa operazione, se usata per ogni file, ad esempio perché sto elaborando un elenco,
  //potrebbe durare parecchio, quindi la lancio una volta sola
  {$IFDEF UNIX}
    ReadTimezoneFile(GetTimezoneFile);
  {$ELSE}
    GetTimeZoneInformation(TZ);
  {$ENDIF}

finalization;

end.

Ciao, Mario
Titolo: Re:[RISOLTO] Data ultima modifica di un file su rete Microsoft
Inserito da: xinyiman - Aprile 10, 2019, 02:11:10 pm
Ok, solo un consiglio spassionato. Non usare
Codice: [Seleziona]
{$IFDEF LINUX}
    , Unix, unixutil
  {$ELSE}
    , windows
  {$ENDIF}
ma usa
Codice: [Seleziona]
{$IFDEF UNIX}
    , Unix, unixutil
  {$ELSE}
    , windows
  {$ENDIF}

Così dovrebbe funzionare anche su mac. In quanto mac osx ha un kernel freebsd derivato.
Titolo: Re:[RISOLTO] Data ultima modifica di un file su rete Microsoft
Inserito da: bonmario - Aprile 10, 2019, 03:01:22 pm
Grazie per il consiglio ... devo solo farci l'abitudine: i miei programmi li uso a casa o al lavoro, su PC Windows o Linux, non avendoli mai compilati/usati su Mac, non ho mai avuto il problema.

P.S. Di solito faccio il contrario: if Windows ... else ... in quel caso mi sarebbe andata bene !!!

Ciao, Mario
Titolo: Re:[RISOLTO] Data ultima modifica di un file su rete Microsoft
Inserito da: bonmario - Aprile 11, 2019, 08:01:35 am
Ok, solo un consiglio spassionato. Non usare ....


Ciao,
ho "sistemato" il codice postato nel messaggio precedente seguendo il tuo consiglio e facendo un paio di piccole correzioni nel codice specifico per Linux/Unix.

Ciao, Mario