1

I am writing a program in C# to convert between times from different countries (something like this). After trekking through Stack Overflow, most people seem to be interested in converting using time zone codes (such as from say GMT to EDT or EST to BST). This strikes me as a little odd because you need to then go through another hurdle to take DST complications into account.

What would be far simpler (and more practical, at least for my purposes) is to simply specify the country/city/state you wish to convert time from/to. If instead we just focus on local time for now, I have created these 2 functions to convert from Local time to FileTime (essentially UTC) and back again.

// Example use: convert_LocalToFile("1/11/2011 00:00:00") = 129645792000000000
long convert_LocalToFile(string time)
{
    DateTime dt = DateTime.Parse(time);
    return dt.ToFileTime();
}
// Example use: convert_FileToLocal(129645792000000000) = "1/11/2011 00:00:00"
string convert_FileToLocal(long time)
{
    return DateTime.FromFileTime(time).ToString();
}

However, it would be great if we solved this once and for all, and had functions which allowed you to specify the country/city too. The spec would be as follows:

// Example use 1: convert_AnyToFile("1/11/2011 00:00:00", "England") = 129645792000000000
// Example use 2: convert_AnyToFile("1/11/2011 00:00:00","New York") = 129645936000000000
long convert_AnyToFile(string time, string location) {
    ...
}

// Example use 1: convert_FileToAny(129645792000000000, "England") = "1/11/2011 00:00:00"
// Example use 2: convert_FileToAny(129645936000000000,"New York") = "1/11/2011 00:00:00"
string convert_FileToAny(long time, string location) {
    ...
}

So my question is two-fold: Can someone 'fill in' the above two empty functions to make them work, and also provide a way to get C# to list all the countries and cities that would be allowed as parameters.

------------------------EDIT: Instead of the country or city, I would also make do with the obscure (to me anyway) TZ codes as shown from this page: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones

As has been pointed out, I would then need to find a data file to map from the tz database code to the country/city/region. In this case, substitute "string location" in the above 2 function templates with "string code". 'code' meaning the tz database time zone code (TZ), not the typical time zone code (such as EST or GMT).

Dan W
  • 3,520
  • 7
  • 42
  • 69
  • 1
    that is exactly the reason timezones exist.... IF really really must do this via country/city then google for a list/DB of a mapping between country/city and TimeZone... hint: http://en.wikipedia.org/wiki/Tz_database – Yahia Nov 01 '11 at 05:18
  • Curious, I didn't even realize these kind of time zones you mentioned existed: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones. I thought only these did: http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations. Anyway, both would be very useful - I'd like my users to be able to enter countries/cities too as well as the obscure TZ or C.C code. – Dan W Nov 01 '11 at 05:43
  • You should go ahead and try to implement what you describe... and come back with specific questions if need be... – Yahia Nov 01 '11 at 06:13
  • If you can give at least a few pointers and C# keywords to use, as I wouldn't really know where to begin. If it means in the end I have to give the answer to my question, that's fine, but the DateTime class is pretty complex. – Dan W Nov 01 '11 at 06:59
  • please see my answer below... – Yahia Nov 01 '11 at 07:12

3 Answers3

1

As per comments some pointers:

For some conversion code see Creating a DateTime in a specific Time Zone in c# fx 3.5 and http://www.jarloo.com/c-convert-timezones/ .

For code to handle timezones as in tz_database (aka Olson...) see http://www.twinsun.com/tz/tz-link.htm and http://www.codeproject.com/KB/dotnet/Using_time_zones_in_NET.aspx .

MSDN references to relevant classes/methods etc.:

UPDATE

After reading about NodaTime (didn't even know it existed) I strongly recommend to use it - see the answer of Jon Skeet.

Community
  • 1
  • 1
Yahia
  • 69,653
  • 9
  • 115
  • 144
  • Thanks for the info. I can't use Jon's code or the code at Twinsun/Codeproject because of the large size. However, C#'s timezoneinfo looks promising. A list can be shown here: http://www.csharpdeveloping.net/Snippet/how_to_get_system_time_zones . However, do these take into account DST? I ask because they seem to be associated with single fixed UTC times and that implies that they don't take DST offsets into account automatically. Hopefully, I'm wrong and they act like Olson... – Dan W Nov 01 '11 at 22:44
  • 1
    @DanW it includes information about DST... please don't forget to upvote/mark as accepted any answer that was of help... – Yahia Nov 02 '11 at 04:15
  • Sure, just wanted to research first. I've posted my own answer on this now. Weird how Windows names like "Eastern Standard Time" take DST into account when of course it shouldn't. – Dan W Nov 03 '11 at 01:55
1

If you're content to use the zoneinfo/Olson names (e.g. "Europe/London", "America/Los_Angeles" etc) you could look at the project I started, Noda Time. It's generally a port of the Joda Time calendar engine, but with a new API to be more .NET-centric.

It's not at v1 yet due to a few missing features, but what's there should work well enough. Note that it addresses many of the deficiencies of DateTime, in particular distinguishing between a "local date/time" and a "zoned date/time" (and also coping with just a time or just a date). There are conversions to and from DateTime if you only want to use Noda Time for time zone conversions, of course - although I'd recommend using it pervasively within your code if you can. (And where it doesn't provide all the features you want, please let us know so that we can add them!)

Note that when treating a local time as being in a particular time zone, you need to be aware of the possibility that it could be either ambiguous or never existed. For example on Sunday the clocks went back in the UK, so 1:30am occurred twice. In spring, 1:30am wouldn't occur at all as the clocks go from 1am to 2am. Of course, Noda Time provides different options for handling this, but you do need to work out what you want to do in each case :)

Wizou
  • 1,336
  • 13
  • 24
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for the info Jon, and what you've created there looks great. However, 300-500k is a bit big for my needs considering my own software comes in at just over that. I'll look at Yahia's tips and see if I can fill in those empty functions I specified (how many lines of code will I need I wonder...). Anyway, hopefully the rest of the world will stop the madness and scrap DST eventually in the same way that Russia has recently :) – Dan W Nov 01 '11 at 20:45
  • @DanW: Is space *really* a concern for you? Is this for an embedded environment or something like that? The bulk of the DLL is the time zone data... note that you *could* use TimeZoneInfo, if you're happy to use the Windows system IDs. – Jon Skeet Nov 01 '11 at 21:17
  • Fraid so. Not embedded, it'd be for my own software called [OpalCalc](http://www.skytopia.com/software/opalcalc/). Adding Noda time would immediately more than double the size of the portable version :). Thanks for the recommendation of TimeZoneInfo. Hopefully I won't have to dip into win32 interop stuff. – Dan W Nov 01 '11 at 21:38
  • I've added my own answer now (not sure if you're notified of that automatically), which probably pales significantly in comparison to your library. Hopefully it'll be 'good enough' for my purposes though providing I can find that mapping from Windows names to cities/states/countries. – Dan W Nov 03 '11 at 01:58
1

As has been pointed out in a similar question, one needs to be careful since Windows' uses very misleading names (e.g.: the bizarre "Romance Standard Time" for Europe/Paris or "Eastern Standard Time" which strangely acts as ET and uses daylight saving time, when the official definition says otherwise. Not to mention US Eastern Standard Time).

Regarding mapping of cities/states/countries from Windows' Time Zones, I haven't yet found any data file for that, but I'll keep looking. I'm guessing one may need to find data to convert from Windows time zone names to Olson Time Zones, and then from there to individual cities/states/country names. This link will help with the former: http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html

With that in mind, I've filled in the empty functions from the main question (thanks to Jon and Yahia for the pointers). The first one is the master function and is the one that will be useful for most people. The other two are secondary functions which the first function uses.

I've checked them and they work for daylight saving time, although I bet Jon Skeet and others will be able to find some ahem, 'minor' flaws. For example, dates before around 1601 won't work as Windows' File Time doesn't support that. It's also recommended to wrap calls to these functions in try/catch statements of course to trap false date input etc.

// Master conversion function
// Example use 1: convertTimezone("1/11/2011 00:00:00", "GMT Standard Time", "Eastern Standard Time") = "31/10/2011 20:00:00"
// Example use 2: convertTimezone("1/11/2011 00:00:00", "Eastern Standard Time", "GMT Standard Time") = "01/11/2011 04:00:00"
// Example use 3: convertTimezone("1/10/2011 00:00:00", "Eastern Standard Time", "GMT Standard Time") = "01/10/2011 05:00:00"
string convertTimezone(string time, string oldlocation, string newlocation)
{
    long l = convertTime_AnyToFile(time, oldlocation);
    string newtime = convertTime_FileToAny(l, newlocation);
    return newtime;
}

// Example use 1: convertTime_AnyToFile("1/11/2011 00:00:00", "GMT Standard Time") =    129645792000000000
// Example use 2: convertTime_AnyToFile("1/11/2011 00:00:00","Eastern Standard Time") = 129645936000000000
long convertTime_AnyToFile(string time, string location)
{
    DateTime a = DateTime.Parse(time  );
    DateTime b = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(a, location, "UTC");
    return b.ToFileTime();
}

// Example use 1: convertTime_FileToAny(129645792000000000, "GMT Standard Time") = "1/11/2011 00:00:00"
// Example use 2: convertTime_FileToAny(129645936000000000,"Eastern Standard Time") = "1/11/2011 00:00:00"
string convertTime_FileToAny(long time, string location)
{
    DateTime a = DateTime.FromFileTimeUtc(time);
    DateTime b = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(a, location);
    return b.ToString();
}

Although it appears to work okay (under the limited tests I've done), before I 'tick' my own answer, I might wait for some feedback first as maybe someone can improve the above code.

Community
  • 1
  • 1
Dan W
  • 3,520
  • 7
  • 42
  • 69
  • I would personally *not* just use `DateTime.Parse` if you know the format you're expecting. Use `ParseExact`, potentially specifying `DatreTime.InvariantCulture` if it's a machine-readable format. It's also not really clear why you want "file time", rather than just returning a `DateTime` and letting the caller decide what to do with it, but that's your call. You also don't need the `SpecifyKind` call in the final method, as `FromFileTimeUtc` will always create a UTC value. (Continued...) – Jon Skeet Nov 03 '11 at 06:24
  • Finally, I'd change the method names to meet .NET naming conventions. But fundamentally, if you *can* use Windows system time zone IDs, then an approach like this should be fine :) – Jon Skeet Nov 03 '11 at 06:24
  • Thanks. I prefer Parse over ParseExact for my purposes as it allows the end user to be more flexible in how they define a date/time (so they can enter "9/10/11" or "10:00:00" or even "9/10 10:00". As for using FileTime, it's better for me to convert these DateTime objects to mere numbers to allow for easier calculation in the sum itself. You're right though, this could have been done by the caller. – Dan W Nov 03 '11 at 22:29
  • Yes, and people use different symbols too of course. .NET seems to handle it all transparently though. – Dan W Nov 03 '11 at 23:45
  • If you can be sure that the culture info of the current thread is correct, yes. – Jon Skeet Nov 04 '11 at 06:20