I'm trying to retrieve a timespan from a string, but TryParseExact is returning false (fail).
I can't see what I'm doing wrong, can you help? I've tried 2 versions of my line in the code, both do not work.
TimeSpan.TryParseExact("04:00:01","HH:mm:ss",CultureInfo.CurrentCulture, out aTime)
and
TimeSpan.TryParseExact("04:00:01","HH:mm:ss", null, out aTime)
EDIT:
both responses here are correct, I have the wrong format for my custom timespan format - the mistake I made is to assume that the custom formats for DateTime would work for TimeSpans, but they do not.
The problem is simply in the format string for the TimeSpan, you have specified "HH:mm:ss". The specifier HH (upper case) is not valid for timespan. You should use hh. Format strings are indeed case sensitive.
The colon character (:) also needs to be escaped, so use "hh\\:mm\\:ss", #"hh\:mm\:ss" or "hh':'mm':'ss". All three forms will have the same effect.
You can review a list of valid custom format strings for TimeSpan here. and the standard format strings for TimeSpan are here.
While HH is valid for DateTime and DateTimeOffset where it represents the 24 hour clock and lower case hh represents a 12 hour clock, For TimeSpan - the hours component is always based on 24 hours. You would think that the HH format would be the one chosen, for uniformity, but nope - it's hh.
It's probably should get mentioned that you need to escape the colon character.
TryParseExact("04:00:01", "HH\\:mm\\:ss" ...
The string format which you are passing is wrong.
var res=TimeSpan.TryParseExact("04:00:01", "g", CultureInfo.CurrentCulture, out aTime);
g- General short format and is culture sensitive.
More on this here Standard Timespan Format Strings
Maybe you were using multiple formats.
public const string TimeFormat1 = "hh\\:mm";
public const string TimeFormat2 = "hh\\:mm:\\ss";
var parsed = TimeSpan.TryParseExact(time, new [] { TimeFormat1, TimeFormat2 }, CultureInfo.CurrentCulture, out TimeSpan ts1);
// parsed is always false
You might have thought you escaped your colon; but didn't, actually...
This "hh\\:mm:\\ss" won't work.
Using TimeFormat2 in ParseExact throws a FormatException...
You meant to use this "hh\\:mm\\:ss" instead.
Related
I am trying to convert string dates like 2020-01-14T17:01:48.757Z and 2020-01-14T17:01:50.760Z in to C# DateTime. Looks like my parsing is failing somewhere.
DateTimeOffset.ParseExact("2020-01-14T17:01:48.757Z", "yyyy-MM-dd'T'HH:mm:sszzz", CultureInfo.InvariantCulture).DateTime;
Whats wrong with above code ? It fails with
String '2020-01-14T17:01:50.760Z' was not recognized as a valid
DateTime.
When I parse same date online https://nsdateformatter.com/ It has no issues.
I even tried using yyyy-MM-dd'T'HH:mm:ssZ but it also gives above error.
Use this date format:
DateTimeOffset.ParseExact("2020-01-14T17:01:48.757Z", "yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture).DateTime;
2020-01-14T17:01:48.757Z
yyyy-MM-ddTHH:mm:ss.fffZ
As You see format corresponds to Your provided date string.
Looks like you forget to use proper format specifier for your milliseconds part and dot (.) between your seconds and milliseconds part.
The "fff" custom format specifier
The "fff" custom format specifier represents the three most
significant digits of the seconds fraction; that is, it represents the
milliseconds in a date and time value.
DateTimeOffset.ParseExact("2020-01-14T17:01:48.757Z",
"yyyy-MM-dd'T'HH:mm:ss.fffZ",
CultureInfo.InvariantCulture)
I'm parsing this date: "3/1/1961" with the following code:
DateTime d = DateTime.ParseExact(theStringDate, "d", lang.Culture);
I think everybody will agree with me that "3/1/1961" and "03/01/1961" are the same date. However, in the first case the code will crash and in the second it will not.
Why is C# behaving this way? Is there any way "3/1/1961" should not get interpreted correctly? How can I tell the compiler to ignore the absence of 0 before the number?
Forcing my user to write 0 before every number or using JS to force the presence of a 0 are both unacceptable solutions. What can I do?
What is the value of lang.Culture?
When the problem arised, I was testing from an italian browser so, I guess, it is "IT-it".
I am not sure how you are parsing with the current format d, it should be "d/M/yyyy"
string theStringDate = "03/01/1961";
DateTime d = DateTime.ParseExact(theStringDate, "d/M/yyyy", CultureInfo.InvariantCulture);
The format "d/M/yyyy" works for both single digit and double digits day/month.
You're using parse exact and the input string isn't in the format you specify. C# is not behaving weird, you're just misusing the method. This snippet is directly from msdn and exactly explains why you get an exception;
// Parse date-only value without leading zero in month using "d" format.
// Should throw a FormatException because standard short date pattern of
// invariant culture requires two-digit month.
dateString = "6/15/2008";
try {
result = DateTime.ParseExact(dateString, format, provider);
Console.WriteLine("{0} converts to {1}.", dateString, result.ToString());
}
catch (FormatException) {
Console.WriteLine("{0} is not in the correct format.", dateString);
}
There are several solutions available to you. Here are few off the top of my head; change your format specifier (I think replacing "d" with "g" will solve your problem in this case, you'll still get exceptions for other formats though), change DateTime.ParseExact to DateTime.Parse or DateTime.TryParse, or change your input so it's in the exact format you require.
Personally I'd recommend getting rid of ParseExact. Do you know exactly what format your date is going to be in? It seems like you don't. If you don't, why would you be using ParseExact?
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")
Can anyone explain why the following snippet returns true?
According to the docs for The "d" custom format specifier, "A single-digit day is formatted without a leading zero." So why doesn't TryParseExact fail when I give it a single-digit day with a leading zero?
DateTime x;
return DateTime.TryParseExact
(
"01/01/2001",
#"d\/MM\/yyyy",
null,
System.Globalization.DateTimeStyles.None,
out x
);
UPDATE
I think maybe I was unclear originally. What I am really trying to get at is: Why does TryParseExact accept some values that don't match exactly? from all of the documentation I have seen, 'd' matching '01' and '1' is just as much a bug as if 'MM' matched 'March' as well as '03'. The issue here isn't that the values are equivalent, its that they don't match the format.
The relevant snippets of documentation are:
From TryParseExact: The format of the string representation must match a specified format exactly.
From The 'd' Specifier: A single-digit day is formatted without a leading zero.
It seems abundantly clear to me that '01' has a leading 0, and therefore doesn't exactly match 'd'.
From the .NET 4 source in DateTimeParse.ParseByFormat():
case 'd':
// Day & Day of week
tokenLen = format.GetRepeatCount();
if (tokenLen <= 2) {
// "d" & "dd"
if (!ParseDigits(ref str, tokenLen, out tempDay)) {
if (!parseInfo.fCustomNumberParser ||
!parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay)) {
result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
return (false);
}
}
if (!CheckNewValue(ref result.Day, tempDay, ch, ref result)) {
return (false);
}
}
else
{...}
The parser lumps "d" and "dd" together.
It appears that behavior is by design, and I think it works that way to be consistent with other string formatting options.
Take the following example:
//Convert DateTime to string
string dateFormat = "d/MM/yyyy";
string date1 = new DateTime(2008, 10, 5).ToString(dateFormat);
string date2 = new DateTime(2008, 10, 12).ToString(dateFormat);
//Convert back to DateTime
DateTime x1, x2;
DateTime.TryParseExact(date1, dateFormat, null, System.Globalization.DateTimeStyles.None, out x1);
DateTime.TryParseExact(date2, dateFormat, null, System.Globalization.DateTimeStyles.None, out x2);
Console.WriteLine(x1);
Console.WriteLine(x2);
In the first part, ToString() outputs a two digit day for October 12th, because it wouldn't make much sense to just write out a single digit day (and which digit would it pick, the 1 or the 2?). So since the "d" represents one OR two digit days when converting to a string, it would have to work the same way when converting back to DateTime. If it didn't, the conversion back to DateTime in TryParseExact in my example would fail, and that would definitely not be an expected behavior.
I would say that if you really need to match a d/MM/yyyy format exactly, you could probably use a regex to validate the string and then pass it through Parse, TryParse or TryParseExact (depending on how good your regex is, since it would have to handle leap years, 30/31 days, etc if you wanted to use Parse).
I'd say it doesn't fail because TryParseExact is smart enough to know that '01' == '1'.
TryParseExact is just trying to be flexible in this case I guess. But the "d" vs "dd" should and would work as advertised when you are converting date to string using a format specifier.
Because a single 'd' means that your DateTime value will be converted to as short value as possible, i.e. without leading zero if there's no necessity for it. I suppose it shouldn't fail when you're converting from string to DateTime because the main purpose of TryParseExact's format string is to help to convert to DateTime, i.e. it serves like a hint, it's not intended to validate string format.
You can use RegEx if you still need hardcore string format validation.
I'm writing a C# class that will convert strings to dates. Pretty easy I guess. The class accepts formatstrings like "yyyy-MM-dd" and inputstrings like "2010-10-10"
However I have some cases that give me trouble:
format "yyyyMMdd" input "19950000"
or
format "dd-MM-yyyy" input "00-06-2001"
Note that these cases have zeroes ('00') for day and/or month, and that these cannot be converted to a DateTime. I'll need to replace them.
To handle theses cases I need to split the input string in the parts, one each for day, month and year, so I can set some default day and month (probably 01) if they are missing. But I need to use the formatstring to accomplish this.
So the question is, how can I split an inputstring in the components specified in the formatstring?
Thanks
[UPDATE] Using Joe's answer I came up with this:
string[] formats = { format, format.Replace("dd", "00").Replace("MM", "00"), format.Replace("dd", "00"), format.Replace("MM", "00") };
// Parse input
DateTime d = DateTime.ParseExact(txtDate.Text, formats, CultureInfo.InvariantCulture, DateTimeStyles.None);
This uses the supplied format and creates alternative formats with zeroes ('00') for day, month and both day and month.
Thanks Joe!
If you have a well-defined set of formats, you can use DateTime.ParseExact, passing an array of format strings.
// Define all allowed formats
string[] formats = { "yyyyMMdd", "yyyyMM00", "yyyy0000" };
// Parse input
DateTime d;
d = DateTime.ParseExact("20100930", formats,
CultureInfo.InvariantCulture, DateTimeStyles.None);
d = DateTime.ParseExact("20100900", formats,
CultureInfo.InvariantCulture, DateTimeStyles.None);
d = DateTime.ParseExact("20100000", formats,
CultureInfo.InvariantCulture, DateTimeStyles.None);
Missing days / months will be set to a default of 1.
My approach would be to define various Regular Expressions and using a chain of responsibility design pattern, pass the value to the first, if matches it stops there and if not sends to the next one until one of them matches the string.
My regex pattern would separate date, month and year element and set a default value for each if it is 0.
Here for Chain-of-responsibility_pattern:
http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern
private const string Pattern_dd-mm-yyyy = "(\d\d)-(\d\d)-(\d){4}";
private const string Pattern_ddmmyyyy = "(\d\d)(\d\d)(\d){4}";
private const string Pattern_ddSlashmmSlashyyyy = "(\d\d)/(\d\d)/(\d){4}";
I don't fully understand your problem (I don't have the rep for a comment) but I can still give you some advices.
First of all, the DateTime class provides a ParseExact method (http://msdn.microsoft.com/en-us/library/w2sa9yss(v=VS.80).aspx) which accepts a formatting string for date and time. You can pass your format string to it
I don't clearly understand the part about the cases: do you need to accept timestamps in multiple formats? If so, you can try/catch until you find a match, or loop using the TryParseExact method which almost works the same.
Once you have a DateTime value, use the Year, Month and Day properties to get the components you've been searching in the input string