Parsing Dates using JSON.NET - c#

I am using JSON.NET's LINQ to JSON to parse data, which includes dates. My sample JSON looks like this:
{"2013" : [
{ "date":"2/5/2013 11:13 AM","id":23 }],
"2012" : [
{ "date":"28/9/2012 5:29 PM","id":24 }]
}
The format of the date is in d/m/yyyy h:mm. I tried parsing it first using DateTime.Parse:
var postTitles = jobject.Children().SelectMany(x => x.First).
Select(p =>
new Report
{
Date = DateTime.Parse((string)p["date"]),
Height = (int)p["height"]
});
However, it throws a FormatException on the second date so I tried using DateTime.ParseExact instead:
var postTitles = jobject.Children().SelectMany(x => x.First).
Select(p =>
new Report
{
Date = DateTime.ParseExact(p["date"].ToString(), "dd/mm/yyyy hh:mm", new
CultureInfo("en-US")),
Height = (int)p["height"]
});
This time, I get a FormatException on the first date. Are there any alternatives on how to parse this dates? I tried using Extension Methods as I am used to doing XML but it seems my extension methods are not recognized.

Your format string for ParseExact is very broken. Basically, you really need to pay attention to the format of your actual data, and look at the MSDN documentation for custom date/time strings. You can't just put any old pattern in and expect it to work: it's got to match your data.
So you probably want a format string of "d/M/yyyy h:mm tt". I would also suggest using the invariant culture:
Date = DateTime.ParseExact(p["date"].ToString(), "d/M/yyyy h:mm tt",
CultureInfo.InvariantCulture);
(The invariant culture is mostly the same as the US culture, but the important aspect is that it indicates that this is intended for machine-generated data rather than genuinely US-centric data.)
There may well be a better way of doing this in JSON.NET, but that should at least be a starting point.

Related

System.FormatException: 'String '"2022-04-14 13:03:12"' was not recognized as a valid DateTime.'

cnn.Open();
using (StreamReader reader = new StreamReader(#"C:/Users/jhogan/Documents/Test Folder/test_import.csv"))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (lineNumber != 0)
{
var values = line.Split(',');
var date = values[0];
var sentDate = DateTime.ParseExact(date, "yyyy-MM-dd hh:mm:ss", CultureInfo.InvariantCulture);
await cnn.ExecuteAsync(insertFeedback, new {
#SentDate = sentDate, #RatedDate = values[1], #Rating = values[2], #Location = values[3], #Notes = values[4], #Email = values[5], #Feedback = values[6] },commandTimeout: 120,commandType: CommandType.StoredProcedure);}
I am attempting to load this data from a csv file into my database, but am running into the following error from the title. The original formatting of the date from my csv is HH:MM:SS PM/AM and my column type in the database is datetime. In my database, the datetime formatting is YYYY-MM-DD HH:MM:SS:MMM. I think this is because of the database's formatting of things, but I am genuinely not sure.
my column type in the database is datetime. In my database, the datetime formatting is YYYY-MM-DD HH:MM:SS:MMM.
You misunderstand how the database and .Net DateTime values work. If the column type or data type is DateTime, the format in the database or in memory is binary, and when you see YYYY-MM-DD HH:MM:SS:MMM that's just a convenience provided for you by your tooling. There is no need to ever try to match your string to that internal binary format.
But as for the code, the problem is the lower-case hh in the format string. We can see in the documentation the lower-case h is for 12-hour times and upper-case H is for 24 hour times. Looking at the input string 2022-04-14 13:03:12 from the error in the question title, we have a 13 in this position, which definitely puts you in upper-case H territory:
var sentDate = DateTime.ParseExact(date, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
But it's worse than that. I also see this in a comment:
the first row is 4/14/2022 1:03:12 PM
So first of all, the format string for DateTime.ParseExact() tells the format of the original input source string, not the target format for the parsed value, which is, after all, binary and not human readable anyway.
The format string to parse that value would look like this: M/dd/yyyy h:mm:ss tt. Also note the use of paired vs single characters in certain positions.
var sentDate = DateTime.ParseExact(date, "M/dd/yyyy h:mm:ss tt", CultureInfo.InvariantCulture);
But the real significance here is the variation between the error in the question title and the comment means you might have a mix of formats. That's scary. DateTime.ParseExact() uses strict parsing. If the input does not EXACTLY match the expected format you WILL get an exception.
Fortunately, there is an overload for the method that accepts multiple format strings. Even with this overload, though, you probably need to take the time to review the csv data and make sure that every possible format in the data is accounted for.
And if data for this field could be entered by random humans may God have mercy on your soul.

Why does C# DateTime parse "01/05/2020" the same regardless of culture?

I am looking for someone to help me understand why this unit test passes.
Essentially we want to support various date formats but en-CA should be treated as DD/MM/YYYY and if client is using en-us culture then we want to treat the date as MM/DD/YYYY. I don't understand why this isn't the default behavior with DateTime.Parse and DateTime.TryParse.
[Fact]
public void DateTimeParse()
{
var cultureUs = CultureInfo.GetCultureInfoByIetfLanguageTag("en-us");
DateTime.TryParse("01/05/2020", cultureUs.DateTimeFormat, DateTimeStyles.None, out var d);
var cultureCa = CultureInfo.GetCultureInfoByIetfLanguageTag("en-ca");
DateTime.TryParse("01/05/2020", cultureCa.DateTimeFormat, DateTimeStyles.None, out var c);
Assert.Equal(d.Month,c.Month);
}
The formats of English (Canada) may be different from what you think. For example, the code:
foreach (var code in new[] { "en-US", "en-CA", "en-GB", })
{
Console.WriteLine($"Code {code}");
var dtf = CultureInfo.GetCultureInfo(code).DateTimeFormat;
Console.WriteLine(dtf.ShortDatePattern);
Console.WriteLine(dtf.DateSeparator);
Console.WriteLine();
}
writes:
Code en-US
M/d/yyyy
/
Code en-CA
yyyy-MM-dd
-
Code en-GB
dd/MM/yyyy
/
on my system.
When you use TryParse (and not TryParseExact) and none of the standard patterns of the date time format provider match, it is hard to know what happens(?).

DateTime FormatException error

DateTime datuMDokumenta = Convert.ToDateTime(txtDatumDokum.Text);
txtDatumDokum.Text is like "09.09.2011".
but i get FormatException error. Must i parse date?
Try DateTime.ParseExact with the dd.MM.yyyy format string
DateTime.ParseExact(txtDatumDokum.Text, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
It's not good to see, anyway try this:
string s = "09.09.2011";
DateTime dt = Convert.ToDateTime(
s.Replace(".",
new System.Globalization.DateTimeFormatInfo().DateSeparator));
You need to tell us why the text input is using this format. If it is because the user enters it this way, then you need to make sure that the format matches that given by Thread.CurrentCulture.DateTimeFormat.ShortDatePattern. Changing the culture (by setting
Thread.CurrentCulture) to an appropriate value will then solve your problem.
If you are supposed to parse the input no matter what format it is in, then you will need to do some manual processing first (perhaps remove spaces and other delimiter characters from the input with string.Replace) and then try to parse the date using DateTime.ParseExact and a known format string.
But it all depends on why the input has that format, and why your application's current culture does not match it.
You could try this, TryParse avoids parsing exceptions.. Then you just need check result to be sure that it parsed.
DateTime datuMDokumenta;
bool result = DateTime.TryParse(txtDatumDokum.Text, out datuMDokumenta);
You will have to determine if this is a good solution for your application.
See this example:
http://msdn.microsoft.com/en-us/library/ch92fbc1.aspx
Judging by the date you gave you need to include a culture, de-DE accepts 01.01.11 type of dates but I'm not sure which one you actually want to use, you'll need to decide that.. the Code would look like this:
using System.Globalization;
DateTime datuMDokumenta;
bool result = DateTime.TryParse(txtDatumDokum.Text, CultureInfo.CreateSpecificCulture("de-DE"), DateTimeStyles.None, out datuMDokumenta);
A list of cultures can be found here, select the appropriate one for you:
http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo%28v=vs.71%29.aspx
The plus here is that this code is a bit more work but it is very difficult to break. Assuming you are using a free text entry on a TextBox you don't want to be throwing exceptions.
Yes you have to parse input date in current culture.
string[] format = new string[] { "dd.MM.yyyy" };
string value = "09.09.2011";
DateTime datetime;
if (DateTime.TryParseExact(value, format, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.NoCurrentDateDefault, out datetime))
//Valid
else
//Invalid
DateTime dt = Convert.ToDateTime(txtDatumDokum.Text)
It is right...there is no isssue
During a Deserialization call under compact framework 3.5 i've had some unexpected behaviour before.
I've converted from using the OpenNETCF serialization classes to the framework XML serialization class. In doing so, the default time format has changed and the order of property/public members. So long story short, i've exposed a text property which converts my date-times back to the format my VB6 application is expecting.
Dim dumbDate As New Date
Dim formats() As String = {"yyyy-MM-ddTHH:mm:ss.fffzzz", _
"yyyy-MM-dd HH:mm:ss:fffffffzzz"}
_datetimeTaken = dumbDate.ParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.None)
' There is something wrong with compact framework during the Serialization calls.
' calling the shared method Date.Parse or Date.ParseExact does not produce the same
' result as calling a share method on an instance of Date. WTF?!?!?!
' The below will cause a "Format" exception.
'_datetimeTaken = Date.ParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.None)
Date.blah doesn't work. dumbDate.blah works. strange.
public static void Main(string[] args)
{
var dt = new DateTime(2018, 04, 1);
Console.WriteLine(dt);
string month = dt.ToString("MMMM");
Console.WriteLine(month); //April
month = dt.ToString("MMM");
Console.WriteLine(month); //Apr
month = dt.ToString("MM");
Console.WriteLine(month); //04
Console.ReadKey();
}
your code:
DateTime datuMDokumenta = Convert.ToDateTime(txtDatumDokum.Text);
try changing this to:
DateTime datuMDokumenta = Convert.ToDateTime(txtDatumDokum);
and when u print the date/time
print datuMDokumenta.Text

Parse string to DateTime in C#

I have date and time in a string formatted like that one:
"2011-03-21 13:26" //year-month-day hour:minute
How can I parse it to System.DateTime?
I want to use functions like DateTime.Parse() or DateTime.ParseExact() if possible, to be able to specify the format of the date manually.
DateTime.Parse() will try figure out the format of the given date, and it usually does a good job. If you can guarantee dates will always be in a given format then you can use ParseExact():
string s = "2011-03-21 13:26";
DateTime dt =
DateTime.ParseExact(s, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
(But note that it is usually safer to use one of the TryParse methods in case a date is not in the expected format)
Make sure to check Custom Date and Time Format Strings when constructing format string, especially pay attention to number of letters and case (i.e. "MM" and "mm" mean very different things).
Another useful resource for C# format strings is String Formatting in C#
As I am explaining later, I would always favor the TryParse and TryParseExact methods. Because they are a bit bulky to use, I have written an extension method which makes parsing much easier:
var dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
Or more simply, if you want to use the date patterns of your current culture implicitly, you can use it like:
DateTime? dt = dtStr.ToDate();
In that case no specific pattern need to be specified.
Unlike Parse, ParseExact etc. it does not throw an exception, and allows you to check via
if (dt.HasValue) { // continue processing } else { // do error handling }
whether the conversion was successful (in this case dt has a value you can access via dt.Value) or not (in this case, it is null).
That even allows to use elegant shortcuts like the "Elvis"-operator ?., for example:
int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;
Here you can also use year.HasValue to check if the conversion succeeded, and if it did not succeed then year will contain null, otherwise the year portion of the date. There is no exception thrown if the conversion failed.
Solution:  The   .ToDate()   extension method
Try it in .NetFiddle
public static class Extensions
{
/// Extension method parsing a date string to a DateTime? <para/>
/// <summary>
/// </summary>
/// <param name="dateTimeStr">The date string to parse</param>
/// <param name="dateFmt">dateFmt is optional and allows to pass
/// a parsing pattern array or one or more patterns passed
/// as string parameters</param>
/// <returns>Parsed DateTime or null</returns>
public static DateTime? ToDate(this string dateTimeStr, params string[] dateFmt)
{
// example: var dt = "2011-03-21 13:26".ToDate(new string[]{"yyyy-MM-dd HH:mm",
// "M/d/yyyy h:mm:ss tt"});
// or simpler:
// var dt = "2011-03-21 13:26".ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
const DateTimeStyles style = DateTimeStyles.AllowWhiteSpaces;
if (dateFmt == null)
{
var dateInfo = System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat;
dateFmt=dateInfo.GetAllDateTimePatterns();
}
var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
style, out var dt) ? dt : null as DateTime?;
return result;
}
}
Some information about the code
You might wonder, why I have used InvariantCulture calling TryParseExact: This is to force the function to treat format patterns always the same way (otherwise for example "." could be interpreted as decimal separator in English while it is a group separator or a date separator in German). Recall we have already queried the culture based format strings a few lines before so that is okay here.
Update: .ToDate() (without parameters) now defaults to all common date/time patterns of the thread's current culture.
Note that we need the result and dt together, because TryParseExact does not allow to use DateTime?, which we intend to return.
In C# Version 7 you could simplify the ToDate function a bit as follows:
// in C#7 only: "DateTime dt;" - no longer required, declare implicitly
if (DateTime.TryParseExact(dateTimeStr, dateFmt,
CultureInfo.InvariantCulture, style, out var dt)) result = dt;
or, if you like it even shorter:
// in C#7 only: Declaration of result as a "one-liner" ;-)
var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
style, out var dt) ? dt : null as DateTime?;
in which case you don't need the two declarations DateTime? result = null; and DateTime dt; at all - you can do it in one line of code.
(It would also be allowed to write out DateTime dt instead of out var dt if you prefer that).
The old style of C# would have required it the following way (I removed that from the code above):
// DateTime? result = null;
// DateTime dt;
// if (DateTime.TryParseExact(dateTimeStr, dateFmt,
// CultureInfo.InvariantCulture, style, out dt)) result = dt;
I have simplified the code further by using the params keyword: Now you don't need the 2nd overloaded method any more.
Example of usage
var dtStr="2011-03-21 13:26";
var dt=dtStr.ToDate("yyyy-MM-dd HH:mm");
if (dt.HasValue)
{
Console.WriteLine("Successful!");
// ... dt.Value now contains the converted DateTime ...
}
else
{
Console.WriteLine("Invalid date format!");
}
As you can see, this example just queries dt.HasValue to see if the conversion was successful or not. As an extra bonus, TryParseExact allows to specify strict DateTimeStyles so you know exactly whether a proper date/time string has been passed or not.
More Examples of usage
The overloaded function allows you to pass an array of valid formats used for parsing/converting dates as shown here as well (TryParseExact directly supports this), e.g.
string[] dateFmt = {"M/d/yyyy h:mm:ss tt", "M/d/yyyy h:mm tt",
"MM/dd/yyyy hh:mm:ss", "M/d/yyyy h:mm:ss",
"M/d/yyyy hh:mm tt", "M/d/yyyy hh tt",
"M/d/yyyy h:mm", "M/d/yyyy h:mm",
"MM/dd/yyyy hh:mm", "M/dd/yyyy hh:mm"};
var dtStr="5/1/2009 6:32 PM";
var dt=dtStr.ToDate(dateFmt);
If you have only a few template patterns, you can also write:
var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
Advanced examples
You can use the ?? operator to default to a fail-safe format, e.g.
var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");
In this case, the .ToDate() would use common local culture date formats, and if all these failed, it would try to use the ISO standard format "yyyy-MM-dd HH:mm:ss" as a fallback. This way, the extension function allows to "chain" different fallback formats easily.
You can even use the extension in LINQ, try this out (it's in the .NetFiddle above):
var strDateArray = new[] { "15-01-2019", "15.01.2021" };
var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
var dtRange = strDateArray.Select(s => s.ToDate(patterns));
dtRange.Dump();
which will convert the dates in the array on the fly by using the patterns and dump them to the console.
Some background about TryParseExact
Finally, Here are some comments about the background (i.e. the reason why I have written it this way):
I am preferring TryParseExact in this extension method, because you avoid exception handling - you can read in Eric Lippert's article about exceptions why you should use TryParse rather than Parse, I quote him about that topic:2)
This unfortunate design decision1) [annotation: to
let the Parse method throw an exception] was so vexing that of course
the frameworks team implemented TryParse shortly thereafter which does the right thing.
It does, but TryParse and TryParseExact both are still a lot less than comfortable to use: They force you to use an uninitialized variable as an out parameter which must not be nullable and while you're converting you need to evaluate the boolean return value - either you have to use an ifstatement immediately or you have to store the return value in an additional boolean variable so you're able to do the check later. And you can't just use the target variable without knowing if the conversion was successful or not.
In most cases you just want to know whether the conversion was successful or not (and of course the value if it was successful), so a nullable target variable which keeps all the information would be desirable and much more elegant - because the entire information is just stored in one place: That is consistent and easy to use, and much less error-prone.
The extension method I have written does exactly that (it also shows you what kind of code you would have to write every time if you're not going to use it).
I believe the benefit of .ToDate(strDateFormat) is that it looks simple and clean - as simple as the original DateTime.Parse was supposed to be - but with the ability to check if the conversion was successful, and without throwing exceptions.
1) What is meant here is that exception handling (i.e. a try { ... } catch(Exception ex) { ...} block) - which is necessary when you're using Parse because it will throw an exception if an invalid string is parsed - is not only unnecessary in this case but also annoying, and complicating your code. TryParse avoids all this as the code sample I've provided is showing.
2) Eric Lippert is a famous StackOverflow fellow and was working at Microsoft as principal developer on the C# compiler team for a couple of years.
var dateStr = #"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);
Check out this link for other format strings!
DateTime.Parse() should work fine for that string format. Reference:
http://msdn.microsoft.com/en-us/library/1k1skd40.aspx#Y1240
Is it throwing a FormatException for you?
Put the value of a human-readable string into a .NET DateTime with code like this:
DateTime.ParseExact("April 16, 2011 4:27 pm", "MMMM d, yyyy h:mm tt", null);
You can also use XmlConvert.ToDateString
var dateStr = "2011-03-21 13:26";
var parsedDate = XmlConvert.ToDateTime(dateStr, "yyyy-MM-dd hh:mm");
It is good to specify the date kind, the code is:
var anotherParsedDate = DateTime.ParseExact(dateStr, "yyyy-MM-dd hh:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
More details on different parsing options http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html
The simple and straightforward answer -->
using System;
namespace DemoApp.App
{
public class TestClassDate
{
public static DateTime GetDate(string string_date)
{
DateTime dateValue;
if (DateTime.TryParse(string_date, out dateValue))
Console.WriteLine("Converted '{0}' to {1}.", string_date, dateValue);
else
Console.WriteLine("Unable to convert '{0}' to a date.", string_date);
return dateValue;
}
public static void Main()
{
string inString = "05/01/2009 06:32:00";
GetDate(inString);
}
}
}
/**
* Output:
* Converted '05/01/2009 06:32:00' to 5/1/2009 6:32:00 AM.
* */
Try the following code
Month = Date = DateTime.Now.Month.ToString();
Year = DateTime.Now.Year.ToString();
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;
DateTime.ParseExact(DateTime, Format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite)
for example:
DateTime.ParseExact("2011-03-21 13:26", "yyyy-MM-dd hh:mm", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite);

How to convert any type of date to dd/mm/yyyy

I receive text from a *.csv file in any date format
For example: dd/mm/yy or dd/mm/yyyy or mm/dd/yyyy or 4 may 2010......
How I can convert to just a single type of format: dd/mm/yyyy ?
I'm working on C#, .NET 3.5, WinForms
Thanks in advance
If you're receiving data in multiple formats and you can't identify them, you've got problems. What does "09/07/2010" mean? September 7th or July 9th? This is the first thing to get straight in your mind, and it has nothing to do with technology. You have two contradictory formats - how are you going to deal with them? Sample the file and pick whichever looks most likely? Treat each line separately, favouring one format over another? Ask the user?
Once you've parsed the data correctly, formatting it in the desired way is easy, as per John's answer. Note that you must use "MM" for the month, not "mm" which represents minutes. You should also specify which culture to use (affecting the date separators) assuming you don't just want to take the system default.
DateTime.Parse("your data").ToString("dd/MM/yyyy");
Check out TryParseExact.
public static string FormatDate(string input, string goalFormat, string[] formats)
{
var c = CultureInfo.CurrentCulture;
var s = DateTimeStyles.None;
var result = default(DateTime);
if (DateTime.TryParseExact(input, formats, c, s, out result))
return result.ToString(goalFormat);
throw new FormatException("Unhandled input format: " + input);
}
Example Usage
var formats - new[] { "dd/MM/yy", "dd/MM/yyyy" };
var next = csvReader.Get("DateField");
var formattedDate = FormatDate(next, "dd/MM/yyyy", formats);
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
namespace dateconvert
{
class Program
{
static void Main(string[] args)
{
DateTime x = Convert.ToDateTime("02/28/10");
Console.WriteLine(string.Format(x.ToString("d", DateTimeFormatInfo.InvariantInfo)));
DateTime y = Convert.ToDateTime("May 25, 2010");
Console.WriteLine(string.Format(y.ToString("d", DateTimeFormatInfo.InvariantInfo)));
DateTime z = Convert.ToDateTime("12 May 2010");
Console.WriteLine(string.Format(z.ToString("d", DateTimeFormatInfo.InvariantInfo)));
Console.Read();
}
}
}
String.Format("{0:MM/dd/yyyy}", DateTime.Now);
String.Format("{0:dd/MM/yyyy}", DateTime.Now);
etc.
Source: http://www.csharp-examples.net/string-format-datetime/
You simply want to be using the DateTime.ParseExact together with the DateTime.ToString methods.
The straight DateTime.Parse method has its uses of course, and can be clever for parsing dates that you know are in a specific culture/locale, but since it seems dates given to you may be in an arbitrary format that cannot be recognised, you may want to specifically use ParseExact.
Example:
var myDate = DateTime.ParseExact("07/14/2010", "MM/dd/yyyy",
CultureInfo.CurrentCulture);
var standardDateString = myDate.ToString("dd/MM/yyyy");

Categories