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";

我需要考虑到时区和夏令时,以小时为单位计算发货时间和接收时间(本地时区)之间的差异。这是我从外部跟踪系统中提取的数据,这正是我得到的。所以 shippingTimeZone 格式有点未知——我不知道该系统中有多少个时区以及确切的字符串格式是什么——或者更确切地说,我不能保证格式总是相同的,这使得解析有点像脆弱的。

除了解析所有这些字符串以提取 GMT 更正和连接日期和时间之外,是否还有其他库我应该为此研究?或者更直接的方式来获得我需要的东西?

4

1 回答 1

2

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

您也不能只从那里取出偏移量 - 因为 Windows 显示名称始终显示标准偏移量,即使该区域处于夏令时也是如此。

基于它说“GMT”而不是“UTC”这一事实,我猜想它们来自旧计算机,例如 Windows XP、Windows Server 2003,或者可能是 Windows Embedded 计算机。例如,您会在此列表中找到它。

真的应该回到那个服务的所有者那里,要求他们给你价值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 - while an outstanding library, can't do a whole lot better than this for your particular problem. You will still have the same issues of resolving the time zone without the proper ID and mapping an ambiguous local datetime. But just for good measure, here is the same thing using 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();
于 2013-05-07T03:18:50.853 回答