Создание DateTime в определенном часовом поясе в c #

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

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

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

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

Ответ 1

Джон ответ говорит о 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", чтобы прояснить ситуацию - я предпочитаю более короткие имена самостоятельно.

Ответ 2

Структура 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));

Ответ 3

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

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, который я недостаточно изучил.

Ответ 4

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

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);   
}
}

Ответ 5

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

  • Значение DateTime
  • a TimeZone object

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

Ответ 6

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

Я читаю значения из базы данных, и я знаю, в какой временной зоне находится база данных. Поэтому в ctor я перейду в часовой пояс базы данных. Но тогда я хотел бы получить значение в местном времени. Jon 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);
}

Ответ 7

Использование класса TimeZones позволяет легко создавать дату для определенного часового пояса.

TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById(TimeZones.Paris.Id));