TLDR;
I need to validate whether a given input string is a valid "format string" for parsing DateTime. For example,
yy-mm-dd
is validyy-aaaaaaa123
is not validI am working on a program which accepts Date format as an input from the user. Below is my stripped out the code
private string datetimeFormat;
public Logger(string dateFormat)
{
datetimeFormat = dateFormat;
}
...
...
...
// Inside some function
string pretext = $"{DateTime.Now.ToString(datetimeFormat)},{logLevel},";
I need to add validation for the dateFormat
string input.
I am thinking of having many possible combinations in an array and accept only those strings. But is there any other way to validate?
Updates:
My input string does not contain any date. This is not a duplicate of the specified question.
This question is not about DateTime at all.
This kind of depends on what you mean by "valid" and how important you think the "only DateTime
, nothing else" restriction is to you.
Here are a few rules that we can use to test format strings, with some express limitations:
Must be suitable for passing to DateTime.ToString(string format)
to convert a DateTime
value to string.
Must be usable to parse the output of rule 1 into a valid DateTime
value.
The output of rule 2 must not contain a time portion.
Optionally, the output of rule 2 should be the same as the input within a defined range of accuracy.
These will be good enough for many uses, as long as you are expecting that the output be a fully specified date. Year, month and day must be specified. Here's some code to test against those rules:
static System.Globalization.CultureInfo DateTimeProvider = System.Globalization.CultureInfo.InvariantCulture;
const System.Globalization.DateTimeStyles ParseExactStyle = System.Globalization.DateTimeStyles.None;
static DateTime[] DateSamples = new[]
{
DateTime.Now,
DateTime.Today,
DateTime.Today.AddDays(1 - DateTime.Today.Day),
DateTime.Parse("10-Jan-2000"),
DateTime.Parse("01-Oct-1990"),
DateTime.Parse("13-Feb-1901")
};
public static bool IsValidDateFormat(string format, out string result)
{
var maxDifference = TimeSpan.FromDays(1);
foreach (var sample in DateSamples)
{
// Rule 1: Must be suitable for '.ToString(...)'
string sampleString;
try
{
sampleString = sample.ToString(format);
}
catch (FormatException e)
{
result = $"Failed rule 1: {e.Message}";
return false;
}
// Rule 2: Must be able to parse the produced string
if (!DateTime.TryParseExact(sampleString, format, DateTimeProvider, ParseExactStyle, out var parsed))
{
result = $"Failed rule 2: does not parse it's own output. '{sampleString}'";
return false;
}
// Rule 3: No time values.
if (parsed != parsed.Date)
{
result = $"Failed rule 3: No time values. '{sampleString}' => #{parsed}#";
return false;
}
// Rule 4: Difference must be less than maxDifference
TimeSpan difference = sample < parsed ? parsed - sample : sample - parsed;
if (difference >= maxDifference)
{
result = $"Failed rule 4: difference '{difference}' too large.";
return false;
}
}
result = "OK";
return true;
}
(This sets the result
out parameter to a description of why the format string failed, or OK if it passed, but you might prefer to return a simple enum value.)
This validates on all sorts of weird formats, including those with extra non-contextual - or at least non-time - characters. The samples include a few tests against time values, order reversal and so on.
There are some limitations however:
TryParseExact
does not work with standard format strings like d
, 'F', etc.yyy
) and other stretching formats.In short, it's good enough for simple work. You can trim it back a bit.