IO.File.GetLastAccessTime выключен на один час - PullRequest
11 голосов
/ 05 января 2011

Я работаю над программой, которая записывает метаданные даты из файлов, такие как время создания, время последнего изменения и т. Д. Старая версия программы написана на VBA и выполняет что-то вроде этого:

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

Вывод для рассматриваемого файла:

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

Это значение, которое я получаю из свойств файла в Windows Explorer.Счастье.

Я портирую эту функцию в приложение VB.Net.Новый код:

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

Сама простота.Выход:

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

Через час.

Обе функции работают на одном компьютере, проверяя один и тот же файл.Я также пытался использовать класс IO.FileInfo с тем же результатом.Я проверил тысячи файлов, и все они отключены на один час.Другие свойства даты для времени создания и времени последнего изменения также отключены на один час.

Справка!

Я забыл упомянуть в исходном сообщении, часовой пояс компьютера - CST, а дневной светэкономия времени в настоящее время не действует.

Я воспроизвел проблему в 64-разрядной версии Windows 7 и 32-разрядной версии Windows XP.

Спасибо.

1/6/2011обновление:

Спасибо всем, кто предложил попробовать рассчитать желаемую дату из UTC, используя соответствующие смещения часового пояса.В настоящее время я решаю, что не стоит рисковать.Для этого конкретного бизнес-требования гораздо лучше сказать, что значение даты не соответствует ожидаемому, поскольку именно так работает API.Если я попытаюсь «починить» это, то я им владею, и я бы предпочел этого не делать.

Просто для удовольствия я попытался использовать старый добрый Scripting.FileSystemObject через interop.Он дает ожидаемые результаты, согласующиеся с Windows Explorer, с примерно пятикратным снижением производительности по сравнению с System.IO.Если выяснится, что я должен получить даты, которые совпадают с тем, что есть в Windows Explorer, я укушу пулю и пройду по этому пути.

Еще один эксперимент, который я пытался, заключался непосредственно в функции API GetFileTime в kernel32 через C #:

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

Это привело к тому же поведению, что и в System.IO, из-за отключения Windows Explorer на час.

Еще раз спасибо всем.

Ответы [ 10 ]

4 голосов
/ 18 февраля 2012

Я могу воспроизвести это, используя .NET 4.0 и 2.0 / 3.5 на XP / Win2k3 / Vista.

Проблема в том, что DST находился в другом состоянии, и сейчас .NET обрабатывает эту разницу иначе, чем в Explorer и Scripting.FileSystemObject.

(Подобная проблема возникает при извлечении файлов из zip-файла, когда летнее время отличается от времени, когда файлы были заархивированы.)

Через Reflector причина, по которой .NET отличается от путей кода, отличных от .NET, заключается в том, что все три локальные даты в IO.FileIO.FileInfo) реализованы как получение даты Utc и применение .ToLocalTime, которое определяет смещение, добавляемое в зависимости от того, происходило ли DST в местном часовом поясе для времени Utc .

Я также проверил, изменив дату на 1 июля прошлого года, создав файл и проследив отметку времени, когда я вернусь к текущей дате (16 марта) (я в Южной Австралии, поэтому сейчас мы находимся в летнее время) и не были 1 июля), и Windows (и, вероятно, FileSystemObject) добавляет DST вместо NOW , поэтому отображаемое время действительно меняется.

Итак, в целом, .NET является более правильным.

Но если вы хотите указать неверное, но такое же, как в Проводнике, используйте дату:

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

Это обсуждалось в блоге Раймонда Чена (где краткая сводка: .NET интуитивно верна, но не всегда обратима, а Win32 строго верна и обратима).

1 голос
/ 14 сентября 2012

Летнее время было виновником для меня. datDate содержал время, которое мне нужно было отрегулировать, но просто проверяя, является ли «Now ()» (или, как показано выше, никакой параметр не делает то же самое) DST, это исправило его.

If TimeZone.CurrentTimeZone.IsDaylightSavingTime(Now()) = True Then
    datDate = DateAdd(DateInterval.Hour, -1, datDate)
End If
0 голосов
/ 16 ноября 2017

Я также столкнулся с этой проблемой, и я думаю, что я понимаю, что происходит.

У меня есть скрипт Powershell и CMD (с помощью команды DIR), который сообщает об измененной дате / времени файлов.После недавнего изменения летнего времени на стандартное время сценарий Powershell сообщал о времени создания файлов до изменения времени на один час позже, чем команда DIR.Проводник Windows сообщил о том же времени, что и Powershell.

Поскольку временные метки файла NTFS хранятся в формате UTC, они преобразуются в местное время для отображения / вывода.Похоже, что команда DIR использует текущее смещение часового пояса от UTC при отображении времени (+8, поскольку я нахожусь в тихоокеанском часовом поясе, и сейчас это стандартное время), в то время как Powershell (и проводник Windows) используют смещение местного времениЭто действовало во время метки времени (когда создавались файлы), которая была UTC +7, так как это было все еще летнее время.

Я думаю, что преобразование Powershell является правильным.Я полагаю, что вы можете спорить в любом случае, но если вы посмотрите на метку времени до и после смены дневного времени, результаты Powershell будут такими же, а результаты DIR будут другими и непоследовательными.

0 голосов
/ 25 февраля 2012

VBA не позволяет работать с часовыми поясами, поэтому он должен использовать что-то стандартное из Win API. Я бы предложил эксперимент:

  • перейдите в свойства «Дата и время» и сначала убедитесь, что летнее время выключено, и сравните результаты
  • затем измените часовой пояс на несколько разных и посмотрите, как ведут себя оба приложения
  • выберите часовой пояс UTC и посмотрите, что произойдет

Вы когда-нибудь получите тот же самый последний доступ?

0 голосов
/ 25 февраля 2012

Вы можете определить смещение, используя следующий код:

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

Добавьте localOffset к вашему времени

0 голосов
/ 10 февраля 2011
Public Function GetLastAccessTime(ByVal FilePath As String) As Date
    Return IO.File.GetLastAccessTime(FilePath).ToLocalTime()
End Function
0 голосов
/ 06 января 2011

Как уже упоминалось, я думаю, что ответом на это является использование времени Utc, и, если вам нужно узнать, какое время «локально», рассчитайте местное время на основе перехода на летнее время в данный конкретный момент времени, используя текущая информация о культуре. Не совсем тривиально, но хотя бы обойтись?

0 голосов
/ 05 января 2011

Что вы получите, если будете использовать текущую культуру для форматирования вывода даты? Например,

Dim dt As DateTime = System.IO.File.GetLastAccessTime(FilePath)
Dim dateText As String = dt.ToString(System.Globalization.CultureInfo.CurrentCulture)
0 голосов
/ 05 января 2011

Хорошо ... Я не могу воспроизвести это на своем компьютере с Windows 7, но это звучит как проблема экономии дневного света.Вы можете попробовать что-то вроде:

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

Может потребоваться экспериментирование относительно того, следует ли складывать / вычитать ...

0 голосов
/ 05 января 2011

Попробуйте GetLastAccessTimeUtc, так как я подозреваю, что это проблема местного времени и летнего времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...