datetime.TryParseExact with different formats of values - c#

I have a json string that contains the values for a datetime and a parsing mechanism that looks like this:
if (DateTime.TryParseExact(TheUserTimeString, "M.d.yyyy.HH.mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out TheUserTime))
{
TheObject.UserDateTime = TheUserTime;
}
The string TheUserTimeString is generated on the client. It can be 12.20.2011.13.21 and the code works fine but when it's 12.20.2011.13.2 the code breaks because the minutes are in one digit. And when the month is also in one digit... who knows.
What would be a better way to rewrite this parsing code so that the string gets parsed correctly every time.
Thanks for your suggestions.

Use the string "M.d.yyyy.HH.m", a single m denotes minutes without the leading 0. Source.

Your DateTime format string just needs to be: "M.d.yyyy.H.m".
This allows for months, days, hours and minutes to be expressed as single digit values.
See here for the MSDN page documenting the valid formats of this string for further information.

Related

C# Converting to date from inconsistent string

I'm trying to parse a file where I get my dates as string like those:
5/18/2020 8:38:32 AM
6/8/2021 10:11:42 PM
11/24/2021 9:21:54 AM
----
I tried to use a DateTime.TryParse on my string and test the "---" case in a if statement which work but it succeed to convert only the 6/8/2021 12:41:56 PM.
I tried to use TryParseExact and specify a date format but it seem that I should make a case months with one and two digits and same the days.
I guess there is something I'm not seeing or don't know.
Thanks for you help.
It is because you are probably on a culture other than en-US which those dates are formatted in. Use IFormatProvider parameter. ie:
void Main()
{
var dates = #"5/18/2020 8:38:32 AM
6/8/2021 10:11:42 PM
11/24/2021 9:21:54 AM
----";
foreach (string s in dates.Split('\n'))
{
if (DateTime.TryParse(s, new CultureInfo("en-US"), DateTimeStyles.None, out DateTime d))
{
Console.WriteLine(d);
}
}
}
Here is the .Net fiddle link.
EDIT: Note that the version on .Net fiddle is slightly different because of the older C# version there.
Without the exact details, I have to guess:
If he only can translate the 6/8 date, you may have the wrong locale settings (you have something like MM/dd/yyyy .... but this works only for the 8th of june, what you might get wrong with 6th of august)
If you use ParseExact, you can also provide a list of valid format strings.
EDIT
Cetins answer is correct. Additionally, the title of the post is a bit confusing, because the datetime string IS consistent (in the 'en-US' local settings)
#Cetin Basoz You are right, it was indeed a culture problem. Thank you!
#nabuchodonossor You are right about the datetime string ("----" apart) being consistent. What I wanted to say was that there is no every time two digits for the days or months which would not happen if the dates were something like 05/18/2020 and 06/08/2021. Sorry, I have a hard time being precise.

ParseExact cannot parse a string in RFC 3339 Internet Date/Time format

It seems that C# does not manage to parse a time in a valid RFC 3339 format:
DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)
This line throws an exception, while this line works just fine:
DateTime.ParseExact("2019-12-31T00:00:00.1234567+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffzzz", null)
So it seems there is a limit on milliseconds, but I cannot find out any documentation on that. Is this how it is supposed to be?
The reason want to parse this date is that I have have an input date field. We use OAS (Swagger) date-time format that quite clearly says that any date in RFC 3339 Internet Date/Time format should be valid. Now from the spec here section 5.6
time-secfrac = "." 1*DIGIT
As far as I understand this means that up to 9 digits should be allowed and to be 100% compliant we have to allow these inputs, but it does not seem that C# even supports that.
Any ideas on how to fix it?
Per MSDN specification, you can use only fffffff
The fffffff custom format specifier represents the seven most
significant digits of the seconds fraction; that is, it represents the
ten millionths of a second in a date and time value.
In your first example
DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)
you are using fffffffff which is more precise for .NET custom date and time format strings
As far as I know, .NET supports seven most significant digits for milliseconds which is The "fffffff" custom format specifier are for.
The "fffffff" custom format specifier represents the seven most
significant digits of the seconds fraction; that is, it represents the
ten millionths of a second in a date and time value.
Although it's possible to display the ten millionths of a second
component of a time value, that value may not be meaningful. The
precision of date and time values depends on the resolution of the
system clock.
That means you are giving not meaningful data that are not supported for .NET Framework. I strongly suggest not doing that.
In addition to the information in the other answers, if you cannot change your input and you still want to parse it, you may use one of the following solutions:
If your input will always be in the same format (i.e., has 9 seconds-fraction digits), you could just remove the two extra ones and proceed to parse it:
string input = "2019-12-31T00:00:00.123456789+01:00";
input = input.Remove(27, 2);
// TODO: parse `input`.
If you don't know the number of the seconds-fraction digits beforehand, you may use something like this:
string input = "2019-12-31T00:00:00.123456789+01:00";
string format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFzzz";
var regEx = new Regex(#"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,7})\d*");
input = regEx.Replace(input, "$1");
DateTime parsedDate = DateTime.ParseExact(input, format, null);

DateTime.ParseExact - how to parse single- and double-digit hours with same format string?

I want to be able to parse strings of time (hours, minutes, seconds) where the hours run from 0 to 23, and where the preceding zero for one-digit hours is optional.
Examples of time strings that I want to be able to parse into valid DateTime objects:
212540
061525
94505
I am trying to use the C# method DateTime.ParseExact to manage the parsing, but I cannot for the life of it come up with a format string that can handle the "single-digit hour without preceding zero" scenario.
How should I specify the DateTime.ParseExact format string to sufficiently parse all examples above with the same line of code?
Inspired by the MSDN page on custom date and time formats, I have tried the following approaches:
DateTime.ParseExact(time_string, "Hmmss", CultureInfo.InvariantCulture);
DateTime.ParseExact(time_string, "%Hmmss", CultureInfo.InvariantCulture);
DateTime.ParseExact(time_string, "HHmmss", CultureInfo.InvariantCulture);
All these format strings work for the first two example cases above, but faced with a single-digit hour and no preceding zero, all formulations throw a FormatException.
You can insert delimiters between hours, minutes and seconds like this:
string timeString = "94505";
string formatedTimeString = Regex.Replace(str, #"\d{1,2}(?=(\d{2})+$)", "$&:");
var datetime = DateTime.ParseExact(formatedTimeString, "H:mm:ss", CultureInfo.InvariantCulture);
UPDATE:
I've found the cause of failure when parsing "94505" with format string "Hmmss":
What's happening is that H, m and s actually grabs two digits when they can, even if there won't be enough digits for the rest of the format. So the for example with the format Hmm and the digits 123, H would grab 12 and there would only be a 3 left. And mm requires two digits, so it fails.
So basically you have two options for handling the "single-digit hour without preceding zero" scenario:
Change time format: place hours to the end (for example, "ssmmH" or "mmssH") or use delimiters (for example, "H:mm:ss")
Modify the string like I've suggested earlier or like keyboardP has.
You could pad your input string if you know that you'll always have six characters.
string input = "94505";
if(input.Length < 6)
input = input.PadLeft(6, '0');
(Or use input.Length == 5 if you have other valid formats that are shorter).
What about using:
DateTime.ParseExact(time_string, "Hmmss", CultureInfo.InvariantCulture).ToString("HH:mm:ss")

DateTime problem when day <= 12

I've looked around a lot and short of writing a horrible chunk of code to manipulate the string, I'd like to ask if anyone knows a nice way of sorting it:
I have a bunch of date strings in cells that I'm pulling out such as:
03/05/2011
27/05/2011
31/05/2011
03/05/2011
09/05/2011
31/05/2011
etc.
While I'm reading any entires where the day can be construed as a month - i.e. entries 1, 4 and 5 above - it gets put in as a DateTime with the day and month swapped.
For example, 03/05/2011 gets read in as a DateTime "05/03/2011 00:00:00"
The others are all read and nicely provide me with a simple string of "27/05/2011".
I'm getting this info from Excel, using
((Excel.Range)worksheet.Cells[rowCount, 3]).Value.ToString()
If I try Value2 as with my other lines, it reads those odd dates as things like "40607" but again, will read the other dates normally.
If you use the DateTime.ParseExact function to convert a string to a DateTime object, you can specify the specific format used by your dates (which looks like "day/month/year") without having to do any string manipulation whatsoever.
Example:
var dateString = "03/05/2011";
var format = "dd/MM/yyyy";
var date = DateTime.ParseExact(dateString, format, CultureInfo.InvariantCulture);
More information on custom Date and Time format strings can be found here.
EDIT: Try using the DateTime.FromOADate method to convert the value returned by the Range.Value2 property to a DateTime object, e.g. something like this:
var dateTime = DateTime.FromOADate(((Excel.Range)worksheet.Cells[rowCount, 3]).Value2);
DateTime.ParseExact Method converts the specified string representation of a date and time to its DateTime equivalent using the specified format and culture-specific format information.
The format of the string representation must match the specified format exactly.
String dateString = "15/06/2008";
String format = "dd/MM/yyyy";
DateTime result =
DateTime.ParseExact(dateString, format, CultureInfo.InvariantCulture);
That sounds like a localization problem. Try setting your locale implicititly. For example in WPF application it's something like:
System.Threading.Thread.CurrentThread.CurrentCulture =
new System.Globalization.CultureInfo("en-US");
I have a bunch of date strings in cells that I'm pulling out such as:
No, you don't. You have a mix of strings that look like dates and dates that look like strings. This is an Excel issue, not a C# issue.
Not sure if you are creating the spreadsheet, or if you are getting it from somewhere else. But it the problem is that Excel attempt to parse text as it is entered in the cell. In this case, it is making some wrong decisions about the dates it finds.
If you enter a date like "03/05/2011", Excel will (incorrectly) parse it as March 5th, 2011, and store that as a numeric date code (40607). It then applies a date formatting to the cell (it uses m/d/yyyy on my machine).
If you enter a date like "31/05/2011", Excel can't parse it as a date, and it stores it as text.
To prove this, select the cells and go to Edit > Clear > Formats. All the "bad dates" will just show as numbers, all the rest will stay looking like dates.
You have a few choices:
Fix the data before its entered into Excel (prepend everything with a ' so its all entered as text, or make sure to create the spreadsheet on a machine that has the right date settings.)
Don't use the .Value.ToString() from Excel, just use .Text. This will ignore the bad parsing that Excel did, and should give you a consistent text value (from both types) that you can ParseExact with C#, per the other answers.
(2) is a lot easier, and if the spreadsheets already exist, may be your only choice.
The problem is because your Dates are being read as american culture or similar.
If you use the following you can specify the format you expect your dates to be in:use
DateTime result;
if(DateTime.TryParseExact("dd/MM/yyyy", out result))
{
// Got an English date
}

Parsing dates without all values specified

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.
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.
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.
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.
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.

Categories

Resources