-2

In my view I am returning a string that is being converted into a DateTime in my controller. The string format is "ddd MMM dd HH:mm:ss 'EST' yyyy".

I am converting it successfully like so:

var startDate = DateTime.ParseExact(_startDate, "ddd MMM dd HH:mm:ss 'EST' yyyy", CultureInfo.InvariantCulture);
model.StartDate = Convert.ToDateTime(startDate);

The problem is that the time zones could be anything. how can I account for this? Its not always the case it will be 'EST' . If it is EDT for example it will throw an exception.

An example input string would be: Mon Feb 20 00:00:00 EST 2017

Edit: It also can be the format ddd MMM d HH:mm:ss 'EST' yyyy This will only be used in USA and Canada Time zones.

GlobalJim
  • 1,149
  • 2
  • 16
  • 24
  • 1
    Can you provide some example inputs – TheLethalCoder Mar 15 '17 at 13:43
  • 1
    Possible duplicate of [Parse DateTime with time zone of form PST/CEST/UTC/etc](http://stackoverflow.com/questions/241789/parse-datetime-with-time-zone-of-form-pst-cest-utc-etc) – Broots Waymb Mar 15 '17 at 13:43
  • 1
    Why the call to `Convert.ToDateTime` when the variable is *already* a `DateTime`? – Panagiotis Kanavos Mar 15 '17 at 13:48
  • I will never know the TimeZone prior to it being parsed in the controller. – GlobalJim Mar 15 '17 at 13:49
  • 1
    @GlobalJim - Why not? You clearly know the *exact* format of the string with the exception of those 3 characters. Just substring them out of `_startDate`.. – Broots Waymb Mar 15 '17 at 13:51
  • @DangerZone But then he is losing out the timezone... – xanatos Mar 15 '17 at 13:53
  • @DangerZone That's what I am doing now. – GlobalJim Mar 15 '17 at 13:53
  • @xanatos I am putting the TZ in a new var to use in the DateTime.ParseExact – GlobalJim Mar 15 '17 at 13:54
  • 1
    BTW, `EST` or `EDT` aren't timezone names. The only standardized timezone names are those in the IANA timezone database. It would be a *lot* easier if you used the ISO8601 format instead of using these abbreviations. – Panagiotis Kanavos Mar 15 '17 at 13:55
  • 1
    Otherwise, you'll have to use a lookup table either to IANA or to Windows timezone names, and use NodaTime or Windows functions to get the correct offset for a specific date. Some countries use different abbreviations for summer and winter time, some don't. You can't match an abbreviation to an offset – Panagiotis Kanavos Mar 15 '17 at 13:56
  • 1
    As an addendum to what Panagiotis wrote, there are abbreviations that are used by multiple different timezones ([CET](http://stackoverflow.com/a/22868721/613130) for example). – xanatos Mar 15 '17 at 13:58
  • @xanatos - I didn't mean to strip it out. Just to copy it to a variable so it can be known before the parse such that the post I linked above could solve OP's issue. – Broots Waymb Mar 15 '17 at 14:17
  • 1
    @DangerZone the only reliable answer in that post was posted by Jodrell which says "you can't do it, use IANA if possible". Everything else can fail as soon as a country changes its rules. It will also fail if one of the duplicate abbreviations are used. – Panagiotis Kanavos Mar 15 '17 at 15:14

2 Answers2

2

I'll quote from here:

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

(there is an explanation on the reason why you can't... I won't copy it here)

So what I can give you is a method to extract the timezone as a string, and then you'll do whatever you want (for example you could have a Dictionary<string, offset>... Not a very good idea but there could be worse, but see this comment: Russia changed DST rules many times in the past 4 years)

public static bool TryParseDateTimeWithTimeZone(string s, out DateTime result, out string timeZone)
{
    if (s == null)
    {
        throw new NullReferenceException("s");
    }

    int ixEnd = s.LastIndexOf(' ');

    if (ixEnd == -1 || ixEnd == 0)
    {
        throw new FormatException();
    }

    int ixStart = s.LastIndexOf(' ', ixEnd - 1);

    if (ixStart == -1)
    {
        throw new FormatException();
    }

    timeZone = s.Substring(ixStart + 1, ixEnd - ixStart - 1);

    string s2 = s.Remove(ixStart) + s.Substring(ixEnd);

    bool success = DateTime.TryParseExact(s2, "ddd MMM dd HH:mm:ss yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result);

    if (!success)
    {
        timeZone = null;
    }

    return success;
}

Use

DateTime dt;
string tz;

bool success = TryParseDateTimeWithTimeZone("Mon Feb 20 01:02:00 EST 2017", out dt, out tz);

Now tx == "EST" and dt is 2017-02-20 01:02:00 with Kind == Undefined.

Other connected answer for abbreviations: Timezone Abbreviations.

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
1

In my controller I do the following now.

 var startDateTZ = _startDate.Substring(20, 4);
        if (startDateTZ[3] == ' ')
        {
            startDateTZ = _startDate.Substring(20, 3);
        }
        if (startDateTZ.Length > 3)
        {
            if (startDateTZ[3] == '0')
            {
                startDateTZ = _startDate.Substring(19, 4);
            }
            if (startDateTZ[3] == '2')
            {
                startDateTZ = _startDate.Substring(19, 3);
            }
        }
        var startDate = new DateTime();
        if (_startDate[9] != ' ')
        {
             startDate = DateTime.ParseExact(_startDate, "ddd MMM dd HH:mm:ss '" + startDateTZ + "' yyyy", CultureInfo.InvariantCulture);
        }
        else
        {
             startDate = DateTime.ParseExact(_startDate, "ddd MMM d HH:mm:ss '" + startDateTZ + "' yyyy", CultureInfo.InvariantCulture);
        }
        model.StartDate = startDate;
GlobalJim
  • 1,149
  • 2
  • 16
  • 24