14

I'm using free-form dates as part of a search syntax. I need to parse dates from strings, but only preserve the parts of the date that are actually specified. For instance, "november 1, 2010" is a specific date, but "november 2010" is the range of dates "november 1, 2010" to "november 30, 2010".

Unfortunately, DateTime.Parse and friends parse these dates to the same DateTime:

DateTime.Parse("November 1, 2010") // == {11/1/2010 12:00:00 AM}
DateTime.Parse("November, 2010") // == {11/1/2010 12:00:00 AM}

I need to know which parts of the DateTime were actually parsed and which were guessed by the parser. Essentially, I need DateTime.Parse("November, 2010") == {11/-1/2010 -1:-1:-1}; I can then see that the day portion is missing and calculate the range of dates covering the whole month.

(Internally, C# has the DateTimeParse and DateTimeResult classes that parse the date and preserve exactly the information I need, but by the time the date gets back to the public interfaces it's been stripped off. I'd rather avoid reflecting into these classes, unless that's really the only route.)

Is there some way to get DateTime.Parse to tell me which format it used to parse the date? Or can the returned DateTime have placeholders for unspecified parts? I'm also open to using another date parser, but I'd like it to be as reliable and locale-flexible as the internal one. Thanks in advance.

EDIT: I've also tried ParseExact, but enumerating all of the formats that Parse can handle seems nearly impossible. Parse actually accepts more formats than are returned by DateTimeFormatInfo.GetAllDateTimePatterns, which is about as canonical a source as I can find.

kevingessner
  • 18,559
  • 5
  • 43
  • 63

4 Answers4

2

You could try using TryParseExact(), which will fail if the data string isn't in the exact format specified. Try a bunch of different combinations, and when one succeeds you know the format the date was in, and thus you know the parts of the date that weren't there and for which the parser filled in defaults. The downside is you have to anticipate how the user will want to enter dates, so you can expect exactly that.

You could also use a Regex to digest the date string yourself. Again, you'll need different regexes (or a REALLY complex single one), but it is certainly possible to pull the string apart this way as well; then you know what you actually have.

KeithS
  • 70,210
  • 21
  • 112
  • 164
2

Parse parses a whole lot of stuff that no sane person would enter as a date, like "January / 2010 - 21 12: 00 :2". I think you'll have to write your own date parser if you want to know what exactly the user entered.

Personally I would do it like KeithS suggested: Parse the string with Parse and only call your own parse function if there's a 0 in one of the fields of the DateTime object. There are not that that possibilities you need to check for, because if the day is 0, the time will be 0, too. So start checking year, month, day, etc..

Or simply instruct the user to use specific formats you recognize.

grimmig
  • 1,391
  • 2
  • 14
  • 24
  • 1
    "Parse the string with Parse and only call your own parse function if there's a 0 in one of the fields of the DateTime object." -- Sadly, the day part of the DateTime can never be 0. It's set to 1 if unspecified, which can't be distinguished from it being specified as 1. – kevingessner Mar 05 '11 at 22:10
0

I used this method that goes back to the original string in order to check for existence of the day and the year:

  • For days, the original string must contain a 1 as integer if the day was specified. So, split the string and look for a 1. The only exception occurs when the month is January (#1 month), so you should check for two 1s or a 1 and "January" or "Jan" in the original string.
  • For years, the original string must contain a number that can be a year (say, from 1900 to 2100). Other possibilities may be the use of an apostrophe, or things like 02-10-16, which you can recognize by the fact that there are exactly three numbers.

I know that this is pretty heuristic, but it's a fast and simple solution that works in most cases. I coded this algorithm in C# in the DateFinder.DayExists() and DateFinder.YearExists() methods in the sharp-datefinder library.

okh
  • 470
  • 4
  • 9
0

Essentially, I need DateTime.Parse("November, 2010") == {11/-1/2010 -1:-1:-1}; I can then see that the day portion is missing and calculate the range of dates covering the whole month.

What you want is an illegal DateTime because you cannot have a negative hours/seconds/minute/day values. If you want to return something else other then a legal DateTime you have to write your own method which does NOT return a DateTime.

Is there some way to get DateTime.Parse to tell me which format it used to parse the date? Or can the returned DateTime have placeholders for unspecified parts? I'm also open to using another date parser, but I'd like it to be as reliable and locale-flexible as the internal one.

Take a look here http://msdn.microsoft.com/en-us/library/w2sa9yss.aspx

You are going to have to manually keep track of what is entered to do this task. The only solution is to make sure the input is in the correct format.

Community
  • 1
  • 1
Security Hound
  • 2,577
  • 3
  • 25
  • 42
  • 3
    I think the -1 specifier was just an example to say 'I need a method which signals which pieces of the DateTime it guessed at' not literally that he wanted a DateTime with -1 values there. – Michael Pryor Mar 01 '11 at 17:12
  • 1
    Michael Pryor is correct, I meant -1 as just a placeholder for unspecified parts. – kevingessner Mar 01 '11 at 17:26