将日期字符串解析为某个时区(支持夏令时)

时间:2011-04-10 23:51:59

标签: c# timezone datetimeoffset

好的,过去几周我一直在努力工作,我遇到了一个小问题。我认为我的思想现在不能完成任务:)所以我需要一些提示/帮助!它可能很简单,但我的头还没有点击。

用户将在AEST中输入日期和时间。还有一个应用程序集“默认”时区(因为它可能需要更改),目前设置为“AUS东部标准时间”

因此我们在美国的服务器上有一个没有时区和定义的系统时区的用户字符串(因此本地不匹配,无法更改或使用)

现在我需要的是一种方式来说“使用时区X解析此用户输入的字符串”我不能只输入+10或+11作为偏移量,因为日期可能在夏令时之内或之外;即使对于相同的时区,肯定会在+10和+11之间进行更改!

当前的AEST时间也可能在DST之内或之外,因此我不能只将UTC日期转换为当前的AEST时间并获取“zzz”字符串并附加它,因为日期将关闭一小时从当前夏令时设置中输入的任何内容。

现在代码实际上就是这样:

TimeZoneInfo ConvTo = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["DefaultTimeZone"]);
DateTimeOffset getDate = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, ConvTo);
string TimeZoneId = " " + getDate.ToString("zzz");
DateTimeOffset cvStartDate = DateTimeOffset.MinValue; DateTimeOffset.TryParse(StartDate + TimeZoneId, out cvStartDate);

然后我通过检查它是否仍然== DateTimeOffset.MinValue或将其转换为UTC并添加到数据库来检查日期是否无效,它将在显示时转换回AEST。然而,有些日期是一小时,有些日期是完美的(如预期的那样):)

解决这个问题最优雅的方法是什么?

编辑:

为了帮助解释这个问题,我写了一些测试代码作为Windows测试应用程序:

// User entered date
string EnteredDate = "2011/01/01 10:00:00 AM";

// Get the timezone we want to use
TimeZoneInfo myTimeZone = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");

// Find the timezone string of the selected timezone to parse the user string
// This is the part that is incorrect and what i need help with.
DateTimeOffset getDate = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, myTimeZone);
string TimeZoneId = " " + getDate.ToString("zzz");

// Parse the string into the date object
DateTimeOffset cvEnteredDate = DateTimeOffset.MinValue; DateTimeOffset.TryParse(EnteredDate + TimeZoneId, out cvEnteredDate);

// Display
textBox1.Text += "Parsed: " + cvEnteredDate.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;

// Convert to UTC and display
cvEnteredDate = cvEnteredDate.ToUniversalTime();
textBox1.Text += "UTC: " + cvEnteredDate.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;

// Convert back to AEST and display
cvEnteredDate = TimeZoneInfo.ConvertTime(cvEnteredDate, myTimeZone);
textBox1.Text += "Changed Back: " + cvEnteredDate.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;

这是什么输出?

Parsed: 2011/01/01 10:00:00 +10:00
UTC: 2011/01/01 00:00:00 +00:00
Changed Back: 2011/01/01 11:00:00 +11:00

请注意小时偏离1,偏移量不同。另外,如果我们只是将输入的日期更改为:

string EnteredDate = "2011/04/20 10:00:00 AM";

我们得到:

Parsed: 2011/04/20 10:00:00 +10:00
UTC: 2011/04/20 00:00:00 +00:00
Changed Back: 2011/04/20 10:00:00 +10:00

这是非常好的,使用相同的代码只是一个不同的输入日期。

这是因为当前的DST设置和输入日期的DST设置不同,这就是我想要的解决方案:)

把它想象成鸡和蛋的问题。在解析之前我需要输入字符串的正确时区数据,这是我在解析字符串后才能得到的(这将是一个精心设计的解决方案)

或者我需要.NET使用myTimeZone对象解析字符串,因此它知道将其设置为自身,但我看不到任何执行此操作的函数,它们 all 已经取得了解析并设置datetime或datetimeoffset对象

所以我正在寻找别人可能做过的优雅解决方案?我当然不能成为唯一注意到这一点的人吗?

EDIT2:

好的,我做了一个“工作”功能,解决了我认为的问题,这是一个例子(在c#windows应用程序中添加一个文本框,并使用下面的代码来测试自己):

private void Form1_Load(object sender, EventArgs e)
{
    TimeZoneInfo myTimeZone = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");

    DateTimeOffset get1Date = ReadStringWithTimeZone("2011/01/01 10:00:00 AM", myTimeZone);
    textBox1.Text += "Read1: " + get1Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get1Date = get1Date.ToUniversalTime();
    textBox1.Text += "Read1 - UTC: " + get1Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get1Date = TimeZoneInfo.ConvertTime(get1Date, myTimeZone);
    textBox1.Text += "Changed Back: " + get1Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine + Environment.NewLine;

    DateTimeOffset get2Date = ReadStringWithTimeZone("2011/04/20 10:00:00 AM", myTimeZone);
    textBox1.Text += "Read2: " + get2Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get2Date = get2Date.ToUniversalTime();
    textBox1.Text += "Read2 - UTC: " + get2Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get2Date = TimeZoneInfo.ConvertTime(get2Date, myTimeZone);
    textBox1.Text += "Changed Back: " + get2Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine + Environment.NewLine;
}

public DateTimeOffset ReadStringWithTimeZone(string EnteredDate, TimeZoneInfo tzi)
{
    DateTimeOffset cvUTCToTZI = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tzi);
    DateTimeOffset cvParsedDate = DateTimeOffset.MinValue; DateTimeOffset.TryParse(EnteredDate + " " + cvUTCToTZI.ToString("zzz"), out cvParsedDate);
    if (tzi.SupportsDaylightSavingTime)
    {
        TimeSpan getDiff = tzi.GetUtcOffset(cvParsedDate);
        string MakeFinalOffset = (getDiff.Hours < 0 ? "-" : "+") + (getDiff.Hours > 9 ? "" : "0") + getDiff.Hours + ":" + (getDiff.Minutes > 9 ? "" : "0") + getDiff.Minutes;
        textBox1.Text += "Diff: " + MakeFinalOffset + Environment.NewLine;
        DateTimeOffset.TryParse(EnteredDate + " " + MakeFinalOffset, out cvParsedDate);
        return cvParsedDate;
    }
    else
    {
        return cvParsedDate;
    }
}

输出:

Diff: +11:00
Read1: 2011/01/01 10:00:00 +11:00
Read1 - UTC: 2010/12/31 23:00:00 +00:00
Changed Back: 2011/01/01 10:00:00 +11:00

Diff: +10:00
Read2: 2011/04/20 10:00:00 +10:00
Read2 - UTC: 2011/04/20 00:00:00 +00:00
Changed Back: 2011/04/20 10:00:00 +10:00

如果用户输入日期在DST的更改时间上是正确的,那么可能只会出现问题,因为它只是读取当前的偏移并使用它,然后检查它是否应该是夏令时与否,如果它在那里它会读错。然而,它比我现在的好几英里。

任何人都可以帮我清理这个功能吗?这是我需要的最佳路线吗?想法?

5 个答案:

答案 0 :(得分:14)

这是一个预定义格式的简单解决方案,也可以是动态的。我个人用它来与javascript交谈:

public DateTimeOffset ParseDateExactForTimeZone(string dateTime, TimeZoneInfo timezone)
{
    var parsedDateLocal = DateTimeOffset.ParseExact(dateTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
    var tzOffset = timezone.GetUtcOffset(parsedDateLocal.DateTime);
    var parsedDateTimeZone = new DateTimeOffset(parsedDateLocal.DateTime, tzOffset);
    return parsedDateTimeZone;
}

答案 1 :(得分:2)

当我需要将已知时区的日期时间字符串解析为本地时区时,这似乎对我有用。在很多情况下都没有测试它,但到目前为止,它在欧盟某个地方的服务器上解析时间非常有用。

TimeZoneInfo.ConvertTime(DateTime.Parse("2012-05-25 23:17:15", CultureInfo.CreateSpecificCulture("en-EU")),TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time"),TimeZoneInfo.Local)

答案 2 :(得分:1)

由于没有人提供任何更好的解决方案(这是非常令人惊讶的!)我接受我自己的功能作为答案,虽然我可能会使功能更简洁并稍后重新工作:

public DateTimeOffset ReadStringWithTimeZone(string EnteredDate, TimeZoneInfo tzi)
{
    DateTimeOffset cvUTCToTZI = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tzi);
    DateTimeOffset cvParsedDate = DateTimeOffset.MinValue;
    DateTimeOffset.TryParse(EnteredDate + " " + cvUTCToTZI.ToString("zzz"), out cvParsedDate);
    if (tzi.SupportsDaylightSavingTime)
    {
        TimeSpan getDiff = tzi.GetUtcOffset(cvParsedDate);
        string MakeFinalOffset = (getDiff.Hours < 0 ? "-" : "+") + (getDiff.Hours > 9 ? "" : "0") + getDiff.Hours + ":" + (getDiff.Minutes > 9 ? "" : "0") + getDiff.Minutes;
        DateTimeOffset.TryParse(EnteredDate + " " + MakeFinalOffset, out cvParsedDate);
        return cvParsedDate;
    }
    else
    {
        return cvParsedDate;
    }
}

答案 3 :(得分:0)

TimeZoneInfo有一个静态方法ConvertTimeToUtc,可让您指定日期时间和时区。这将为您进行时间调整,并返回UTC日期。

MSDN文档为http://msdn.microsoft.com/en-us/library/bb495915.aspx,完整的示例。

答案 4 :(得分:0)

最简单的解决方案: 首先将字符串解析为本地DateTime,使用任何解析方法 然后打电话

new DateTimeOffset(dateTimeLocal.Ticks, timezone.BaseUtcOffset)