.NET DateTime.Now возвращает неверное время при изменении часового пояса

эта проблема возникла во время изменения летнего времени. После изменения мы заметили, что наше серверное приложение начало записывать в журнал неправильное время-на час вперед, что означает, что .NET кэширует смещение часового пояса. Нам пришлось перезапустить приложение, чтобы решить эту проблему. Я написал простое приложение, чтобы воспроизвести эту проблему. Когда я изменяю часовой пояс во время работы приложения, DateTime.Теперь свойство продолжает производить время в старом часовом поясе. Делает кто-нибудь знает, есть ли обходной путь для этой проблемы, кроме перезапуска приложения?

4 ответов


да, текущий часовой пояс кэшируется. По веской причине это позволяет избежать проблем с разбитым кодом, который использует DateTime.Теперь для реализации измерения прошедшего времени. Такой код имеет тенденцию страдать сердечным приступом, когда время внезапно меняется на час или более.

вам придется вызвать систему.Глобализация.Свойство CultureInfo.ClearCachedData () для сброса кэшированного значения. Следующий вызов DateTime.Теперь теперь дадим новое местное время. Если вы вообще используете класс .NET 3.5 TimeZoneInfo вам также нужно будет вызвать его метод ClearCachedData (). Вы можете использовать SystemEvents.Событие TimeChanged в качестве триггера.


наиболее распространенная рекомендация-хранить DateTime.UtcNow и, если вы хотите показать пользователю локализованное время, конвертируйте в местное время учет летнего времени.

.NET обеспечивает расчеты с использованием летнего времени с DaylightTime и часовой пояс классов, и ToLocalTime метод предположительно может конвертировать UTC в локальный учет для летнего времени.


полный класс выше был немного выключен, но, возможно, он изменился в .NET 3.5.

System.Globalization.CultureInfo.CurrentCulture.ClearCachedData()

также не забудьте включить (в C#.NET) или импорт (с VB.NET) справочник по библиотеке System.Globalization.CultureInfo

вызовите его непосредственно перед использованием DateTime.Now. Хотя, вероятно, лучше всего назвать это в событии запуска вашего Global.файл asax.

========== Также убедитесь, что вы проверить часовой пояс на Windows сервере или локальном компьютере, в зависимости от того, где веб-сервер IIS запущен.


в моем проекте мне нужно было сбросить ряд переменных, если время (или часовой пояс) было изменено. Чтобы я мог получить факт, что это событие произошло, я в конечном итоге использовал WindowsMessageFilter.

Я использую .Net 2.0, поэтому я не мог использовать (или, может быть, я ищу в неправильных местах) ClearCachedData, поэтому я использовал этот подход с помощью небольшого отражения.

    Private mTZChangeFilter As WindowsMessageFilter


    mTZChangeFilter = New WindowsMessageFilter()
    AddHandler mTZChangeFilter.TimeChanged, AddressOf onTimeChanged

    Application.RemoveMessageFilter(mTZChangeFilter)


Public Class WindowsMessageFilter
    Implements IMessageFilter

    <System.Diagnostics.DebuggerStepThrough()> _
    Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean Implements System.Windows.Forms.IMessageFilter.PreFilterMessage
        ' Debug.Print(m.Msg.ToString)
        If m.Msg = 30 Then
            ResetTimeZone()
            RaiseEvent TimeChanged(Me)
        End If
    End Function

    Private Sub ResetTimeZone()
        Dim tz As Type = GetType(System.TimeZone)
        Dim mth As System.Reflection.MethodInfo

        Try
            mth = tz.GetMethod("ResetTimeZone", BindingFlags.NonPublic Or BindingFlags.Static)
            mth.Invoke(mth, Nothing)
        Catch ex As Exception
            Debug.Print(ex.ToString)
        End Try
    End Sub 

end class