Parsing the DateTime format to get Format String
Asked Answered
T

4

7

I would like to be able to get the format string from a DateTime string.

e.g.

"2012-12-08 15:00:00" => "yyyy-MM-dd HH:mm:ss"

"2013/30/01 16:00" => "yyyy/dd/MM HH:mm"

Is this possible?

Taxidermy answered 19/9, 2013 at 10:4 Comment(13)
why not not sure try this once DateTime.ParseExtract(string,"yyyy-MM-dd hh:mm:ss")Tucker
What would you expect the output to be for "07/06/2013 15:00:00"? (It could be month/day/year, or day/month/year.)Sheave
@Dotnet: No, that's the wrong way round - the point is to work out the format string from the data.Sheave
@Jon Skeet: I would expect it to figure it out based on the current environment locale.Taxidermy
@JonSkeet: That one is easy. How about 10-11-12 10:00? ;-)Ramiform
If this was possible, I could always tell if the yoghurt passed the "best served before" date or not... (without smelling, that is)Evalyn
@ronag: You should specify that in your question then.Sheave
@ronag: so you'd like to get the format string of the default locale?Evalyn
@ppeterka66: I would want it to get the format string as best effort with the current locale prioritized in case of ambiguous matches such as Jon Skeet's example.Taxidermy
What format string is this: "12"? I can tell you: yy. Or is it MM? There are myriad of variants and you can only guess what it is.Knowling
@TimSchmelter: That would be ambiguous and should fail...Taxidermy
@ronag: But "13" would be ok? Wait, is it yy or HH? In almost all cases you will have a problem. 12:12 can be HH:mm or mm:ss or ss:ff or even yy:MM or whatever.Knowling
@TimSchmelter: "best effort"...Taxidermy
S
10

It would be very hard to do this in a completely general way, but one option would be to extract the relevant DateTimeFormatInfo that you're interested in (using CultureInfo.DateTimeFormat), extract the culture-specific patterns from that (LongDatePattern, LongTimePattern etc), combine the patterns appropriately in some cases (e.g. ShortDatePattern space ShortTimePattern) and then try each pattern in turn using DateTime.TryParseExact - remembering to still specify the culture each time in order to handle date separators etc appropriately.

When DateTime.TryParseExact returns true, you know you've got a pattern which will parse the given text.

Sample code - including showing an example where you'd expect it to work but it doesn't:

using System;
using System.Collections.Generic;
using System.Globalization;

class Test
{
    static void Main()        
    {
        var us = new CultureInfo("en-US");
        var uk = new CultureInfo("en-GB");
        string text = "07/06/2013 11:22:11";

        // This one fails, as there's no appropriate time format
        Console.WriteLine(GuessPattern(text, us));
        // This one prints dd/MM/yyyy HH:mm:ss
        Console.WriteLine(GuessPattern(text, uk));
    }

    static string GuessPattern(string text, CultureInfo culture)
    {
        foreach (var pattern in GetDateTimePatterns(culture))
        {
            DateTime ignored;
            if (DateTime.TryParseExact(text, pattern, culture,
                                       DateTimeStyles.None, out ignored))
            {
                return pattern;
            }
        }
        return null;
    }

    static IList<string> GetDateTimePatterns(CultureInfo culture)
    {
        var info = culture.DateTimeFormat;
        return new string[]
        {
            info.FullDateTimePattern,
            info.LongDatePattern,
            info.LongTimePattern,
            info.ShortDatePattern,
            info.ShortTimePattern,
            info.MonthDayPattern,
            info.ShortDatePattern + " " + info.LongTimePattern,
            info.ShortDatePattern + " " + info.ShortTimePattern,
            info.YearMonthPattern
            // Consider the sortable pattern, ISO-8601 etc
        };        
    }
} 

You could potentially hard-code some "extra" date and time formats which you expect to work.

EDIT: To handle ambiguity, you could easily make GuessPattern return an IEnumerable<string> instead of a single string:

static IEnumerable<string> GuessPatterns(string text, CultureInfo culture)
{
    DateTime ignored;
    return GetDateTimePatterns(culture)
        .Where(pattern => DateTime.TryParseExact(text, pattern, culture,
                                             DateTimeStyles.None, out ignored))
    }
}
Sheave answered 19/9, 2013 at 10:13 Comment(4)
But even if it returns true there might be other patterns that also return true. Then it should fail as ambiguous as OP mentioned in the comments what makes it even harder to return something useful.Knowling
@TimSchmelter: Ah, I hadn't noticed there were additional requirements in the comments. Will edit to address those.Sheave
@JonSkeet Jon, your answer really looks good, but I tried it out for a personal use and it turns out that also the call to GuessPattern with the UK culture is fails. I Couldn't find why...Rectory
@YairNevet: Whoops, changed the value it was guessing for. Will edit back to sanity :)Sheave
M
2

You can have a set of predefined formats and parse the date and see if it passes, then you can get the string format you are looking for.

Refer to the answer but its in java - How to get the given date string format(pattern) in java?

Mazer answered 19/9, 2013 at 10:13 Comment(1)
I wonder why do I even try to answer a question which Jon Skeet is reading ;)Mazer
R
2

I had the same idea as Jon Skeet and went to implement it:

// Helper method
IEnumerable<string> DateTimeFormatPatterns(DateTimeFormatInfo format)
{
    var accessors = new Func<DateTimeFormatInfo, string>[]
    {
        f => f.FullDateTimePattern,
        f => f.LongDatePattern,
        f => f.LongTimePattern,
        f => f.MonthDayPattern,
        f => f.ShortDatePattern,
        f => f.SortableDateTimePattern,
        f => f.UniversalSortableDateTimePattern,
        f => f.YearMonthPattern,
    };

    return accessors.Select(accessor => accessor(format));
}

// The real function
string DetectDateTimeFormat(string date, CultureInfo culture)
{
    DateTime dummy;
    foreach (var pattern in DateTimeFormatPatterns(culture.DateTimeFormat))
    {
        if (DateTime.TryParseExact(date, pattern, culture,
                                   DateTimeStyles.None, out dummy))
        {
            return pattern;
        }
    }

    return null;
}

There is space for improvement here (e.g. the hardcoded DateTimeStyles.None doesn't help, an overload which assumes the current culture would also be useful), but you can use it like this:

var format = DetectDateTimeFormat("2012-12-08 15:00:00",
                                  CultureInfo.CurrentCulture);
Ramiform answered 19/9, 2013 at 10:24 Comment(1)
@ronag: Nice, I like the use of .SingleOrDefault too. Also, the helper method as I give it above is probably a liiiiiittle bit overengineered -- if those accessors are not being used anywhere else, it's much better to just build an array directly like Jon Skeet does.Ramiform
A
0

You can try by writing the below code to get yyyy/dd/MM HH:mm format.

DateTimeFormatInfo df1 = new DateTimeFormatInfo();
df1.SortTimePattern();

It would give you same HH:mm format.

Alleen answered 31/3, 2016 at 5:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.