What is the longest string that would convert to a valid DateTime? - c#

I am writing a data parser and trying to work out if a field is a number, a date, a string etc.
The .NET DateTime.TryParse is understandably slow when checking many records (as it checks many different date formats). Therefore, I want to shortcut the processing if possible. A simple check I can do initially is look at the length of the string and reject it if it falls outside of some bounds.
The shortest date I think I should reasonably expect is 6 characters long (e.g. d/M/yy) so I can make the following check:
if (fieldValue.Length < 6)
{
// no datetime is shorter than 6 chars (e.g. d/M/yy is the shotest I can think of)
return false;
}
What is the longest string that still represents a parse-able DateTime?
(For example, "Wednesday, 30th September 2020 12:34:56" is pretty long but I bet there are longer examples!)
A few points:
I am not looking for tricksy answers where the date is padded out with white space or something like that.
I am focused on English dates initially but would be interested if other cultures can throw up longer examples.

What is the longest string that still represents a parse-able
DateTime?
Take a look at the list of custom format specifiers for a DateTime, and take all of those into account.
For instance, this:
DateTime dt = DateTime.Now;
string strNow = dt.ToString("dddd, MMMM dd, yyyy gg hh:mm:ss.fffffff tt K");
Console.WriteLine(strNow);
Gives:
Tuesday, June 16, 2020 A.D. 08:47:02.2667911 AM -06:00
But those different types of values can be output differently based on the information in the DateTime. Look CLOSELY at all the different possible outputs for each specifier in the documentation to see what I mean.

Related

Strange behaviour of the "M" custom format specifier in c#

This Code produces the output in the comments bellow each Console.WriteLine statement.
Can anyone explain this kind of behaviour?
DateTime date1 = new DateTime(2008, 8, 18);
Console.WriteLine(date1.ToString("M"));
// Displays August 18
Console.WriteLine(date1.ToString("M "));
// Displays 8
Console.WriteLine(date1.ToString("(M)"));
// Displays (8)
Console.WriteLine(date1.ToString("(M) MMM, MMMM"));
// Displays (8) Aug, August
Can anyone explain this kind of behaviour?
Yes, it's completely documented in standard date and time format strings and custom date and time format strings.
Let's go through them one at a time:
date1.ToString("M"): That uses a single character 'M', so it's the standard format string for "month/day pattern"
date1.ToString("M "): That uses a format string with two characters, so it's a custom format string using M which is the month number, 1-12 with no padding.
date1.ToString("(M)"): Again, a custom format string using M
date1.ToString("(M) MMM, MMMM"): A custom format string using M, as well as MMM ("abbreviated name of the month") and MMMM (" full name of the month")
The important difference between the first two is that a format string is only considered to be a standard format if it's a single character. Otherwise, it's considered to be a custom format. If you want to use a single-character custom format specifier on its own, you can use % as a leading character - so date1.ToString("%M") would return "8" rather than "August 18".
Date and Time in C# are handled by DateTime struct in C# that provides properties and methods to format dates in different datetime formats.
M-> Month number(eg.3)
MM-> Month number with leading zero(eg.04)
MMM-> Abbreviated Month Name (e.g. Dec)
MMMM-> Full month name (e.g. December)
https://www.c-sharpcorner.com/blogs/date-and-time-format-in-c-sharp-programming1

Most loose way to parse a date/time in C#?

I'm parsing a broad range of RSS feeds - apprently they all use their own way to show the timestamp of the article.
Now we even found one that uses a local words, like Donderdag 17 juli 2018.
At the moment we have a fallback mechanism where we just fall back to DateTime.UtcNow when we can't parse the date.
Still I would like to make a best attempt. What is the best way to really loosely parse a DateTime in C#? So it can handle i.e.:
13-11-2018 14.32
donderdag 13 november 2018, 14:32
13 nov 2018
14:32 13.11.2018
2018-11-13T16:32:00+2:00
etc. I know that this would not be foolproof, but still I like to make a best attempt.
Is there any recommended way? Or do I have to roll my own?
You could use DateTime.TryParseExact and include all the expected formats.
DateTime result;
if( DateTime.TryParseExact(input, new [] {"dd-MM-yyyy HH.mm", "dddd dd MMMM yyyy, HH:mm", "more formats here"}, CultureInfo.CreateSpecificCulture("nl-NL"), DateTimeStyles.None, out result)) {
Console.WriteLine("Succeeded " + result);
}
The only big "gotcha" here is date formats where the date and month are in ambiguous positions. I do not see any in your example but if you were to mix cultures in one stream then it could become a problem. As an example the U.S. generally starts a formatted date with the month while the Netherlands starts it with the day of the month. If this is a problem there is no way to handle this dynamically in your use case above unless you also get the culture from the RSS stream in which case you could try to create a set of culture specific parsing rules.
This suggestion is not specific to date times, but you could try to use parser combinators, especially if you decide to roll your own solution.
There are multiple libs for .net, Sprache for example.
Loosely parsing date times from mixed sources if data is probably not a good idea. Some things like Microsoft's text-to-speech may try, but it can sometimes have the effect of reading consecutive dates as
October first, November first, December first, January thirteenth, etc.
The only way loose parsing can be made somewhat reliable is if one can use
other cues to associate dates with whatever wrote them. If you have a bunch of dates that occur at the top level of a particular feed, and you find that all parsing patterns that work for all of them yield the same results, then it's likely that that parsing pattern is parsing the dates correctly. The biggest parts of such an endeavor, however, will likely not be parsing the dates, but rather (1) ensuring that dates that are written in different formats get grouped separately, and (2) providing a means by which an operator can assist the program in places where it has trouble.
Incidentally, I don't know if any date parsing programs make use of attached weekdays as part of format validation, but they could often help. For example, "2-1-2018" could either be January 2 or February 1, but "Thursday 2-1-2018" could only be the latter. It may be helpful when parsing numeric dates from a source whose format isn't fully established to determine what the weekday would be with each method of parsing and check whether the input contains something that looks like a weekday matching one but not the other.
You can use the TryParse method to try to parse the strings, while looping through all cultures to capture any culture differences in the string. The following method will parse all standard formats for all cultures and return the date in the out parameter if it's found.
Note that the danger here is that some dates will have ambiguous month and day values (any number less than 13 could be a month or a day). In that case, the result will be the first culture found that matches, which may not be correct.
Here's the code:
public static bool TryParseAllCultures(string formattedDate,
out DateTime result)
{
// First try in our local culture
if (DateTime.TryParse(formattedDate, out result)) return true;
foreach (var cultureInfo in CultureInfo.GetCultures(CultureTypes.AllCultures))
{
if (DateTime.TryParse(formattedDate, cultureInfo, DateTimeStyles.None,
out result))
{
return true;
}
}
return false;
}
Sample usage
Note: I modified one of your dates because the date itself was invalid! The second date used to be "donderdag 13 november 2018", except the 13th is dienstag (Tuesday), not donderdag (Thursday).
private static void Main()
{
DateTime date;
var dateFormats = new List<string>
{
"13-11-2018 14.32",
"donderdag 15 november 2018, 14:32",
"13 nov 2018",
"14:32 13.11.2018",
"2018-11-13T16:32:00+2:00"
};
DateTime result;
foreach (var dateFormat in dateFormats)
{
if (TryParseAllCultures(dateFormat, out result))
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"SUCCESS: {dateFormat.PadRight(36, '.')} {result}");
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"ERROR: Unable to parse format: {dateFormat}");
}
Console.ResetColor();
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output

Comparison between dates in C# application

I have an asp.net application in which i have these three dates:
now = 08-10-13 15:56:19
cloture1 = 01-10-13 00:00:00
cloture2= 01-01-50 00:00:00
The format of dates is DD-MM-YY HH:MM:SS. the problem is that the function DateTime.Compare() gives me the same result ie
DateTime.Compare(now,cloture1) > 0 and DateTime.Compare(now,cloture2) > 0.
So what is the reasons of this problem? How can i fix my snippet?
The problem is your program most probably interpreting cloture2 as 1950, not 2050.
Because you have not posted the code in which you set cloture2, I cannot offer a concrete solution, but the best I can offer is that you use 01-10-2050 explicitly in your code.
From The "yy" Custom Format Specifier
In a parsing operation, a two-digit year that is parsed using the "yy"
custom format specifier is interpreted based on the
Calendar.TwoDigitYearMax property of the format provider's current
calendar.
In a parsing operation, a two-digit year that is parsed using the "yy"
custom format specifier is interpreted based on the
Calendar.TwoDigitYearMax property of the format provider's current
calendar. The following example parses the string representation of a
date that has a two-digit year by using the default Gregorian calendar
of the en-US culture.
From GregorianCalendar.TwoDigitYearMax
This property allows a 2-digit year to be properly translated to a
4-digit year. For example, if this property is set to 2029, the
100-year range is from 1930 to 2029. Therefore, a 2-digit value of 30
is interpreted as 1930, while a 2-digit value of 29 is interpreted as
2029.
Your application should set this value to 99 to indicate that 2-digit
years are to be taken literally. For example, if this property is set
to 99, the 100-year range is from 0 (not a valid value for most
calendars) to 99. Therefore, a 2-digit value of 30 is interpreted as
30.
Even when you decompile GregorianCalendar.TwoDigitYearMax property, you can see yourself;
public override int TwoDigitYearMax
{
get
{
if (this.twoDigitYearMax == -1)
this.twoDigitYearMax = Calendar.GetSystemTwoDigitYearSetting(this.ID, 2029);
return this.twoDigitYearMax;
}
Boluc's answer is completely right. I want to point also your format part.
You can't format two digit year with YYYY format. You need to use yy format which allows two digit formatting.
DateTime dt = DateTime.ParseExact("01-01-50 00:00:00", "dd-MM-yy HH:mm:ss", CultureInfo.InvariantCulture);
Prints
01.01.1950 00:00:00
Here a DEMO.
Check out for more informations from Custom Date and Time Format Strings
If now is later than cloture1 then the returned value will be 1 (or Greater than Zero).
The code you have supplied along with the example dates seems to work fine in reference to the MSDN article for DateTime.Compare method.
Please check the following link for more information on the DateTime.Compare method:
http://msdn.microsoft.com/en-us/library/system.datetime.compare.aspx
If you believe that your code is still incorrect please elaborate on your question.

Date format pattern

Setting format for the date
#String.Format("{0:D}", Model.Date)
Code above shows the date in the following format: (13 January 2012)
The required output: (Friday 13 January 2012)
Is there a format for this pattern?
Yep, here you go.
String.Format("{0:dddd d MMMM yyyy}", Model.Date)
Full MSDN Documentation
The general rule I use to remember these formats is like this:
one character means the number alone;
two characters means add a leading zero if necessary
three characters means use three letters if day or month, four numbers for year
four letters means use full word for day or month
Extra stuff (not special characters) just gets put in the string
e.g. Consider 1st Jan 2001
String.Format("{0:(d;dd;ddd;dddd),(M;MM;MMM;MMMM),(y,yy,yyy,yyyy)}", DateTime.Parse("2001/01/01"))
will return
(1;01;Mon;Monday),(1;01;Jan;January),(1,01,2001,2001)
Similar rules for times, like this:
String.Format("{0:(h;hh):(m;mm):(s,ss) (t,tt)}", DateTime.Now)
to give this:
(9;09):(41;41):(34,34) (P,PM)
DateTime now = DateTime.Now;
Console.WriteLine(String.Format("{0:dddd d MMMM yyyy}",now));
//output = Friday 13 January 2012
if you want the standard date format just use
Console.WriteLine(now.ToString("D"));
here is something I wrote real quick as well to help you in the future if you want to see what you can to with the now.ToString() in regards to passing formats.
try this out in a Console Application to see the results.. Cheers
DateTime now = DateTime.Now;
Console.WriteLine(now.ToString("d"));
Console.WriteLine(now.ToString("D"));
Console.WriteLine(now.ToString("f"));
Console.WriteLine(now.ToString("F"));
Console.WriteLine(now.ToString("g"));
Console.WriteLine(now.ToString("G"));
Console.WriteLine(now.ToString("m"));
Console.WriteLine(now.ToString("M"));
Console.WriteLine(now.ToString("o"));
Console.WriteLine(now.ToString("O"));
Console.WriteLine(now.ToString("s"));
Console.WriteLine(now.ToString("t"));
Console.WriteLine(now.ToString("T"));
Console.WriteLine(now.ToString("u"));
Console.WriteLine(now.ToString("U"));
Console.WriteLine(now.ToString("y"));
Console.WriteLine(now.ToString("Y"));
Console.Read();
As #Dommer points out String.Format("{0:dddd d MMMM yyyy}", Model.Date) gives you the result you want. And here you'll find MSDN documentation on date and time formats.
If you need to be culturally-aware then use Format(IFormatProvider, String, Object()).
String.Format(CultureInfo.InvariantCulture, "{0:D}", Model.Date);

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();
}

Categories