2011-01-05 23 views
11

Pracuję nad programem, który zapisuje metadane daty z plików, takich jak czas utworzenia, czas ostatniej modyfikacji itp. Stara wersja programu jest napisana w języku VBA, a robi coś takiego:IO.File.GetLastAccessTime jest wyłączony o godzinę

Public Function GetFileLastAccessTime(ByVal FilePath As String) As Date 
    Dim fso As New Scripting.FileSystemObject 
    Dim f As Scripting.File 
    Set f = fso.GetFile(FilePath) 
    GetFileLastAccessTime = f.DateLastAccessed 
End Function 

Output pliku w pytaniu:

?getfilelastaccesstime("SomePath") 
7/30/2010 2:16:07 PM 

to wartość dostaję od właściwości plików w systemie Windows Exploder. Szczęście.

Przenoszę tę funkcję do aplikacji VB.Net. Nowy kod:

Public Function GetLastAccessTime(ByVal FilePath As String) As Date 
    Return IO.File.GetLastAccessTime(FilePath) 
End Function 

Sama prostota. Dane wyjściowe:

?GetLastAccessTime("SomePath") 
#7/30/2010 3:16:07 PM# 

Godzinę później.

Obie funkcje działają na tym samym komputerze, sprawdzając ten sam plik. Próbowałem również używać klasy IO.FileInfo z tym samym wynikiem. Sprawdziłem tysiące plików i wszystkie są wyłączone o godzinę. Pozostałe właściwości daty dotyczące czasu utworzenia i czasu ostatniej modyfikacji również są wyłączone o jedną godzinę.

Pomoc!

Zapomniałem wspomnieć w oryginalnym poście, strefa czasowa komputera to CST, a czas letni nie jest obecnie dostępny.

Powtórzyłem problem w systemie Windows 7 w wersji 64-bitowej i w systemie Windows XP w wersji 32-bitowej.

Dzięki.

1/6/2011 zmiana:

Dziękujemy wszystkim, którzy je próbuje obliczyć żądaną datę z UTC używając odpowiednie przesunięcia strefy czasowej. W tej chwili decyduję, że nie jest to warte ryzyka. W przypadku tego konkretnego wymagania biznesowego znacznie lepiej jest powiedzieć, że wartość daty nie jest tym, czego oczekiwałeś, ponieważ tak działa interfejs API. Jeśli spróbuję to "naprawić", to jestem jego właścicielem, a wolałbym nie.

Tylko dla kopnięć Próbowałem używać starego dobrego Scripting.FileSystemObject przez interop. Daje oczekiwane rezultaty, które zgadzają się z Eksploratorem Windows, z około 5-krotnie wyższą wydajnością w porównaniu do System.IO. Jeśli się okaże, że muszę mieć daty pasujące do tego, co posiada Eksplorator Windows, ukąszę kulę i pójdę tą drogą.

Kolejny eksperyment Próbowałem jechał bezpośrednio do funkcji GetFileTime API w KERNEL32 poprzez C#:

[DllImport("kernel32.dll", SetLastError = true)] 
private static extern bool GetFileTime(
IntPtr hFile, 
ref FILETIME lpCreationTime, 
ref FILETIME lpLastAccessTime, 
ref FILETIME lpLastWriteTime 
); 

które doprowadziły w dokładnie tym samym zachowanie System.IO miał czas był wyłączony przez godzinę z Windows Explorer .

Jeszcze raz dziękuję wszystkim.

+2

Wygląda na to, że problem z czasem letnim oszczędza mi czas: – Flipster

+0

Czas ostatniego dostępu to ziarnistość wynosząca około 1 godziny. Należy również pamiętać, że domyślnie w systemie Windows Vista i nowszych wartość [Ostatni czas dostępu nie jest domyślnie aktualizowana w woluminach NTFS] (http://blogs.technet.com/b/filecab/archive/2006/11/07 /disabling-last-access-time-in-windows-vista-to-improve-ntfs-performance.aspx). –

+0

DST nie działa teraz, ale * działał *, gdy plik był ostatnio modyfikowany. Kto ma rację? Pracuj w UTC, aby uniknąć konieczności zadawania tego pytania. –

Odpowiedz

3

Mogę odtworzyć to przy użyciu .NET 4.0 i 2.0/3.5 na XP/Win2k3/Vista.

Problem jest w stanie DST w innym stanie niż obecnie, a .NET przetwarza tę różnicę inaczej niż Explorer i Scripting.FileSystemObject.

(Można zobaczyć podobny problem podczas pobierania plików z pliku zip, gdy strefa czasowa jest inna, gdy pliki zostały skompresowane.)

Via Reflektor, powodem .NET różni się od niezarejestrowanych ścieżkach kodu .NET jest że wszystkie trzy lokalne znaczniki daty w IO.File (i IO.FileInfo) są implementowane jako pobierające znacznik daty Utc i stosujące .ToLocalTime, który określa przesunięcie do dodania na podstawie tego, czy DST działo się w lokalnej strefie czasowej dla czasu Utc.

Sprawdziłem również, zmieniając datę na 1 lipca zeszłego roku, tworząc plik i oglądając znacznik czasu po powrocie do bieżącej daty (16 marca) (jestem w Południowej Australii, więc jesteśmy w DST teraz i nie było 1 lipca), a Windows (i prawdopodobnie FileSystemObject) dodaje DST na miejscu TERAZ, więc wyświetlany czas faktycznie się zmienia.

Podsumowując, .NET jest bardziej poprawny.

Ale jeśli chcesz niepoprawny, ale tak samo jak Explorer, datę zastosowanie:

Public Function GetLastAccessTime(ByVal FilePath As String) As Date 
    Return IO.File.GetLastAccessTimeUtc(FilePath) _ 
     .Add(TimeZone.CurrentTimeZone.GetUtcOffset(Now)) 
End Function 

ten został omówiony na Raymond Chen's blog (gdzie jest podsumowanie: NET jest intuicyjnie poprawne, ale nie zawsze odwracalna i Win32 jest ściśle poprawny i odwracalny).

0

Spróbuj GetLastAccessTimeUtc, ponieważ podejrzewam, że jest to kwestia oszczędności czasu lokalnego i czasu letniego.

0

Cóż ... Nie mogę tego odtworzyć na moim komputerze z systemem Windows 7, ale brzmi to jak kwestia oszczędności światła dziennego.Można spróbować coś takiego:

Dim dt As DateTime = System.IO.File.GetLastAccessTime(FilePath) 
    If Not dt.IsDaylightSavingTime() Then 
     dt.AddHours(1) 
    End If 

Eksperymenty mogą być wymagane, czy dodać/odjąć ...

+0

Zapomniałem wspomnieć w oryginalnym poście, strefa czasowa komputera to CST, a czas letni nie jest obecnie dostępny. Dzięki. –

+1

@Roger - Co się stanie, jeśli użyjesz SetBastAccessTime VB.Net, aby ustawić wartość ręcznie na pliku, a następnie odczytać wartość w VBA? Czy VBA i Eksplorator Windows wyświetlają się w tym samym czasie, co kod ustawiony w kodzie? Może to być ciekawy test. – Peter

0

Co otrzymasz, jeśli używasz bieżącej kultury formatowania wyjścia datę ?, np

Dim dt As DateTime = System.IO.File.GetLastAccessTime(FilePath) 
Dim dateText As String = dt.ToString(System.Globalization.CultureInfo.CurrentCulture) 
+0

Schludny pomysł! Niestety zwrócono ten sam nieprawidłowy czas, co poprzednio. –

0

Jak już wspomniano Myślę, że odpowiedź na to jest użycie czasu UTC, a jeśli trzeba wiedzieć, co czas, który jest „lokalnie” wypracować czasu lokalnego na podstawie czasu letniego w danym punkt w czasie przy użyciu aktualnej informacji o kulturze. Nie całkiem banalna, ale przynajmniej praca dookoła?

0
Public Function GetLastAccessTime(ByVal FilePath As String) As Date 
    Return IO.File.GetLastAccessTime(FilePath).ToLocalTime() 
End Function 
0

Można wykryć przesunięcie stosując następujący kod:

'... 
Dim datetimeNow as DateTime = DateTime.Now 
Dim localOffset as TimeSpan = datetimeNow - now.ToUniversalTime() 
'... 

dodać localOffset do czasu

0

VBA nie pozwala na skrzypce z stref czasowych, więc to musi być za pomocą czegoś standard od Win API.Proponuję eksperyment:

  • Przejdź do daty & właściwości czasu i najpierw upewnij się, Daylight Saving Time jest wyłączony i porównać wyniki
  • następnie zmienić strefę czasową na kilka różnych nich i zobaczyć, jak obie aplikacje zachowują
  • wybrać UTC Strefa czasowa i zobacz, co się stanie

Czy kiedykolwiek uzyskasz ten sam ostatni dostęp do czasu?

1

Oszczędności czasu letniego były dla mnie winne. datDate zawierał czas potrzebny do dostosowania, ale po prostu sprawdzając, czy "Now()" (lub jak pokazano powyżej, żaden parametr nie robi tego samego) jest DST, to naprawiło to.

If TimeZone.CurrentTimeZone.IsDaylightSavingTime(Now()) = True Then 
    datDate = DateAdd(DateInterval.Hour, -1, datDate) 
End If 
+0

Wiem, że prawie nigdzie nie ma przesunięcia oszczędności czasu dziennego innego niż 1 godzina, ale w połączeniu z '= Prawda' i prawie kliknąłem -1 razy, gdy patrzę na to ... –

0

Również napotkałem ten problem i uważam, że rozumiem, co się dzieje.

Mam skrypt Powershell i skrypt CMD (przy użyciu polecenia DIR), który zgłasza zmodyfikowaną datę/czas plików. Po ostatniej zmianie z czasu letniego na standardowy, skrypt Powershell zgłasza czas utworzenia plików przed zmianą czasu w godzinę później niż polecenie DIR. Eksplorator Windows zgłoszono w tym samym czasie co Powershell.

Ponieważ sygnatury czasowe plików NTFS są przechowywane jako UTC, są one konwertowane na czas lokalny wyświetlania/wyjścia. Wygląda na to, że polecenie DIR używa bieżącego przesunięcia strefy czasowej z UTC podczas wyświetlania czasu (+8, ponieważ jestem w strefie czasowej Pacyfiku i jest to teraz czas standardowy), podczas gdy Powershell (i Eksplorator Windows) używa lokalnego przesunięcia czasu TO BYŁO W EFEKTIE w momencie znacznika czasu (kiedy pliki zostały utworzone), który był UTC +7, ponieważ był to jeszcze czas letni.

Myślę, że konwersja Powershell jest poprawna. Przypuszczam, że możesz argumentować w dowolny sposób, ale jeśli spojrzysz na znacznik czasu przed i po zmianie czasu na światło dzienne, wyniki Powershell będą takie same, podczas gdy wyniki DIR będą różne i niespójne.

Powiązane problemy