Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Zeitumstellung "wegcodieren"
D. Annies - Sa 20.11.10 10:21
Titel: Zeitumstellung "wegcodieren"
Hi, Delpher,
jedesmal, wenn auf Sommer- Winterzeit umgestellt wird, werden (bisher) viele Dateien, deren Zeit sich genau um diese eine Stunde unterscheidet, mitkopiert, obwohl das ja unnötig ist.
Ich vermute, dass ich eine AND-Bedingung an der markierten Stelle ergänzen muss, aber welche und wie?
Der bisherige, funktionierende Code ist:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| if FileDateToDateTime(FileAge(work_filename)) <> FileDateToDateTime(FileAge(home_filename))
then begin erg := xmessagedlg('ArchivBit wurde gesetzt' +#13 + 'Soll ' + work_filename + ' '+#13+ datetimetostr(filedatetodatetime(FileAge(work_filename))) +#13+ 'durch ' + home_filename + ' '+#13+ datetimetostr(filedatetodatetime(FileAge(home_filename))) +#13+ ' ersetzt werden?', mtConfirmation, [mbYes, mbno, mbcancel], ['ja', 'nein', 'Abbrechen'], self.font); case erg of mryes : CopyFile(PChar(home_filename), PChar(work_filename), FALSE); mrcancel : raus := true; end; end; |
Danke, Detlef
F34r0fTh3D4rk - Sa 20.11.10 12:53
Ich weiß zwar nicht, was der Code tut (bzw. in welchen Intervallen er ausgeführt wird), aber würde es nicht reichen, zu prüfen, ob die Differenz größer als eine Stunde ist? Oder würde das den allgemeinen Fall beeinflussen?
D. Annies - Sa 20.11.10 14:12
Ja, das wäre genau richtig und ok, wenn ich sagen könnte: Wenn die Differenz (genau) eine Stunde beträgt, dann
nicht speichern [weil ja nichts verändert ist], nur neuer Zeitstempel gesetzt]
Gruß, Detlef
F34r0fTh3D4rk - Sa 20.11.10 14:32
Das gilt ja aber nur bei der Umstellung auf die Sommerzeit, bei der Winterzeit ist ja eine Stunde doppelt vorhanden.
Du könntest prüfen, um welches Datum es sich handelt. Wikipedia sagt:
| Zitat: |
Die Umstellung von der Normal- auf die Sommerzeit findet am letzten Sonntag im März um 1 Uhr UTC, also in der mitteleuropäischen Zeitzone von 2 Uhr MEZ auf 3 Uhr MESZ, statt.
Die Umstellung von der Sommer- auf die Normalzeit findet am letzten Sonntag im Oktober um 1 Uhr UTC, also in der mitteleuropäischen Zeitzone von 3 Uhr MESZ auf 2 Uhr MEZ, statt. |
Daran könntest du eine eventuelle Umstellung erkennen.
Kha - Sa 20.11.10 14:48
*hust* UTC *hust* ?
Solche fürchterlichen Hacks solltest du also umgehen können, indem du die
FileAge-Werte direkt vergleichst (ich gehe mal davon aus, dass Delphi die Umrechnung UTC->Local Time erst in FileDateToDateTime durchführt) :) .
/edit: Aber wie das Problem überhaupt entstehen kann, wenn beide Dateien gleichzeitig abgefragt werden, kann ich auch nicht erklären.
D. Annies - Sa 20.11.10 16:16
Und wie könnte es gehen?
Gruß, Detlef
Delete - Sa 20.11.10 16:39
Was willst du machen? Wenn du prüfen willst, ob sich eine Datei geändert hat, ist ein Hash besser geeignet.
D. Annies - Sa 20.11.10 19:13
Hi, Luckie,
aha, ein Hash - klingt gut, aber nie gehört. Na, da brauche ich noch weitere Informationen
Gruß, Detlef
D. Annies - So 21.11.10 10:50
Ich habe es jetzt so gemacht:
Delphi-Quelltext
1: 2: 3:
| if (FileDateToDateTime(FileAge(work_filename)) <> FileDateToDateTime(FileAge(home_filename))) and (fileage(work_filename) + 3600 <> FileAge(home_filename)) and (fileage(work_filename) <> FileAge(home_filename) + 3600) then |
Was meint ihr dazu?
Gruß, Detlef
jaenicke - So 21.11.10 11:14
Gibt es einen Grund warum du erstens teilweise ins Delphi-Datumsformat umwandelst und zweitens den Wert ständig wieder neu abfragst (also FileAge mehrfach für die selbe Datei aufrufst)?
Zudem ist FileAge veraltet und bringt zudem genau das genannte Problem mit der Sommerzeit mit sich, da du damit die Zeit explizit erst in die lokale Zeit umwandelst...
Nimm doch einfach FindFirstFile und benutze direkt ftLastWriteTime aus der entsprechenden TWin32FindData Struktur. Das ist erstens schneller und zweitens hast du dann direkt den UTC Zeitstempel, wenn es sich wie bereits geschrieben um ein NTFS Laufwerk handelt.
Eine Stunde sollte dabei 36.000.000.000 entsprechen (da der Zeitstempel die Anzahl der 100 Nanosekunden-Intervalle seit dem 01.01.1601 um 12 Uhr enthält).
D. Annies - So 21.11.10 15:12
Danke, Sebastian,
was ich gefunden habe ist der folgende Code:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47:
| procedure BuildFileList(aSourceDir, aDestDir: string; aSource, aFiles: TStringList; aMaxFileAge: Integer); cdecl; var sFileName, sSourceFileName: string; hSearch: THandle; rSourceFindData, rDestFindData: TWIN32FindData; ftCreationTime, ftLastWriteTime: TFileTime; begin aSourceDir := IncludeTrailingPathDelimiter(aSourceDir); aDestDir := IncludeTrailingPathDelimiter(aDestDir); aFiles.BeginUpdate; try for sFileName in aSource do begin sSourceFileName := aSourceDir + sFileName; hSearch := FindFirstFile(PChar(sSourceFileName), rSourceFindData); Windows.FindClose(hSearch); if (hSearch = INVALID_HANDLE_VALUE) or (rSourceFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then Continue; hSearch := FindFirstFile(PChar(aDestDir + sFileName), rDestFindData); Windows.FindClose(hSearch); if hSearch = INVALID_HANDLE_VALUE then begin aFiles.Add(sSourceFileName); Continue; end; if aMaxFileAge = 0 then begin if UInt64(rSourceFindData.ftLastWriteTime) > UInt64(rDestFindData.ftLastWriteTime) then aFiles.Add(sSourceFileName); end else begin FileTimeToLocalFileTime(rSourceFindData.ftCreationTime, ftCreationTime); FileTimeToLocalFileTime(rSourceFindData.ftLastWriteTime, ftLastWriteTime); if (DaysBetween(FileTimeToDateTime(ftCreationTime), Date) <= aMaxFileAge) or (DaysBetween(FileTimeToDateTime(ftLastWriteTime), Date) <= aMaxFileAge) then aFiles.Add(sSourceFileName); end; end; aFiles.Sort; finally aFiles.EndUpdate; end; end; |
Da blick ich so nicht durch! Was ist dabei für mich überflüssig?
Wie gesagt, ich will ja nur speichern, wenn die Datei verändert wurde.
(Ich sehe auch nicht, dass ich fileage mehrfach für die gleiche Datei aufrufe.)
Gruß, Detlef
jaenicke - So 21.11.10 15:15
D. Annies hat folgendes geschrieben : |
| (Ich sehe auch nicht, dass ich fileage mehrfach für die gleiche Datei aufrufe.) |
Beispiel für die erste der beiden Dateien:
D. Annies hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3:
| if (FileDateToDateTime(FileAge(work_filename)) <> FileDateToDateTime(FileAge(home_filename))) and (fileage(work_filename) + 3600 <> FileAge(home_filename)) and (fileage(work_filename) <> FileAge(home_filename) + 3600) then | |
3 Aufrufe ;-)
D. Annies - So 21.11.10 15:52
ok, ok.
jaenicke - So 21.11.10 15:56
So, hier einmal ein schnelles Beispiel, das direkt die Systemzeit benutzt und dementsprechend die UTC Zeit:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
| function GetFileTime(const FileName: string): TFileTime; var SearchHandle: THandle; FindData: TWin32FindData; begin Result.dwLowDateTime := 0; Result.dwHighDateTime := 0; SearchHandle := FindFirstFile(PChar(FileName), FindData); if SearchHandle <> INVALID_HANDLE_VALUE then begin Windows.FindClose(SearchHandle); if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then Result := FindData.ftLastWriteTime; end; end;
function IsFileOlder(const AFileName, AReferenceFileName: string): Boolean; var FileTime, ReferenceTime: TFileTime; begin FileTime := GetFileTime(AFileName); ReferenceTime := GetFileTime(AReferenceFileName); Result := FileTime.dwHighDateTime < ReferenceTime.dwHighDateTime; if not Result then Result := (FileTime.dwLowDateTime < ReferenceTime.dwLowDateTime) and (FileTime.dwHighDateTime = ReferenceTime.dwHighDateTime); end;
function IsFileOlderEx(const AFileName, AReferenceFileName: string; ADays: Int64 = 0; AHours: Int64 = 0; AMinutes: Int64 = 0; ASeconds: Int64 = 0): Boolean; var AllowedDiff: Int64; DiffLow, DiffHigh: Cardinal; FileTime, ReferenceTime: TFileTime; begin AllowedDiff := ((ADays * 24 + AHours) * 60 + AMinutes) * 60 + ASeconds; AllowedDiff := AllowedDiff * 10000000; DiffLow := AllowedDiff and $FFFFFFFF; DiffHigh := AllowedDiff shr 32; FileTime := GetFileTime(AFileName); ReferenceTime := GetFileTime(AReferenceFileName); Result := FileTime.dwHighDateTime + DiffHigh < ReferenceTime.dwHighDateTime; if not Result then Result := (FileTime.dwLowDateTime + DiffLow < ReferenceTime.dwLowDateTime) and (FileTime.dwHighDateTime + DiffHigh = ReferenceTime.dwHighDateTime); end; |
Bei IsFileOlderEx kannst du auch eine minimale Differenz angeben, damit die Funktion True zurückgibt.
Gerd Kayser - So 21.11.10 16:00
D. Annies hat folgendes geschrieben : |
| Wie gesagt, ich will ja nur speichern, wenn die Datei verändert wurde. |
Und warum wertest Du dann nicht einfach das Archiv-Bit aus? Wenn Archiv-Bit gesetzt, dann die Datei kopieren und anschließend das Archiv-Bit entfernen.
D. Annies - So 21.11.10 16:55
@ Sebastian: Ich bin begeistert!
Jetzt muss ich mir nur noch überlegen, wie ich die Differenz von einer Stunde ausrechne, um dann nicht zu speichern.
@Gerd: Das wirkt viel leichter! Ich weiß nicht, wie man ein Archiv-Bit setzt/ändert.
LG an euch, Detlef
jaenicke - So 21.11.10 17:07
D. Annies hat folgendes geschrieben : |
| Jetzt muss ich mir nur noch überlegen, wie ich die Differenz von einer Stunde ausrechne, um dann nicht zu speichern. |
Es wird doch ohnehin die Weltzeit genommen, so dass die Sommerzeit usw. ohnehin keine Rolle spielt.
Und die Berechnung der Stunde (und Sekunden usw.) habe ich doch schon drin (in IsFileOlderEx). ;-)
Delete - So 21.11.10 17:18
Gerd Kayser hat folgendes geschrieben : |
D. Annies hat folgendes geschrieben : | | Wie gesagt, ich will ja nur speichern, wenn die Datei verändert wurde. | Und warum wertest Du dann nicht einfach das Archiv-Bit aus? Wenn Archiv-Bit gesetzt, dann die Datei kopieren und anschließend das Archiv-Bit entfernen. |
Gefährlich, gefährlich. Nero setzt zum Beispiel beim Brennen das Archivbit zurück. Jetzt brenne ich Daten für meinen Kumpel, das Archivbit wird zurückgesetzt und schon sind sie beim nächsten Backup nicht mehr enthalten.
Und das mit dem letzten Änderungsdatum ist auch so eine Sache. Früher als man noch auf Disketten gespeichert hat, wurde beim Kopieren von der Diskette auf die Festplatte das Änderungsdatum auf das Datum zum Zeitpunkt des Kopierens gesetzt.
Wie gesagt, um zu entscheiden, ob sich eine Datei geändert hat, geht nur einigermaßen zuverlässig mittels eines direkten Vergleiches der Dateien. Und da hilft ein Hash.
D. Annies - So 21.11.10 18:02
@sebastian: Danke für deine erneuten Hinweise. Irgendwann blicke ich durch :))
@ Luckie: Ich finde nichts (brauchbares) über den Hash :((
Gruß, Detlef
Delete - So 21.11.10 18:17
D. Annies hat folgendes geschrieben : |
| @ Luckie: Ich finde nichts (brauchbares) über den Hash :(( |
Google: delphi + hash = 687.000 Treffer. Also wenn da nichts brauchbares bei, weiß ich es auch nicht. :roll:
D. Annies - So 21.11.10 22:44
Tut mir leid, aber ich komme nicht weiter.
(Bitte nicht fragen: "und wo ist dein Problem")
Detlef
jaenicke - So 21.11.10 23:41
und wo ist dein Problem? :mrgreen:
Für das Erstellen eines MD5 Hashs reicht es im Grunde die entsprechende Unit herunterzuladen und einzubinden und dann die MD5 Hash Erstellungsfunktion aufzurufen. :gruebel:
Gerd Kayser - Mo 22.11.10 07:11
D. Annies hat folgendes geschrieben : |
| @Gerd: Das wirkt viel leichter! Ich weiß nicht, wie man ein Archiv-Bit setzt/ändert. |
Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| procedure TForm1.Button1Click(Sender: TObject); var Search : TSearchRec; Verz : String; Liste : TStringList; Schleife : integer; begin Verz := 'L:\'; Liste := TStringList.Create; try if FindFirst(Verz + '*.*', faAnyFile, Search) = 0 then try repeat if (Search.Attr and faArchive) = faArchive then Liste.Add(Verz + Search.Name); until FindNext(Search) <> 0; finally SysUtils.FindClose(Search); end; for Schleife := 0 to Liste.Count - 1 do begin FileSetAttr(Liste.Strings[Schleife], FileGetAttr(Liste.Strings[Schleife]) and not faArchive); end; finally Liste.Free; end; Label1.Caption := 'fertig'; end; |
Übrigens: Nero 9 setzt das Archivbit beim Brennen NICHT zurück. Ich habs vorhin getestet.
D. Annies - Mo 22.11.10 09:55
Hi, Gerd,
ich setz das jetzt mal auf deine vorgeschlagene Weise um, werde mich aber wohl bis zur
nächsten Zeitumstellung gedulden müssen ...
Gruß, Detlef
Delete - Mo 22.11.10 12:30
Gerd Kayser hat folgendes geschrieben : |
| Übrigens: Nero 9 setzt das Archivbit beim Brennen NICHT zurück. Ich habs vorhin getestet. |
Wie es bei neuen Nero Versionen aussieht, kann ich nicht sagen. Aber bei älteren Versionen war es jedenfalls so. Nichts desto trotz sollte man sich nicht auf das Archivbit verlassen. Auch wenn es wohl ursprünglich genau für so was gedacht war.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!