سؤال

I have the following information:

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

I need to find the difference between the shipped time and the received time (in local timezone) in hours, taking timezones and daylight savings into account. This is data that I have pulled from an external tracking system, and this is exactly what I get. So the shippedTimeZone format is kind of an unknown- I don't know how many time zones are in that system and what the exact string format is- or rather I can't guarantee the format will always be the same, making parsing kind of fragile.

Besides parsing all these strings to pull out the GMT correction and concatenating date and time, are there additional libraries that I should look into for this? Or a more straightforward way to get what I need?

هل كانت مفيدة؟

المحلول

That looks like a Windows Time Zone, like the kind you get with TimeZoneInfo, but the service gave you the DisplayName property rather than the Id. This makes using it a bit cumbersome. Especially since the display names are localized - you have the English name. It would look different on a computer with different culture settings.

You can't just take the offset out of there either - because the Windows display names always show the standard offset, even when the zone is in daylight savings time.

Based on the fact that it says "GMT" rather than "UTC", I would guess that they were sourced from an older computer, such as Windows XP, Windows Server 2003, or perhaps a Windows Embedded computer. For example, you'll find it on this list.

You really should go back to the owners of that service and ask them to give you the value of TimeZoneInfo.Id rather than TimeZoneInfo.DisplayName. Assuming you can't do that, here is one way you might go about getting this to work with what you have:

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

Another thing you need to be aware of here - if either one of the date/time values is ambiguous, you may not get the correct result. This will happen during the "fall-back" transition associated with Daylight Savings Time. There's nothing you can do about it, because you don't have any qualifying information in the source data.

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();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top