Создание DateTime в определенном часовом поясе в c # - PullRequest
129 голосов
/ 29 октября 2008

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

В тесте мне нужно иметь возможность создавать объекты DateTime в не локальном часовом поясе, чтобы люди, выполняющие тест, могли делать это успешно, независимо от того, где они находятся.

Из того, что я вижу из конструктора DateTime, я могу установить TimeZone как местный часовой пояс, часовой пояс UTC или не заданный.

Как мне создать DateTime с определенным часовым поясом, таким как PST?

Ответы [ 6 ]

180 голосов
/ 29 октября 2008

Ответ Джона говорит о TimeZone , но я бы предложил вместо этого использовать TimeZoneInfo .

Лично мне нравится хранить вещи в UTC, где это возможно (по крайней мере, в прошлом; хранение UTC для будущее имеет потенциальные проблемы ), поэтому я бы предложил такую ​​структуру :

public struct DateTimeWithZone
{
    private readonly DateTime utcDateTime;
    private readonly TimeZoneInfo timeZone;

    public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
    {
        var dateTimeUnspec = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
        utcDateTime = TimeZoneInfo.ConvertTimeToUtc(dateTimeUnspec, timeZone); 
        this.timeZone = timeZone;
    }

    public DateTime UniversalTime { get { return utcDateTime; } }

    public TimeZoneInfo TimeZone { get { return timeZone; } }

    public DateTime LocalTime
    { 
        get 
        { 
            return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); 
        }
    }        
}

Вы можете изменить имена «TimeZone» на «TimeZoneInfo», чтобы сделать вещи более понятными - я предпочитаю более короткие имена.

43 голосов
/ 15 августа 2009

Структура DateTimeOffset была создана именно для этого типа использования.

См: http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx

Вот пример создания объекта DateTimeOffset с определенным часовым поясом:

DateTimeOffset do1 = new DateTimeOffset(2008, 8, 22, 1, 0, 0, new TimeSpan(-5, 0, 0));

31 голосов
/ 27 января 2012

Другие ответы здесь полезны, но они не охватывают, как получить доступ к Тихоокеанскому региону - вот вам:

public static DateTime GmtToPacific(DateTime dateTime)
{
    return TimeZoneInfo.ConvertTimeFromUtc(dateTime,
        TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
}

Как ни странно, хотя "тихоокеанское стандартное время" обычно означает нечто отличное от "тихоокеанского летнего времени", в данном случае оно относится к тихоокеанскому времени в целом. На самом деле, если вы используете FindSystemTimeZoneById для его извлечения, одним из доступных свойств будет bool, сообщающий вам, находится ли данный часовой пояс в летнее время или нет.

Вы можете увидеть более обобщенные примеры этого в библиотеке, которую я бросил вместе, чтобы иметь дело с DateTimes, которые мне нужны в разных часовых поясах, в зависимости от того, откуда пользователь спрашивает, и т. Д .:

https://github.com/b9chris/TimeZoneInfoLib.Net

Это не будет работать за пределами Windows (например, Mono в Linux), поскольку список времени поступает из реестра Windows: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\

Под ней вы найдете ключи (значки папок в редакторе реестра); имена этих ключей - то, что вы передаете FindSystemTimeZoneById. В Linux вы должны использовать отдельный стандартный для Linux набор определений часовых поясов, который я недостаточно изучил.

6 голосов
/ 03 апреля 2014

Я изменил Jon Skeet ответ немного для Интернета с методом расширения. Он также работает на лазурной как очарование.

public static class DateTimeWithZone
{

private static readonly TimeZoneInfo timeZone;

static DateTimeWithZone()
{
//I added web.config <add key="CurrentTimeZoneId" value="Central Europe Standard Time" />
//You can add value directly into function.
    timeZone = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["CurrentTimeZoneId"]);
}


public static DateTime LocalTime(this DateTime t)
{
     return TimeZoneInfo.ConvertTime(t, timeZone);   
}
}
2 голосов
/ 11 декабря 2013

Мне нравится ответ Джона Скита, но я хотел бы добавить одну вещь. Я не уверен, что Джон ожидал, что ctor будет всегда передаваться в местном часовом поясе. Но я хочу использовать это для случаев, когда это что-то отличное от местного.

Я читаю значения из базы данных, и я знаю, в каком часовом поясе находится эта база данных. Поэтому в ctor я передам часовой пояс базы данных. Но тогда я хотел бы получить значение по местному времени. Jon's LocalTime не возвращает исходную дату, преобразованную в дату местного часового пояса. Возвращает дату, конвертированную в исходный часовой пояс (все, что вы передали в ctor).

Мне кажется, эти имена свойств проясняют ...

public DateTime TimeInOriginalZone { get { return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); } }
public DateTime TimeInLocalZone    { get { return TimeZoneInfo.ConvertTime(utcDateTime, TimeZoneInfo.Local); } }
public DateTime TimeInSpecificZone(TimeZoneInfo tz)
{
    return TimeZoneInfo.ConvertTime(utcDateTime, tz);
}
2 голосов
/ 29 октября 2008

Для этого вам нужно создать собственный объект. Ваш пользовательский объект будет содержать два значения:

  • значение DateTime
  • a TimeZone объект

Не уверен, что тип данных, предоставленный CLR, уже существует, но по крайней мере компонент TimeZone уже доступен.

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