66

I'm trying to parse an international datetime string similar to:

24-okt-08 21:09:06 CEST

So far I've got something like:

CultureInfo culture = CultureInfo.CreateSpecificCulture("nl-BE");
DateTime dt = DateTime.ParseExact("24-okt-08 21:09:06 CEST",
    "dd-MMM-yy HH:mm:ss ...", culture);

The problem is what should I use for the '...' in the format string? Looking at the Custom Date and Time Format String MSDN page doesn't seem to list a format string for parsing timezones in PST/CEST/GMT/UTC form.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
thelsdj
  • 9,034
  • 10
  • 45
  • 58
  • 13
    For the record, time zone abbreviations such as EST, PST, CST are not standardized and are sometimes ambiguous (e.g. CST can mean both U.S. Central Standard Time GMT-6 and China Standard Time GMT+8). – dbkk Feb 23 '09 at 06:50
  • 2
    @dbkk and "Cuba Standard Time" Caribbean UTC -5 and "Central Standard Time" Central America UTC - 6 hours. http://www.timeanddate.com/library/abbreviations/timezones/ – Jodrell Apr 07 '14 at 08:29
  • I would suggest that you rather parse just the date, month, year, hour, minute, second and then add the offset hours (CEST) to the datetime object created. That would be much more stanadard. – Soundararajan Feb 16 '20 at 16:30

6 Answers6

44

AFAIK the time zone abbreviations are not recognized. However if you replace the abbreviation with the time zone offset, it will be OK. E.g.:

DateTime dt1 = DateTime.ParseExact("24-okt-08 21:09:06 CEST".Replace("CEST", "+2"), "dd-MMM-yy HH:mm:ss z", culture);
DateTime dt2 = DateTime.ParseExact("24-okt-08 21:09:06 CEST".Replace("CEST", "+02"), "dd-MMM-yy HH:mm:ss zz", culture);
DateTime dt3 = DateTime.ParseExact("24-okt-08 21:09:06 CEST".Replace("CEST", "+02:00"), "dd-MMM-yy HH:mm:ss zzz", culture);
Panos
  • 18,992
  • 6
  • 45
  • 54
  • 14
    Will this handle Daylight Time? – Vitaliy Ulantikov Mar 17 '13 at 11:09
  • 6
    No, it will not, since the offset is being specified manually here. – gzak Dec 23 '15 at 17:15
  • It will, provided you have a Replace call for all the timezones you might receive and the source date uses the correct timezone (e.g. doesn't use EST when they mean EDT) – thelem Jul 05 '18 at 16:00
  • 1
    so wait.. does it handle daylight time or not? – MIKE Oct 03 '18 at 20:43
  • 1
    @MIKE: Considering CEST (central european summer time) and CET (central european [winter] time) are not the same, then yes. However, a call to replace is risky, as you don't know if CEST/CET occurs in the date string for culture X. – Stefan Steiger Jan 21 '19 at 10:52
  • Hardcoded Timezone offset shouldn’t be used as due to day light savings it can change for the same Timezone. Consider https://stackoverflow.com/questions/246498/creating-a-datetime-in-a-specific-time-zone-in-c-sharp – Michael Freidgeim Jul 14 '22 at 22:22
35

The quick answer is, you can't do it.


Here is why,

There is a definitive database of world timezones, you can get it from the IANA here.

The problem is, the 3 or 4 letter abbreviations have a many-to-one association with the IANA timezones. For instance "AMT" means different things, depending on your culture, what part of the world you are in and the context of your application.

AMT "Armenia Time" Asia          UTC + 4 hours 
AMT "Amazon Time"  South America UTC - 4 hours 

If you really want to tackle this, I suggest using Noda Time to represent your Instances. You'll have to write some code to convert the abbreviations to a standard IANA timezone.

We can't do this for you, it depends on the context of your application.


Another good example is "CST".

CST "China Standard Time"   Asia            UTC + 8 hours 
CST "Central Standard Time" Central America UTC - 6 hours 
CST "Cuba Standard Time"    Caribbean       UTC - 5 hours 
CST "Central Standard Time" North America   UTC - 6 hours 
Jodrell
  • 34,946
  • 5
  • 87
  • 124
  • Technically this is still doable, since you can pass a `CultureInfo` object to the parser to disambiguate your context. – gzak Dec 23 '15 at 17:17
  • 2
    @gzak, yes, if you wanted the abbreviations localised to a passed culture you could work out the mappings. – Jodrell Mar 22 '16 at 08:41
  • 2
    No you couldn't. Culture info will tell you things about the user's language and cultural preferences. It will tell you nothing about a user's time zone. My culture might be `en-US` even if I'm visiting Japan. – Matt Johnson-Pint Oct 30 '17 at 15:27
  • 2
    @MattJohnson but if your culture is `en-US` we could infer that `CST` meant `"Central Standard Time" North America UTC -6` to you regardless of your timezone, even if it were `UTC-5`. Of course, what is appropriate for one application may be inappropriate for another. In abstract, you can't tell, as my answer states. With enough context, part of which would be he culture, we could tell. – Jodrell Oct 30 '17 at 16:01
  • 1
    "en-US" is intended to mean "English as spoken in the United States". In other words, US is referring to dialect, not location. Why would it be correct to infer Central Standard Time from CST just because I speak English in the way that Americans do? Sure, Americans say "CST", but we might also say that referring to China - especially if we happen to be in China. :) – Matt Johnson-Pint Oct 30 '17 at 16:07
  • @MattJohnson, as we are agreeing, additional context is necessary. – Jodrell Oct 31 '17 at 08:43
  • 1
    @MattJohnson Because as an United States English speaker in Japan using your device configured to your normal culture, you would probably not type CST in an application to mean something other than U.S. Central. Admittedly, it's not definitive, but it's okay as a heuristic. – jpmc26 Jan 10 '19 at 00:44
16

Dictionary of abbreviations if you decide to go the search&replace route (I did).

Dictionary<string, string> _timeZones = new Dictionary<string, string>() {
            {"ACDT", "+1030"},
            {"ACST", "+0930"},
            {"ADT", "-0300"},
            {"AEDT", "+1100"},
            {"AEST", "+1000"},
            {"AHDT", "-0900"},
            {"AHST", "-1000"},
            {"AST", "-0400"},
            {"AT", "-0200"},
            {"AWDT", "+0900"},
            {"AWST", "+0800"},
            {"BAT", "+0300"},
            {"BDST", "+0200"},
            {"BET", "-1100"},
            {"BST", "-0300"},
            {"BT", "+0300"},
            {"BZT2", "-0300"},
            {"CADT", "+1030"},
            {"CAST", "+0930"},
            {"CAT", "-1000"},
            {"CCT", "+0800"},
            {"CDT", "-0500"},
            {"CED", "+0200"},
            {"CET", "+0100"},
            {"CEST", "+0200"},
            {"CST", "-0600"},
            {"EAST", "+1000"},
            {"EDT", "-0400"},
            {"EED", "+0300"},
            {"EET", "+0200"},
            {"EEST", "+0300"},
            {"EST", "-0500"},
            {"FST", "+0200"},
            {"FWT", "+0100"},
            {"GMT", "GMT"},
            {"GST", "+1000"},
            {"HDT", "-0900"},
            {"HST", "-1000"},
            {"IDLE", "+1200"},
            {"IDLW", "-1200"},
            {"IST", "+0530"},
            {"IT", "+0330"},
            {"JST", "+0900"},
            {"JT", "+0700"},
            {"MDT", "-0600"},
            {"MED", "+0200"},
            {"MET", "+0100"},
            {"MEST", "+0200"},
            {"MEWT", "+0100"},
            {"MST", "-0700"},
            {"MT", "+0800"},
            {"NDT", "-0230"},
            {"NFT", "-0330"},
            {"NT", "-1100"},
            {"NST", "+0630"},
            {"NZ", "+1100"},
            {"NZST", "+1200"},
            {"NZDT", "+1300"},
            {"NZT", "+1200"},
            {"PDT", "-0700"},
            {"PST", "-0800"},
            {"ROK", "+0900"},
            {"SAD", "+1000"},
            {"SAST", "+0900"},
            {"SAT", "+0900"},
            {"SDT", "+1000"},
            {"SST", "+0200"},
            {"SWT", "+0100"},
            {"USZ3", "+0400"},
            {"USZ4", "+0500"},
            {"USZ5", "+0600"},
            {"USZ6", "+0700"},
            {"UT", "-0000"},
            {"UTC", "-0000"},
            {"UZ10", "+1100"},
            {"WAT", "-0100"},
            {"WET", "-0000"},
            {"WST", "+0800"},
            {"YDT", "-0800"},
            {"YST", "-0900"},
            {"ZP4", "+0400"},
            {"ZP5", "+0500"},
            {"ZP6", "+0600"}
        };
Jussi Palo
  • 848
  • 9
  • 26
  • 13
    Sorry, but this approach is unreliable. Any hardcoded list of abbreviation to offset is opinionated with regard to ambiguities, and is just a snapshot in time. – Matt Johnson-Pint Nov 16 '15 at 17:27
  • 9
    Of course it is. It is equally (un)reliable as using these non-standardized time zones in the first place. My answer is just backing up the accepted answer. Remember to down-vote that as well. – Jussi Palo Nov 17 '15 at 07:03
  • 6
    Some people (I'd go even further and say most) actually only receive data pertinent to a single culture. This approach, while inperfect, will suffice. – Vincent Vancalbergh Nov 17 '15 at 08:04
  • 3
    This *is* unreliable, even for a single culture. Russia changed DST rules many times in the past 4 years. Mapping the abbreviation to an IANA or Windows tz name and looking up the current rules is far more reliable – Panagiotis Kanavos Mar 15 '17 at 14:02
  • 1
    If given a choice between right some of the time and right none of the time, I'll take "some" of the time for the project I'm currently working on. Right now I'd have to punt without using a crutch like this which might get it wrong..... – boatcoder Sep 01 '21 at 00:57
3

I have two answers because I'm not exactly sure what you are asking.

1) I see you are using CultureInfo, so if you just want to format the date and time to be culture specific, I would separate the date/time and timezone, apply culture method on the date/time and append the timezone. If "CEST" is different for different cultures, you will have to change it by listing all the options (maybe in a case statement).

2) If you want date/time to be converted to another timezone, you can't use CultureInfo,

I suggest reading: http://msdn.microsoft.com/en-us/library/ms973825.aspx

You can also use the .net framework 3.5 class TimeZoneInfo (different from TimeZone) to make your life easier.

http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx

makstaks
  • 2,111
  • 4
  • 24
  • 38
2

This is how:

  1. Get the string (precondition: format: ddd, dd MMM yyyy HH:mm:ss zzz)
  2. Get the last whitespace
  3. Remove zzz from string, but save value of zzz
  4. Lookup offset for zzz
  5. Add offset to string
string dateString = reader.ReadContentAsString();
int timeZonePos = dateString.LastIndexOf(' ') + 1;
string tz = dateString.Substring(timeZonePos);
dateString = dateString.Substring(0, dateString.Length - tz.Length );
dateString += s_timeZoneOffsets[tz];

// https://msdn.microsoft.com/en-us/library/w2sa9yss(v=vs.110).aspx
//string es = reader.ReadElementString("pubDate");
this.m_value = System.DateTime.ParseExact(dateString, "ddd, dd MMM yyyy HH:mm zzz", System.Globalization.CultureInfo.InvariantCulture);

with

private static System.Collections.Generic.Dictionary<string, string> s_timeZoneOffsets =
    new System.Collections.Generic.Dictionary<string, string>() {
    {"ACDT", "+10:30"},
    {"ACST", "+09:30"},
    {"ADT", "-03:00"},
    {"AEDT", "+11:00"},
    {"AEST", "+10:00"},
    {"AHDT", "-09:00"},
    {"AHST", "-10:00"},
    {"AST", "-04:00"},
    {"AT", "-02:00"},
    {"AWDT", "+09:00"},
    {"AWST", "+08:00"},
    {"BAT", "+03:00"},
    {"BDST", "+02:00"},
    {"BET", "-11:00"},
    {"BST", "-03:00"},
    {"BT", "+03:00"},
    {"BZT2", "-03:00"},
    {"CADT", "+10:30"},
    {"CAST", "+09:30"},
    {"CAT", "-10:00"},
    {"CCT", "+08:00"},
    {"CDT", "-05:00"},
    {"CED", "+02:00"},
    {"CET", "+01:00"},
    {"CEST", "+02:00"},
    {"CST", "-06:00"},
    {"EAST", "+10:00"},
    {"EDT", "-04:00"},
    {"EED", "+03:00"},
    {"EET", "+02:00"},
    {"EEST", "+03:00"},
    {"EST", "-05:00"},
    {"FST", "+02:00"},
    {"FWT", "+01:00"},
    {"GMT", "+00:00"},
    {"GST", "+10:00"},
    {"HDT", "-09:00"},
    {"HST", "-10:00"},
    {"IDLE", "+12:00"},
    {"IDLW", "-12:00"},
    {"IST", "+05:30"},
    {"IT", "+03:30"},
    {"JST", "+09:00"},
    {"JT", "+07:00"},
    {"MDT", "-06:00"},
    {"MED", "+02:00"},
    {"MET", "+01:00"},
    {"MEST", "+02:00"},
    {"MEWT", "+01:00"},
    {"MST", "-07:00"},
    {"MT", "+08:00"},
    {"NDT", "-02:30"},
    {"NFT", "-03:30"},
    {"NT", "-11:00"},
    {"NST", "+06:30"},
    {"NZ", "+11:00"},
    {"NZST", "+12:00"},
    {"NZDT", "+13:00"},
    {"NZT", "+12:00"},
    {"PDT", "-07:00"},
    {"PST", "-08:00"},
    {"ROK", "+09:00"},
    {"SAD", "+10:00"},
    {"SAST", "+09:00"},
    {"SAT", "+09:00"},
    {"SDT", "+10:00"},
    {"SST", "+02:00"},
    {"SWT", "+01:00"},
    {"USZ3", "+04:00"},
    {"USZ4", "+05:00"},
    {"USZ5", "+06:00"},
    {"USZ6", "+07:00"},
    {"UT", "-00:00"},
    {"UTC", "-00:00"},
    {"UZ10", "+11:00"},
    {"WAT", "-01:00"},
    {"WET", "-00:00"},
    {"WST", "+08:00"},
    {"YDT", "-08:00"},
    {"YST", "-09:00"},
    {"ZP4", "+04:00"},
    {"ZP5", "+05:00"},
    {"ZP6", "+06:00"}
};
jpmc26
  • 28,463
  • 14
  • 94
  • 146
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • Thank you. Yes there were lots of bad answers. – JohnB Mar 30 '18 at 04:57
  • 2
    This is still a bad answer for reasons discussed elsewhere on this page. You are hardcoding assumptions about what abbreviations like "AMT" or "CST" mean. The problem is this question has no good answer; the codes are ambiguous, and no lookup table can be definitive. These assumptions may have sufficed to solve OP's problem, but they were not stated in the question. – Ryan Bemrose May 28 '18 at 20:52
  • 3
    Do not put "Necromancing" declarations in your posts. These are clutter; SO has no policy about activity on old questions. If anything, it is somewhat encouraged since new answers arise as technology changes. – jpmc26 Jan 10 '19 at 00:39
  • @jpmc26: I put those declaration in my posts when I answer an old question. The idea is, that people see that low-vote-count compared to other answers is due to that the answer is new, and not that the answer is bad. Though, it is true that one could already see this when looking at the answer-date - so consequently, that declaration is always redundant. – Stefan Steiger Nov 23 '21 at 12:25
  • @Ryan Bemrose: Actually, I'm assuming they are windows-timezones, so the abbrevations are unambiguous. It's true however, that IANA time-zones would be ambiguous, thus there would be no solution. Assuming the input values are from windows-machines, which would be true in 95% of cases of desktop machines, this works best. If you'd be running the code on a Linux-machine however, you'd first have to map the posix-timezones generated from C# to windows-timezones for lookup-purposes. I have the code for this somewhere, but if it's unambiguous, is a good question. – Stefan Steiger Nov 23 '21 at 12:39
  • Anybody, you might be interested in https://github.com/ststeiger/ews-managed-api/blob/master/Microsoft.Exchange.WebServices.Data.NetFramework4/TimeZoneData/TimeZoneTranslator.cs – Stefan Steiger Nov 23 '21 at 12:44
-1

Here's what I had to do.

I receive the datetime from javascript and then pass it on to ASP.NET to store in Oracle database. Here is my C# code for Eastern and Central times.

string datetimevalue = hidfileDateTime.Value; 

datetimevalue= datetimevalue.Replace("EDT", "EST"); 
datetimevalue = datetimevalue.Replace("CDT", "CST");
if (datetimevalue.Contains("CST"))
{
    filedt = DateTime.ParseExact(datetimevalue, "ddd MMM d HH:mm:ss CST yyyy", provider).ToUniversalTime().AddHours(1).ToLocalTime();
}
else
{
    filedt = DateTime.ParseExact(datetimevalue, "ddd MMM d HH:mm:ss EST yyyy", provider);
}
luke
  • 36,103
  • 8
  • 58
  • 81
Deep Kumar
  • 125
  • 1
  • 3