ParseExact/TryParseExact failing with single digit hour [duplicate] - c#

This question already has answers here:
DateTime conversion from string C#
(3 answers)
Closed 5 years ago.
I have a time and a date value with no field delimiters which I'm attempting to parse into a DateTime using TryParseExact. The time component has a single digit hour, and two digit minutes and seconds.
The following expression:
DateTime.ParseExact("20170101 84457", "yyyyMMdd Hmmss",
System.Globalization.CultureInfo.InvariantCulture)
results in FormatException with the message "String was not recognized as a valid DateTime.". I am assuming that this is because the time can't be unambiguously resolved, however since mm and ss are always going to be two digits each, I don't understand why this would be an issue.
The following results in successful parsing:
Hacking the input time to include delimiters (e.g., '8:44:57' and 'H:mm:ss')
Kludging the input time to have a leading zero if < 6 digits
Both of these seem a bit of a hack.

From what I can understand from other people's research, parsing attempts to retrieve two digits if it can, and parses from left to right.
Using my failing example of the raw time value 84857 and format Hmmss, because the hour is followed by a digit, it will be parsed as 84 - hence throwing a format exception.

According to the documentation:
If format is a custom format pattern that does not include date or time separators (such as "yyyyMMddHHmm"), use the invariant culture for the provider parameter and the widest form of each custom format specifier. For example, if you want to specify hours in the format pattern, specify the wider form, "HH", instead of the narrower form, "H".
So it appears that if you don't have any delimiters, you are required to use HH and not H.
Personally I would pad the time component to 6 digits and use HH. The following works fine for me:
DateTime.ParseExact("20170101 084457", "yyyyMMdd hhmmss", System.Globalization.CultureInfo.InvariantCulture);
If you want to wrap this in a custom function, you can use something like this:
static DateTime ParseDateTime(string input)
{
int dateInteger, timeInteger;
var s = input.Split(' ');
bool dateOK = int.TryParse(s[0], out dateInteger);
bool timeOK = int.TryParse(s[1], out timeInteger);
if (!dateOK || !timeOK) throw new FormatException("Invalid date/time string.");
var newInput = String.Format("{0:00000000} {1:000000}", dateInteger, timeInteger);
return DateTime.ParseExact(newInput, "yyyyMMdd hhmmss", System.Globalization.CultureInfo.InvariantCulture);
}

Related

DateTime.ParseExact "Hmmssff" FormatException [duplicate]

I have tried like below:
DateTime.ParseExact("Feb520161000PM",
"MMMdyyyyhhmmtt", CultureInfo.InvariantCulture)
But it's giving FormatException.
Interestingly
DateTime.ParseExact(DateTime.Now.ToString("MMMdyyyyhhmmtt"), "MMMdyyyyhhmmtt",
CultureInfo.InvariantCulture)
This is also giving format exception.
Alexei's answer is quite right, I wanna explain little bit deep if you let me..
You are thinking 5 should match with d specifier, right? But this is not how DateTime.ParseExact works under the hood.
Since The "d" custom format specifier represents number from 1 through 31, this specifier will map 52 in your string, not just 5. That's why your code throws FormatException.
As you can see, your string format can't parsed unless you do it some string manipulations with it.
In such a case, .NET Team suggests either using two digit forms like 05 or insert separators for your date and time values.
You can create a custom method that parse this MMMdyyyyhhmmtt format for only parse this kind of formatted strings like;
public static DateTime? ParseDate_MMMdyyyyhhmmtt(string date)
{
if (date == null)
return null;
if (date.Length < 14)
return null;
if (date.Length == 14)
date = date.Insert(3, "0");
DateTime dt;
if (DateTime.TryParseExact(date, "MMMdyyyyhhmmtt",
CultureInfo.InvariantCulture,
DateTimeStyles.None, out dt))
return dt;
return null;
}
The format is not parseable with regular parsing which work from left to right - you need custom code that parses value from right to left instead as you want leftmost number to be variable width ("Feb111111111PM").
If possible - change format to one with fixed width fields (preferably ISO8601). Otherwise split string manually and construct date from resulting parts (time part would work fine by itself, so just need to manually parse date part).
Some other approaches and info can be found in similar post about time - DateTime.ParseExact - how to parse single- and double-digit hours with same format string?

Short Time with DateTime.ParseExact

I’m trying to parse a time. I’ve seen this question asked/answered here many times but not for this specific scenario. Here’s my code:
var time1 = DateTime.ParseExact("919", "Hmm", CultureInfo.InvariantCulture);
also
var time2 = DateTime.ParseExact("919", "Hmm", null);
both of these throw the same
"String was not recognized as a valid DateTime"
What I want is 9:19 AM.
For further info I also need to parse “1305” as 1:05 PM, this is working fine.
It seems to me I’m using the correct format. What am I overlooking?
I'm not sure there is any format that can handle this. The problem is that "H" can be either one digit or two, so if there are two digits available, it will grab both - in this case parsing it as hour 91, which is clearly invalid.
Ideally, you'd change the format to HHmm - zero-padding the value where appropriate - so "0919" would parse fine. Alternatively, use a colon in the format, to distinguish between the hours and the minutes. I don't believe there's any way of making DateTime parse a value of "919" as you want it to... so you'll need to adjust the string somehow before parsing it. (We don't have enough context to recommend a particular way of doing that.)
Yes, your format is right but since H specifier might be 2 character, ParseExact method try to parse 91 as an hour, which is an invalid hour, that's why you get FormatException in both case.
I connected to microsoft team about this situation 4 months ago. Take a look;
DateTime conversion from string C#
They suggest to use 2 digit form in your string or insert a date separator between them.
var time1 = DateTime.ParseExact("0919", "Hmm", CultureInfo.InvariantCulture);
or
var time1 = DateTime.ParseExact("9:19", "H:mm", CultureInfo.InvariantCulture);
You cant exclude the 0 prefix to the hour. This works
var time1 = DateTime.ParseExact("0919", "Hmm", CultureInfo.InvariantCulture);
Perhaps you want to just prefix 3-character times with a leading zero before parsing.
Much appreciated for all the answers. I don’t have control of the text being created so the simplest solution for me seemed to be prefixing a zero as opposed to adding a colon in the middle.
var text = "919";
var time = DateTime.ParseExact(text.PadLeft(4, '0'), "Hmm", null);

How to parse this string as a DateTime

How can I parse the following string to DateTime with timezone?
Should I use CultureInfo? TimeZoneInfo? Or some other kind?
15.08.11 17:15:18.084867000 +02:00
I have try to use the following format:
var z = DateTime.Now.ToString("dd.MM.yy H:mm:ss.fffffff", CultureInfo.InvariantCulture);
But it threw an exception.
DateTime.ParseExact is what you want.
The actual format string you need is dd.MM.yy HH:mm:ss.FFFFFFF00 zzz
var dateTest = "15.08.11 17:15:18.084867000 +02:00";
var format = "dd.MM.yy HH:mm:ss.FFFFFFF00 zzz";
var returnDate = DateTime.ParseExact(dateTest, format, System.Globalization.CultureInfo.InvariantCulture);
The problem is that the fractional portion of seconds can only be 7 digits, and you need to pad the format string with zeros to cater for it.
There is a problem in that the last two digits of the seconds must be 00, otherwise the format won't work, so if the last two digits are ever anything other than 00 this format string won't work for you.
You'd need to parse out the entire string excluding the last to digits of the seconds but keeping the rest of the string intact. If one was to go to that much bother one might be as well off just manually parsing the string.
Sorry I can't be of more help.

Converting the WhenChanged attribute (Generalized-Time) in LDAP to a DateTime in C#

I recently switch from using S.DS namespace (which uses ADSI) to the S.SD.Protocol namespace. The only problem is that ADSI handled the conversion of Generalized-Time to a DateTime for me. Now I'm getting back a value of "20070828085401.0Z" for the WhenChanged attribute. DateTime.Parse() will not convert this so is there another way?
The format you are getting is close to the round trip date time pattern ("o") and universal sortable round trip date time pattern ("u") standard date time format strings as described here.
One kludgy solution would be to massage the string you get to fit the pattern and then use the "o" or "u" standard format string with ParseExact.
A better way would be to construct a custom format string that matches the data you are already getting. In the "How Standard Format Strings Work" section of the standard date time format strings page you'll see the full custom formatting strings equivalent to "o" and "u". That should give you a good start.
EDIT: Add code
string format = "yyyyMMddHHmmss.f'Z'";
string target = "20070828085401.0Z";
DateTime d = DateTime.ParseExact(target, format, CultureInfo.InvariantCulture);
In the comments lixonn observes that, using the format string above, ParseExact will not successfully parse a time string like 199412160532-0500.
It also won't parse a number of other valid strings such as times without the trailing 'Zulu' indicator (20070828085401.0); times without a fractional part (20070828085401Z) and times that represent minutes and seconds as a fractional hour (2007082808.90028Z).
The format string can be made slightly more forgiving by replacing the hard-coded 'Z' with the K custom specifier which will accept 'Z', an offset like -0500, and nothing. Whether that additional flexibility is a good thing will depend on your application.
Note that even with the K specifier Lixonn's string won't be parsed successfully since it lacks a fractional part to match the .f component of the format string.
You'll have to use DateTime.ParseExact() specifying the exact format.
You might have to play with the format a little bit but it would be something like this.
DateTime result;
CultureInfo provider = CultureInfo.InvariantCulture;
string format="yyyyMMddhhmmss.0Z";
result = DateTime.ParseExact(dateString, format, provider);
You can use datetime's .strptime().
import datetime
# Since 0Z denotes UTC, you can get rid of it and apply the timezone
# later if you would like
time_string = "20070828085401.0Z".split('.')[0]
time_object = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%S")
time_object should output as datetime.datetime(2007, 8, 28, 8, 54, 1). I believe it will be timezone naive, and equivalent to UTC time.
// WIN32 FILETIME is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).
// While the unix timestamp represents the seconds since January 1, 1970 (UTC).
private static long Win32FileTimeToUnixTimestamp(long fileTime)
{
//return fileTime / 10000L - 11644473600000L;
return DateTimeOffset.FromFileTime(fileTime).ToUnixTimeSeconds();
}
// The GeneralizedTime follows ASN.1 format, something like: 20190903130100.0Z and 20190903160100.0+0300
private static long GeneralizedTimeToUnixTimestamp(string generalizedTime)
{
var formats = new string[] { "yyyyMMddHHmmss.fZ", "yyyyMMddHHmmss.fzzz" };
return DateTimeOffset.ParseExact(generalizedTime, formats, System.Globalization.CultureInfo.InvariantCulture).ToUnixTimeSeconds();
}

Why do these two date formats differ?

I'm trying to produce just the day number in a WPF text block, without leading zeroes and without extra space padding (which throws off the layout). The first produces the day number with a space, the second produces the entire date. According to the docs, 'd' should produce the day (1-31).
string.Format("{0:d }", DateTime.Today);
string.Format("{0:d}", DateTime.Today);
UPDATE:Adding % is indeed the trick. Appropriate docs here.
See here
d, %d
The day of the month. Single-digit days do not have a leading zero. The application specifies "%d" if the format pattern is not combined with other format patterns.
Otherwise d is interpreted as:
d - 'ShortDatePattern'
PS. For messing around with format strings, using LinqPad is invaluable.
From the MSDN documentation for "Custom Date and Time Format Strings":
Any string that is not a standard date
and time format string is interpreted
as a custom date and time format
string.
{0:d} is interpreted as a standard data and time format string. From "Standard Date and Time Format Strings", the "d" format specifier:
Represents a custom date and time
format string defined by the current
ShortDatePattern property.
With the space, {0:d } doesn't match any standard date and time format string, and is interpreted as a custom data and time format string. From "Custom Date and Time Format Strings", the "d" format specifier:
Represents the day of the month as a
number from 1 through 31.
The {0:d} format uses the patterns defined in the Standard Date and Time Format Strings document of MSDN. 'd' translates to the short date pattern, 'D' to the long date pattern, and so on and so forth.
The format that you want appears to be the Custom Date and Time Format Modifiers, which work when there is no matching specified format (e.g., 'd ' including the space) or when you use ToString().
You could use the following code instead:
string.Format("{0}", DateTime.Today.ToString("d ", CultureInfo.InvariantCulture));

Categories