I am trying to do a simple parsing using following statements:
//In actual code the date time value comes from db
var dateTime = new DateTime(2018, 04, 26);
var dtExtact = DateTime.ParseExact(dateTime.ToString(), "dd MMM yyyy HH:mm:ss:fff",null);
Now when I try doing this getting error,
System.FormatException: 'String was not recognized as a valid DateTime.'
I did have a look at the MSDN sample, but it does not provide any pointers on whats wrong with my date time.
If you would like to parse a date-time string in a specific format, make sure that your formatted date/time string matches the format of your parser.
In your example "round-tripping" a date/time is easy to achieve by reusing the same format string for formatting the date and for parsing:
var dateTime = new DateTime(2018, 04, 26);
const string dateFormat = "dd MMM yyyy HH:mm:ss:fff";
var dtExact = DateTime.ParseExact(
dateTime.ToString(dateFormat)
, dateFormat
, null
);
Console.WriteLine("{0} {1}", dateTime, dtExact);
Demo.
Both ToString() and Parse() are designed to automagically extract the Region setting from Windows. It is rarely a good idea to override this behavior. And apparently your pattern is not a valid one. For all we know, ':' is not actually the proper cultural seperator for Time elements
Overall this seems rather suspect - you turn a DateTime to string, only to parse it again right there. I can only guess it is for testing. But testing for what is the question.
I have 3 general rules when dealing with DateTimes:
Always store, retrieve and transmit the UTC value. You do not want ot add timezones to your issues. That way lies madness. There are rare exceptions, but then you are pretty much on your own to deal with that mess
Avoid storing, retreival or transmission as Text. keep it in proper DataTypes as long as possible
If you can follow the 2nd rule (using XML or some other serialsiation), at least pick a fixed Culture Format, Format String and string encoding at all endpoints. You do not want to add those issues to your worries
Following those rules, I rarely had any issues.
Related
I have a program that do several things.
Two of them is read a date from a txt and rewrite a date in the same txt.
The read of the date is a regex expression like:
[0-9]{2}/[0-9]{2}/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-5]{1}[0-9]{1})
The problem is that my regex expression only works in the format
"DD/MM/YYYY hh:mm:ss" and its impossible to make sure my regex expression can match all system datetime formats.
So, I need to make sure my program run's in every system, regardless the system datetime.now.
For that, i thought about format every system datetime.now, at start, to the format mentioned "DD/MM/YYYY hh:mm:ss".
At the moment i have the following code:
Datetime currentDate = DateTime.ParseExact(DateTime.Now.ToString(), "DD/MM/YYYY hh:mm:ss", CultureInfo.InvariantCulture);
However, when running some tests, using a system date in format "D/M/YYYY h:m:s" i get the error:
"String was not recognized as a valid DateTime."
The problem is that if my date, for example, is "9/27/2019 04:26:46"(M/D/YYYY h:m:s) it can't fit in the format i defined.
Any idea?
Thank you in advance!
You need to use the same format string and culture in every place where you convert the DateTime to string as well. In your sample code, you're doing
DateTime.Now.ToString()
This uses the default culture for the thread, and the default format. Unless assigned otherwise, the thread is probably using the local culture info. Instead, you would want to use the same format and the invariant culture:
DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture);
(note the lowercase "dd". "DD" is not a valid format specifier for date times; these things are case sensitive. Also note the "HH", which gives a 24-hour value, rather than 12-hour)
In practice, just using the invariant culture should be enough for persistence. Cultures already include default datetime formats, so unless you have a specific need to use a different format, why not use the default?
Also note that DateTime doesn't have a format. The format only comes into play when you convert from or to a string. That is the place where you need to ensure the same culture and format is used for both sides of the operation (and that's why for persistence, especially for data shared between different users or computers, you generally want to use the invariant culture).
If you need
to make sure my program run's in every system, regardless the system datetime.now
you can adapt international standard for this, say, ISO 8601.
In order to validate the DateTime, regular expressions like you have are not enough (just imagine leap years), but TryParse does it job:
string source = "2019-09-26T23:45:59";
// Either current culture date and time format or ISO
bool isValid = DateTime.TryParse(
source,
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal,
out var _date);
Or if you want to be more restrictive use TryParseExact:
// ISO only
bool isValid = DateTime.TryParseExact(
source,
"s",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal,
out var _date);
If you want to represent DateTime.Now in ISO 8601, add "s" standard format string:
string dateAsString = DateTime.Now.ToString("s");
Alas, you can provide a bunch of formats which are able to cope with any date and time formats; a classical example of ambiguous date is
01/02/03 - 01 Feb 2003 (Russia)
01/02/03 - 02 Jan 2003 (USA)
01/02/03 - 03 Feb 2001 (China)
You can alleviate the problem, while providing several formats:
// Here we try to support 4 formats (note different delimeters)
string[] formats = new string[] {
"s", // try ISO first
"dd'.'MM'.'yyyy HH':'mm':'ss", // if failed try Russian
"MM'/'dd'/'yyyy HH':'mm':'ss", // on error have a look at USA
"yyyy'-'MM'-'dd HH':'mm':'ss", // the last hope is Chinese
};
bool isValid = DateTime.TryParse(
source,
formats,
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal,
out var date);
I have the following Arabic date in the Umm Al-Qura calendar that I want to parse into a .NET DateTime object:
الأربعاء، 17 ذو الحجة، 1436
This date is equivalent to September 30th 2015 in the Gregorian calendar.
I've been trying the following "standard" C# code to parse this date, but without success:
var cultureInfo = new CultureInfo("ar-SA");
cultureInfo.DateTimeFormat.Calendar = new UmAlQuraCalendar(); // the default one anyway
var dateFormat = "dddd، dd MMMM، yyyy"; //note the ، instead of ,
var dateString = "الأربعاء، 17 ذو الحجة، 1436";
DateTime date;
DateTime.TryParseExact(dateString, dateFormat, cultureInfo.DateTimeFormat, DateTimeStyles.AllowWhiteSpaces, out date);
No matter what I do, the result of TryParseExact is always false. How do I parse this string properly in .NET?
By the way, if I start from a DateTime object, I can create the exact date string above using ToString()'s overloads on DateTime without problems. I just can't do it the other way around apparently.
Your datestring is 30 characters long and contains four UNICODE 8207 U+200F RIGHT TO LEFT MARK characters, but your dateformat does not.
// This gives a string 26 characters long
var str = new DateTime(2015,9,30).ToString(dateFormat, cultureInfo.DateTimeFormat)
RIGHT TO LEFT MARK is not whitespace.
If it only contains RLM/LRM/ALM you should probably just strip them out. Same with the isolates LRI/RLI/FSI and PDI sets, and LRE/RLE sets. You may not want to do that with LRO though. LRO is often used with legacy data where the RTL characters are stored in the opposite order, i.e. in the left-to-right order. In these cases you may want to actually reverse the characters.
Parsing dates from random places is a hard problem. You need a layered solution, try first one method, then another in priority order until you succeed. There is no 100% solution though, because people can type what they like.
See here for more information: http://www.unicode.org/reports/tr9/
This is a Right-To-Left culture, which means that the year will be rendered first. For example, the following code:
var cultureInfo = new CultureInfo("ar-SA");
cultureInfo.DateTimeFormat.Calendar = new UmAlQuraCalendar();
Console.WriteLine(String.Format(cultureInfo,"{0:dddd، dd MMMM، yyyy}",DateTime.Now));
produces الأربعاء، 17 ذو الحجة، 1436. Parsing this string works without problem:
var dateString="الأربعاء، 17 ذو الحجة، 1436";
var result=DateTime.TryParseExact(dateString, dateFormat, cultureInfo.DateTimeFormat,
DateTimeStyles.AllowWhiteSpaces,out date);
Debug.Assert(result);
PS: I don't know how to write the format string to parse the original input, as changing the position of what looks like a comma to me, changes the actual characters rendered in the string.
I'm trying to use DateTime.Parse in order to parse a string containing a DateTime in a custom format.
The format is yy-MMM MMMM-dddd-ddd-dd, the string is 15-jan. január-szerda-Sze-07.
I've modified the ShortDatePattern in the OS' regional settings, and you can see it in the CultureInfo.CurrentCulture while debugging.
I'm using the following code:
var date = DateTime.Parse(dateInString, CultureInfo.CurrentCulture);
But it fails with exception String was not recognized as a valid DateTime.
Using ParseExact it does work.
var date = DateTime.ParseExact(
dateInString,
CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern,
CultureInfo.CurrentCulture);
Shouldn't Parse work as well?
Any help would be appreciated.
Edit:
Assuming Parse is just not good enough, should this be okay or can it cause problem I can't think of right now (it works with the aforementioned problem)?
DateTime date = new DateTime();
bool success = false;
foreach (var format in currentCulture.DateTimeFormat.GetAllDateTimePatterns())
{
success = DateTime.TryParseExact(dateString, format, culture, DateTimeStyles.AllowWhiteSpaces, out date);
if (success)
break;
}
if (!success)
throw new Exception();
I played a little bit with your example and I couldn't make it work. I think that DateTime.Parse is simply not clever enough to parse your string and MSDN documentation confirms that. Here is a section from ParseExact documentation according to which Parse should not be used with custom cultures/patterns and why:
If you parse a date and time string generated for a custom culture, use the ParseExact method instead of the Parse method to improve the
probability that the parse operation will succeed. A custom culture
date and time string can be complicated, and therefore difficult to
parse. The Parse method attempts to parse a string with several
implicit parse patterns, all of which might fail.
And here is another interesting fragment from SetAllDateTimePatterns documentation:
The Parse and TryParse methods do not fully iterate all strings in patterns when parsing the string representation of a date and time. If
you require a date and time string to have particular formats in a
parsing operation, you should pass the array of valid formats to the
DateTime.ParseExact...
I have a string that has a date stored in it.
String date = "03-05-2013 00:00:00";
I parsed it to Datetime as follows:
DateTime Start = DateTime.Parse(date);
Start.ToString() gave me "3/5/2013 12:0:00 AM"
I also used:
DateTime Start = DateTime.ParseExact(date,"dd-MM-yyyy HH:mm:ss",CultureInfo.InvariantCulture);
Then, Start.ToString() gave me "3/5/2013 12:0:00 AM", which is the exact same result as the previous one. I need to keep the original formatting. How may I do it? Thanks.
The format you parse with does not dictate how the DateTime is formatted when you convert the date back to a string. When you call ToString on a date it pulls the format from the current culture of the thread your code is executing on (which defaults to the culture of the machine your on).
You can override this by passing the format into ToString() i.e.
Start.ToString("dd-MM-yyyy HH:mm:ss", CultureInfo.InvariantCulture);
See Custom Date and Time Formats.
You need to pass the format in the ToString() call.
Start.ToString("dd-MM-yyy HH:mm:ss");
I need to keep the original formatting.
Then you need to apply the same pattern again when you call ToString:
string formatted = Start.ToString("dd-MM-yyyy HH:mm:ss",
CultureInfo.InvariantCulture);
(Note that you should specify the same culture when formatting as you did when parsing, to avoid things like the time separator from changing.)
Note that for some formats this still might not give the exact original representation - if you're using a format which includes the text for a month, for example, that would match case-insensitively, so input including "MARCH" would be reformatted as "March".
A DateTime value is just a date and time (and a "kind", but that's another story) - it doesn't maintain a textual representation any more than an integer does. It's important to differentiate between the inherent data in a value and a textual representation of that data. Most types which have multiple possible textual representations have no notion of keeping "the original representation" alongside the data.
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