Как преобразовать смещение строки в промежуток времени в С#

Я пытаюсь преобразовать время преобразования в часовой пояс пользователя, но у меня нет строки часового пояса Windows, такой как «Стандартное тихоокеанское время». Все, что у меня есть, это смещение строки, например "-07:00". Похоже, мне нужно создать временной интервал. Единственный способ разобрать эту строку вручную? Кажется, должен быть способ конвертировать время с использованием смещения строки, но, возможно, я что-то упускаю.

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

static void Main(string[] args)
{
    var currentTimeInPacificTime = ConvertUtcTimeToTimeZone(DateTime.UtcNow, "Pacific Standard Time");
    //TimeSpan ts = new TimeSpan("-07:00");
    Console.ReadKey();
}

static DateTimeOffset ConvertUtcTimeToTimeZone(DateTime dateTime, string toTimeZoneDesc)
{
    if (dateTime.Kind != DateTimeKind.Utc) throw new Exception("dateTime needs to have Kind property set to Utc");
    TimeSpan toUtcOffset = TimeZoneInfo.FindSystemTimeZoneById(toTimeZoneDesc).GetUtcOffset(dateTime);
    var convertedTime = DateTime.SpecifyKind(dateTime.Add(toUtcOffset), DateTimeKind.Unspecified);
    return new DateTimeOffset(convertedTime, toUtcOffset);
}

person KingOfHypocrites    schedule 17.08.2013    source источник


Ответы (4)


Вы можете просто использовать метод TimeSpan.Parse:

TimeSpan ts = TimeSpan.Parse("-07:00");
Console.WriteLine(ts);   // -07:00:00

Будьте осторожны, чтобы убрать начальный +, так как TimeSpan.Parse здесь не работает. +01:00 неверно, но 01:00 работает.

Или, если вы хотите быть немного более безопасным, попробуйте метод TimeSpan.TryParse. :

TimeSpan ts;
if (TimeSpan.TryParse("-07:00", out ts))
    Console.WriteLine(ts);   // -07:00:00

Но, конечно, если все, что вы хотите сделать, это преобразовать дату/время UTC в локальную дату/время, вы можете просто сделать это:

DateTime localDateTime = utcDateTime.ToLocalTime();

Или преобразовать его в любой часовой пояс:

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(toTimeZoneDesc);
DateTime localDateTime = TimeZoneInfo.ConvertTime(utcDateTime, tzi);
person p.s.w.g    schedule 17.08.2013
comment
Спасибо! Тоже так просто. Я не думал искать метод разбора. - person KingOfHypocrites; 18.08.2013
comment
TimeSpan.Parse правильно для этой части вопроса, но будьте осторожны с .ToLocalTime(), потому что это будет в часовом поясе компьютера, на котором он работает, который, вероятно, будет сервером в каком-то другом часовом поясе. Последний блок кода правильный, но он сказал, что у него нет идентификатора часового пояса для ввода. - person Matt Johnson-Pint; 18.08.2013
comment
Это не работает, если ваша строка смещения содержит символ +. Это должно быть удалено в первую очередь. - person PaulG; 20.03.2014

Для более сложных/нестандартных форматов вы также можете использовать TimeSpan.ParseExact(String, String, IFormatProvider), где вторая строка представляет собой строку пользовательского формата TimeSpan.

Информация об API доступна на сайте msdn.microsoft.com и находится по ссылке выше.linked.

person Andrew Ring    schedule 17.08.2013

Я пытаюсь преобразовать время преобразования в часовой пояс пользователя, но у меня нет строки часового пояса Windows, такой как «Стандартное тихоокеанское время». Все, что у меня есть, это смещение строки, например "-07:00".

Тогда у вас нет того, что вам нужно, чтобы сделать правильное преобразование. Прочтите «Часовой пояс! = Смещение» в вики тега часового пояса.

Важно понимать, что значение "Pacific Standard Time" является значением .Id для объекта TimeZoneInfo, который используется для тихоокеанского времени США. Он охватывает как тихоокеанское стандартное время (UTC-8), так и тихоокеанское летнее время (UTC-7).

Все, что у меня есть, это смещение строки, например "-07:00". Похоже, мне нужно создать временной интервал.

Теперь у вас есть то, что обычно называют проблемой XY. Вам не нужно работать со смещением само по себе.

В вашем коде есть вызов dateTime.Add(toUtcOffset). При преобразовании часовых поясов возникает запах кода, что вы делаете это неправильно. Вам никогда не придется вручную добавлять или вычитать время только для управления часовыми поясами. Это должно быть зарезервировано для фактического изменения момента времени, о котором вы говорите.

Что вы должны сделать, так это получить идентификатор реального часового пояса от вашего пользователя. Либо "Pacific Standard Time" для использования с TimeZoneInfo, либо "America/Los_Angeles" для использования с реализацией TZDB, такой как Noda Time.

Если преобразования часовых поясов не важны в вашем контексте, вы можете вместо этого просто собрать полное значение DateTimeOffset, например 2013-08-17T13:27:00.000-07:00.

person Matt Johnson-Pint    schedule 17.08.2013

Есть строки часового пояса, которые включают "Стандартное тихоокеанское время". Полный список можно найти здесь. http://www.xiirus.net/articles/article-_net-convert-datetime-from-one-timezone-to-another-7e44y.aspx

Любой объект DateTime можно преобразовать в некоторый часовой пояс -

    TimeZoneInfo timeZoneInfo; 
    DateTime dateTime ; 

    //Set the time zone information to Pacific Standard Time
    timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); 
    //Get date and time in US Mountain Standard Time 
    dateTime = TimeZoneInfo.ConvertTime(DateTime.Now, timeZoneInfo);
    //Print out the date and time
    Console.WriteLine(dateTime.ToString("yyyy-MM-dd HH-mm-ss")); 

Таким образом, ваш метод может быть изменен как -

static DateTimeOffset ConvertUtcTimeToTimeZone(DateTime dateTime, string toTimeZoneDesc)
{
   return new DateTimeOffset(TimeZoneInfo.ConvertTime(dateTime, TimeZoneInfo.FindSystemTimeZoneById(toTimeZoneDesc)));
}
person Aseem Gautam    schedule 17.08.2013
comment
Вы позволили местному часовому поясу проникнуть в преобразование, используя DateTime.Now, и снова передав Unspecified вид DateTime в конструктор DateTimeOffset. - person Matt Johnson-Pint; 18.08.2013
comment
@MattJohnson - это был просто пример, большинству людей достаточно подсказки. - person Aseem Gautam; 18.08.2013
comment
Может быть, пример того, как не делать это... И большинство людей просто копипастят, не задумываясь. - person Matt Johnson-Pint; 18.08.2013
comment
Поясню, последние две строки следует объединить в одну строку: return TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, timeZoneInfo);. Это вернет DateTimeOffset, где время и смещение верны для предоставленного timeZoneInfo. - person Matt Johnson-Pint; 18.08.2013
comment
Были некоторые опечатки, кроме того, что я не вижу никаких проблем с кодом. - person Aseem Gautam; 18.08.2013
comment
Это проблема с .Net API. Это заставляет вас думать, что все в порядке, когда это не так. Даже после ваших изменений это все еще неправильно. Возврат от TimeZoneInfo.ConvertTime при наличии DateTime будет DateTime с .Kind==DateTimeKind.Unspecified. Когда вы передадите это конструктору new DateTimeOffset(dateTime), он ошибочно предположит, что вам нужно локальное смещение. Таким образом, результирующий DateTimeOffset не будет иметь смещение, соответствующее желаемому TimeZoneInfo. - person Matt Johnson-Pint; 18.08.2013
comment
Вот почему я предпочитаю использовать Noda Time. Он не позволит совершать подобные ошибки. Это не твоя вина - извини, если я прозвучала резко. Этот подверженный ошибкам API придумала команда .Net BCL. - person Matt Johnson-Pint; 18.08.2013