2013-05-06 39 views
2

我有以下信息:时区转换到本地

string shippedTime = "10:53 AM"; 
string shippedDate = "12/12/2012"; 
string shippedTimeZone = "(GMT-05:00) Eastern Time (US & Canada)"; 

string receivedTime = "10:45 AM"; 
string receievedDate = "12/13/2012"; 

我需要找到(当地时区)的发货时间和接收时间之间的差异小时,服用时区和夏令成帐户。这是我从外部跟踪系统获取的数据,这正是我所得到的。所以shipTimeZone格式是一种未知的 - 我不知道该系统中有多少个时区,以及确切的字符串格式是什么 - 或者说我无法保证格式将始终如一,从而使解析类型脆弱。

除了解析所有这些字符串以提取GMT校正和连接日期和时间外,是否还有其他库可供我们查看?或者更直接的方式来获得我所需要的?

+0

你试过'DateTime.Parse +一些字符串tricks'或[NodaTime(https://code.google.com/p/noda在问这个问题之前?)? – I4V 2013-05-06 20:38:12

+3

你可能想看看[Noda Time](http://code.google.com/p/noda-time/)([和它的博客](http://noda-time.blogspot.com/) ))进行转换和正确的夏令时。不过,我认为Noda Time不会解析脆弱的时区字符串。 – Jesse 2013-05-06 20:38:20

+0

+1看Noda时间。时区转换看起来似乎很简单......直到您开始查看边缘案例。 – 2013-05-06 20:46:55

回答

2

这看起来像一个Windows时区,就像你用TimeZoneInfo得到的那种,但服务给你的是DisplayName属性而不是Id。这使得使用起来有点麻烦。特别是因为显示名称是本地化的 - 你有英文名字。它在具有不同文化设置的计算机上看起来不同。

因为Windows显示名称总是显示标准偏移量,所以即使区域处于夏令时,也不能将偏移量从此处移出。

基于它提到“GMT”而不是“UTC”的事实,我猜测它们来源于较早的计算机,如Windows XP,Windows Server 2003或Windows Embedded计算机。例如,您可以在this list上找到它。

真的应该回到该服务的业主,并要求他们给你的TimeZoneInfo.Id而非TimeZoneInfo.DisplayName值。假设你不能做到这一点,这里是你可能去获得这与你有什么工作的一种方式:

// Look up the time zone. This won't work on non-English language computers! 
// (setting an English CultureInfo won't help you here) 
var shippedTZ = TimeZoneInfo.GetSystemTimeZones() 
    .FirstOrDefault(x => x.DisplayName == shippedTimeZone.Replace("GMT", "UTC")); 
if (shippedTZ == null) 
    throw new Exception("Time Zone Not Found!"); 

// You may want to handle the exception by hardcoding some specific time zones 

// Let's turn your date/time strings into an actual DateTime 
var shippedDT = DateTime.ParseExact(shippedDate + " " + shippedTime, 
          "MM/dd/yyyy hh:mm tt", CultureInfo.InvariantCulture); 

// Then we'll use the time zone to get a DateTimeOffset. 
var shippedDTO = new DateTimeOffset(shippedDT, 
            shippedTZ.GetUtcOffset(shippedDT)); 

// And the same thing for the received date... 
var receivedTZ = TimeZoneInfo.Local; 
var receivedDT = DateTime.ParseExact(receievedDate + " " + receivedTime, 
          "MM/dd/yyyy hh:mm tt", CultureInfo.InvariantCulture); 
var receivedDTO = new DateTimeOffset(receivedDT, 
            receivedTZ.GetUtcOffset(receivedDT)); 

// Now you can subtract them 
TimeSpan elapsed = receivedDTO - shippedDTO; 

你需要知道这里的另一件事 - 如果日期/时间一方值是不明确的,你可能得不到正确的结果。这将发生在与夏令时相关的“后退”转换过程中。对此无能为力,因为源数据中没有任何合格的信息。

NodaTime - 而一个优秀的图书馆,对于您的特定问题,无法做得比这更好。如果没有正确的ID并且映射模糊的本地日期时间,您仍然会遇到同样的问题。但只是良好的措施,这里是使用NodaTime同样的事情:

// Try to locate the time zone 
var bclZoneProvider = DateTimeZoneProviders.Bcl; 
var zoneShipped = bclZoneProvider.Ids 
    .Select(x => bclZoneProvider[x]) 
    .Cast<BclDateTimeZone>() 
    .FirstOrDefault(x => x.DisplayName == shippedTimeZone.Replace("GMT", "UTC")); 
if (zoneShipped == null) 
    throw new Exception("Time Zone Not Found!"); 

// You wanted the system time zone 
var zoneReceived = bclZoneProvider.GetSystemDefault(); 

// Parse the date/time values as a LocalDateTime 
var pattern = LocalDateTimePattern 
        .CreateWithInvariantCulture("MM/dd/yyyy hh:mm tt"); 
var ldtShipped = pattern.Parse(shippedDate + " " + shippedTime).Value; 
var ldtReceived = pattern.Parse(receievedDate + " " + receivedTime).Value; 

// Assign them to the zones. 
// "Leniently" means to use the standard offset when there is an ambiguity. 
var zdtShipped = ldtShipped.InZoneLeniently(zoneShipped); 
var zdtReceived = ldtReceived.InZoneLeniently(zoneReceived); 

// Subtract them to get the Duration you are looking for. 
Duration elapsed = zdtReceived.ToInstant() - zdtShipped.ToInstant(); 
+0

这是一个很好的回应,谢谢Matt!当我读第一个例子(没有noda)时,我不得不笑,因为这非常接近我最终的结果。我注意到,用UTC替换GMT允许我从系统时区抢先或默认。从那里得到抵消是一个问题 - 我进一步考虑了一下,看看是否有任何一个日期时间是夏令时,如果是这样就加了一个小时。 – Nicros 2013-05-07 05:13:41

+1

DST已通过对GetUtcOffset的调用进行解释。不要多加一小时。 – 2013-05-07 12:37:05

+0

啊,很高兴知道!我将删除+1小时。谢谢! – Nicros 2013-05-14 03:33:39

相关问题