Trouble with DateTime.ParseExact - c#

I am working on a reminder application. The applications stores the reminder Date, Time and DateLastShown (in different fields) in the database and pulls them out to performs checks.
All dates are in "d/MM/yyyy" format. My problem is that when i pull the dates from the DB and try to store back into DateTime format they are still being shown in "M/d/yyyy" format which is not how the app needs to be.
I essentially need to pull the values from the DB do some checks to determine if it's time to show the reminder and do so. It seems rather straight forward, maybe i am making some small error.
Below is my code with comments.
Any help really appreciated.
public void CheckReminders()
{
IQueryable<Reminder> reminders;
DateTime reminderDate;
DateTime reminderTime;
DateTime reminderLastShown;
DateTime todayDate;
DateTime timeNow;
while (true)
{
try
{
db = new StudioManagementEntities();
reminders = from r in db.Reminders
select r;
foreach (Reminder r in reminders)
{
if (r.Enabled == 1)
{
if (r.Recurring == 1)
{
// This is the code i was using before when the date was in "M/d/yyyy" format
// which seems to be default.
reminderTime = DateTime.Parse(r.Time);
timeNow = DateTime.Parse(DateTime.Now.ToLongTimeString());
if (r.DateLastShown != DateTime.Today.ToShortDateString() && timeNow >= reminderTime)
{
FrmReminder frmReminder = new FrmReminder(r.Id, true);
frmReminder.ShowDialog();
r.DateLastShown = DateTime.Today.ToShortDateString();
}
}
else
{
// Now i need to pass in "d/M/yyyy" format but the
// code seems to return in "M/d/yyyy" format.
reminderDate = DateTime.ParseExact(r.Date, "d/MM/yyyy", null);
// Even this returns in wrong format
reminderDate = DateTime.ParseExact("24/01/2013", "d/MM/yyyy", null);
// Have tried with CultureInfo.InvariantCulture too.
MessageBox.Show(reminderDate.ToString());
return;
if (
r.DateLastShown != DateTime.Today.Date.ToShortDateString() //&&
//r.Date == DateTime.ParseExact(DateTime.Today, "d/MM/yyyy", CultureInfo.InvariantCulture).ToString() //&&
//now.TimeOfDay.TotalSeconds >= reminderTime.TimeOfDay.TotalSeconds
)
{
FrmReminder frmReminder = new FrmReminder(r.Id, true);
frmReminder.ShowDialog();
r.DateLastShown = DateTime.Today.ToShortDateString();
}
}
}
}
db.SaveChanges();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
// Check every minute
Thread.Sleep(60000);
}
}
And the DB table.

If the parsing into the date object is not erroring out, you are just having a problem with your output when you call .ToString().
From the docs:
The ToString method returns the string representation of the date and
time in the calendar used by the current culture.
If you need something other than the user's current culture settings, you can specify that using a format string in the overloaded ToString() method:
var reminderDate = DateTime.ParseExact("24/01/2013", "d/MM/yyyy", null);
MessageBox.Show(reminderDate.ToString("d/MM/yyyy"));
Also, as others have stated in comments, if possible you should be using the date data type in your database instead of storing the values as strings.

Related

Random date format read from Excel

I'm using OleDb to read data from an Excel file. The piece of code that reads the data is the following:
OleDbCommand oleDbCommand = new OleDbCommand(selectCommandText, oleDbConnection);
using (OleDbDataReader dr = oleDbCommand.ExecuteReader())
{
DataTable dt = new DataTable();
dt.Load(dr);
return dt;
}
The problem is that the data readed appears randomly as a string sometimes (for example "16.02.1995") or like a number - timestamp (41187), something like this convert Excel Date Serial Number to Regular Date.
Is there any way to solve this? I would like to read the data always as a format, don't matter if it's a number or a string.
Edit: I found that when I have the Excel file opened the date readed is in number format (date serial number) and when I don't have the file opened the date is in string format. Does somebody know why?
Edit2: The personalized format used in the date cell
To convert date number or date string to c# you need two different methods.
One to convert string and the other one to convert number to date format.
So, regarding converting string to date, there are TryParse method in c#, and regarding the number conversation to date there are already answer on that in SO.
Putting that together we can do some thing like:
public static DateTime? GetDateTime(object o)
{
DateTime? date;
try
{
date = FromStringToDate(o.ToString());
if (date == DateTime.MinValue)
{
date = FromExcelSerialDate((int)o);
}
}
catch (Exception e)
{
//log your exception
date = null;
}
return date;
}
private static DateTime FromExcelSerialDate(int serialDate)
{
if (serialDate > 59) serialDate -= 1; //Excel/Lotus 2/29/1900 bug
return new DateTime(1899, 12, 31).AddDays(serialDate);
}
private static DateTime FromStringToDate(string stringDate)
{
DateTime.TryParse(stringDate, out DateTime result);
return result;
}
To put that in use, in your main method for testing you can do some thing like:
List<object> excelData = new List<object>()
{
"16.02.1995",
41187,
13131.3242,
"",
null
};
foreach (object o in excelData)
{
var dateTime = GetDateTime(o);
if (dateTime != null)
{
Console.WriteLine(dateTime);
}
}
The output will be:
16-02-1995 00:00:00
05-10-2012 00:00:00
I have testing it i excel as well.
Note: This is just example, you might improve the methods, change the order, adding more protective lines so it does not break, for example if date is null, empty or wrong format in excel to fit you business logic.

Issue around utc date - TimeZoneInfo.ConvertTimeToUtc results in date change

Having an issue whereby the date I wish to save is changing from the onscreen selected date if the users selects a timezone that is ahead x number of hours.
E.g. they choose UTC+2 Athens and date of 25/02/2016 from the calendar pop-up, then date recorded will be 24/02/2016.
I've narrowed the reasoning down to the fact that the selected datetime is recorded as for example 25/02/2016 00:00:00 and with the 2 hour offset, this takes it to 24/02/2016 22:00:00
Having never worked with timezones before, or UTC dates/times, this is highly confusing.
Here is the code -
oObject.RefDate = itTimeAndDate.ParseDateAndTimeNoUTCMap(Request, TextBox_RefDate.Text);
if (!string.IsNullOrEmpty(oObject.TimeZoneDetails))
{
TimeZoneInfo oTimeZone = TimeZoneInfo.FindSystemTimeZoneById(oObject.TimeZoneDetails);
oObject.RefDate = itTimeAndDate.GetUTCUsingTimeZone(oTimeZone, oObject.RefDate);
}
RefDate would equate to something like 25/02/2016 00:00:00 once returned from ParseDateAndTimeNoUTCMap * (code below)*
static public itDateTime ParseDateAndTimeNoUTCMap(HttpRequest oTheRequest, string sValue)
{
DateTime? oResult = ParseDateAndTimeNoUTCMapNull(oTheRequest, sValue);
if (oResult != null)
return new itDateTime(oResult.Value);
return null;
}
/// <summary>
/// Translate a string that has been entered by a user to a UTC date / time - mapping using the
/// current time zone
/// </summary>
/// <param name="oTheRequest">Request context</param>
/// <param name="sValue">Date / time string entered by a user</param>
/// <returns>UTC date / time object</returns>
static public DateTime? ParseDateAndTimeNoUTCMapNull(HttpRequest oTheRequest, string sValue)
{
try
{
if (string.IsNullOrEmpty(sValue))
return null;
sValue = sValue.Trim();
if (string.IsNullOrEmpty(sValue))
return null;
if (oTheRequest != null)
{
const DateTimeStyles iStyles = DateTimeStyles.AllowInnerWhite | DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
// Create array of CultureInfo objects
CultureInfo[] aCultures = new CultureInfo[oTheRequest.UserLanguages.Length + 1];
for (int iCount = oTheRequest.UserLanguages.GetLowerBound(0); iCount <= oTheRequest.UserLanguages.GetUpperBound(0);
iCount++)
{
string sLocale = oTheRequest.UserLanguages[iCount];
if (!string.IsNullOrEmpty(sLocale))
{
// Remove quality specifier, if present.
if (sLocale.Contains(";"))
sLocale = sLocale.Substring(0, sLocale.IndexOf(';'));
try
{
aCultures[iCount] = new CultureInfo(sLocale, false);
}
catch (Exception) { }
}
else
{
aCultures[iCount] = CultureInfo.CurrentCulture;
}
}
aCultures[oTheRequest.UserLanguages.Length] = CultureInfo.InvariantCulture;
// Parse input using each culture.
foreach (CultureInfo culture in aCultures)
{
DateTime oInputDate;
if (DateTime.TryParse(sValue, culture.DateTimeFormat, iStyles, out oInputDate))
return oInputDate;
}
}
return DateTime.Parse(sValue);
}
catch (Exception)
{
}
return null;
}
Once returned from the above, the following lines are executed -
TimeZoneInfo oTimeZone = TimeZoneInfo.FindSystemTimeZoneById(oObject.TimeZoneDetails);
oObject.RefDate = itTimeAndDate.GetUTCUsingTimeZone(oTimeZone, oObject.RefDate);
It is within GetUTCUsingTimeZone that the problem seems to occur to me.
static public itDateTime GetUTCUsingTimeZone(TimeZoneInfo oTimeZone, itDateTime oDateTime)
{
if (oDateTime == null || oTimeZone == null)
return oDateTime;
DateTime oLocal = DateTime.SpecifyKind(oDateTime.Value, DateTimeKind.Unspecified);
DateTime oResult = TimeZoneInfo.ConvertTimeToUtc(oLocal, oTimeZone);
return new itDateTime(oResult);
}
I have checked TimezoneInfo for the offset value, and oResult always equates to the oLocal param - the offset. So 25/02/2016 00:00:00 with a 3 hour offset would equate to 24/02/2016 21:00:00
When the offset is -hours, it goes in the other direct, so oResult = oLocal + the offset, if that makes sense. So the main issue of the date changing is not occurring in those instances.
Obviously this is not what I want. I want the date to be what the user has selected, for their timezone.
Has anyone seen something like this before? Any possible solution?
I'm not entirely sure what I've done wrong.
If you need to maintain the correct timezone, you should be using the DateTimeOffset type instead of DateTime type.
DateTimeOffset maintains the offset from UTC so you never lose your timezone information and has a lot of useful methods like
UtcDateTime
From the horses mouth:
https://msdn.microsoft.com/en-us/library/system.datetimeoffset(v=vs.110).aspx
https://learn.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime
The fix was to run the following after grabbing the value from the db and before redisplaying it -
static public itDateTime FixUTCUsingTimeZone(TimeZoneInfo oTimeZone, itDateTime oDateTime)
{
if (oDateTime == null || oTimeZone == null)
return oDateTime;
DateTime oTime = DateTime.SpecifyKind(oDateTime.Value, DateTimeKind.Unspecified);
DateTime oResult = TimeZoneInfo.ConvertTimeFromUtc(oTime, oTimeZone);
return new itDateTime(oResult);
}
So essentially just doing the reverse of the ConvertTimeToUtc performed earlier. Not sure why this wasn't done originally, but there you go.

C# CSV Parsing- how to get correct format from string (date) representation?

I am parsing a CSV file. One of it's columns represents a date. The thing is that the date can be in dd/MM/yyyy and MM/dd/yyyy.
I can't identify which format is correct when for example the date there is 06/04/2015 (April or June?)
My code is doing this to format the output (standarized csv) when I can tell the difference. Do you know if there is a better method to this and how to Identify correct format?
public static DateTime? ToValidDateTime(this string date)
{
if (string.IsNullOrEmpty(date))
return null;
try
{
var r = DateTime.Parse(date, CultureInfo.InvariantCulture);
return new DateTime(r.Year, r.Month, r.Day, r.Hour, r.Minute, r.Second);
}
catch (Exception exception)
{
var r = DateTime.Parse(date);
return new DateTime(r.Year, r.Month, r.Day, r.Hour, r.Minute, r.Second);
}
}
This is not an answer, but it needs too much formating to fit as a comment. This code is redundant:
var r = DateTime.Parse(date);
return new DateTime(r.Year, r.Month, r.Day, r.Hour, r.Minute, r.Second);
You're creating two datetime instances for no good reason. Just the first one is good enough. You can just do this instead:
return DateTime.Parse(date);
The same holds for the version with the InvariantCulture format specifier.

how to compare two dates in datetimepicker

I am doing Windows project, in which I have two DateTimePicker controls, one for StartDate and other for EndDate.
In runtime, when user selects the StartDate and EndDate from that controls, it should read the textfile(i.e) log.txt & search line by line get the matching of both these dates and as well as in between dates and write the data to a textbox or label control
for eg in log file, data is like below:
3/12/2013 2:51:47 PM - ASDASDASD.D20131203145019
4/12/2013 2:52:23 PM - ASDDFSDSA.C20131203145019
5/12/2013 2:52:37 PM - SDASAFAS_20131203182101.D
6/12/2013 3:17:11 PM - RRRTWEWA_20131203184602.D00
7/12/2013 3:35:32 PM - XBCNXCXCXC.D0120131203153408
if I search from 5 dec 2013 to 7 dec 2013, it should retrieve:
5/12/2013 2:52:37 PM - SDASAFAS_20131203182101.D
6/12/2013 3:17:11 PM - RRRTWEWA_20131203184602.D00
7/12/2013 3:35:32 PM - XBCNXCXCXC.D0120131203153408
but up to now I am getting:
5/12/2013 2:52:37 PM - SDASAFAS_20131203182101.D
7/12/2013 3:35:32 PM - XBCNXCXCXC.D0120131203153408
I am retriving only the StartDate & EndDate matching data not in between date data.
The below are some of the coding I have tried:
try
{
// ...
string FDDate = FD.Date.ToString("M/d/yyyy");
string TDDate = TD.Date.ToString("M/d/yyyy");
string searchstring = EnterFileNameTextbox.Text.ToString();
string searchfromdate = FromDateTimePicker.Value.ToShortDateString();
string searchtodate = ToDateTimePicker.Value.ToShortDateString();
string line;
StringBuilder sb = new StringBuilder();
int counter = 0;
using (StreamReader file = new StreamReader(#"D:\log.txt"))
{
while ((line = file.ReadLine()) != null)
{
if (line.Contains(searchstring) && line.Contains(FDDate))
{
sb.AppendLine(line.ToString());
counter++;
}
else if (line.Contains(searchstring) && !line.Contains(FDDate))
{
if (FD.Date < TD.Date)
{
sb.AppendLine(line.ToString());
counter++;
FDDate = FD.Date.AddDays(1).ToShortDateString();
}
else if (FD.Date == TD.Date)
{
FDDate = FD.Date.ToShortDateString();
sb.AppendLine(line.ToString());
counter++;
}
}
}
}
ResultTextBox.Text = sb.ToString();
CountLabel.Text = counter.ToString();
}
catch (Exception ex)
{
MessageBox.Show("The file could not be read");
MessageBox.Show(ex.Message);
}
I am not getting idea to do the loop.
I would recommend using DateTime.TryParse (doc here) instead of just converting. That will allow you to provide valid formatting, handle failures (convert will throw if it fails).
The part about using the Date property seems fine, and comparison operators will work as expected.
Try and deal with DateTime datatypes here rather than converting everything to string. After reading the line from the file, parse out the date. Assuming it is always going to be the first element in the file, and is followed by a space, you could do this by using DateTime lineDate = DateTime.Parse(line.Split(" ")[0]);
Once you have the date from the file in that format, and you have DateTime fromDate and DateTime toDate obtained from your date pickers, you can write if(lineDate >= fromDate && lineDate <= toDate) sb.AppendLine(line);

check if date time string contains time

I have run into an issue. I'm obtaining a date time string from the database and and some of these date time strings does not contain time. But as for the new requirement every date time string should contain the time like so,
1)1980/10/11 12:00:01
2)2010/APRIL/02 17:10:00
3)10/02/10 03:30:34
Date can be in any format followed by the time in 24hr notation.
I tried to detect the existence of time via the following code,
string timestamp_string = "2013/04/08 17:30";
DateTime timestamp = Convert.ToDateTime(timestamp_string);
string time ="";
if (timestamp_string.Length > 10)
{
time = timestamp.ToString("hh:mm");
}
else {
time = "Time not registered";
}
MessageBox.Show(time);
But this only works for the No 1) type timestamps. May I please know how to achieve this task on how to detect if the time element exist in this date time string. Thank you very much :)
POSSIBLE MATCH
How to validate if a "date and time" string only has a time?
INFO the three answers provided by Arun Selva Kumar,Guru Kara,Patipol Paripoonnanonda are all correct and checks for the time and serves my purpose. But I select Guru Karas answer solely on ease of use and for the explanation he has given. Thank you very much :) very much appreciated all of you :)
The date time components TimeOfDay is what you need.
MSDN says "Unlike the Date property, which returns a DateTime value that represents a date without its time component, the TimeOfDay property returns a TimeSpan value that represents a DateTime value's time component."
Here is an example with consideration of all your scenarios.
Since you are sure of the format you can use DateTime.Parse else please use DateTime.TryParse
var dateTime1 = System.DateTime.Parse("1980/10/11 12:00:00");
var dateTime2 = System.DateTime.Parse("2010/APRIL/02 17:10:00");
var dateTime3 = System.DateTime.Parse("10/02/10 03:30:34");
var dateTime4 = System.DateTime.Parse("02/20/10");
if (dateTime1.TimeOfDay.TotalSeconds == 0) {
Console.WriteLine("1980/10/11 12:00:00 - does not have Time");
} else {
Console.WriteLine("1980/10/11 12:00:00 - has Time");
}
if (dateTime2.TimeOfDay.TotalSeconds == 0) {
Console.WriteLine("2010/APRIL/02 17:10:00 - does not have Time");
} else {
Console.WriteLine("2010/APRIL/02 17:10:00 - Has Time");
}
if (dateTime3.TimeOfDay.TotalSeconds == 0) {
Console.WriteLine("10/02/10 03:30:34 - does not have Time");
} else {
Console.WriteLine("10/02/10 03:30:34 - Has Time");
}
if (dateTime4.TimeOfDay.TotalSeconds == 0) {
Console.WriteLine("02/20/10 - does not have Time");
} else {
Console.WriteLine("02/20/10 - Has Time");
}
Try this,
DateTime myDate;
if (DateTime.TryParseExact(inputString, "dd-MM-yyyy hh:mm:ss",
CultureInfo.InvariantCulture, DateTimeStyles.None, out myDate))
{
//String has Date and Time
}
else
{
//String has only Date Portion
}
You can try using other format specifiers as listed here, http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
Combining the answers of Guru Kara and Patipol Paripoonnanonda with the .net globalisation API results in:
bool HasExplicitTime(DateTime parsedTimestamp, string str_timestamp)
{
string[] dateTimeSeparators = { "T", " ", "#" };
string[] timeSeparators = {
CultureInfo.CurrentUICulture.DateTimeFormat.TimeSeparator,
CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator,
":"};
if (parsedTimestamp.TimeOfDay.TotalSeconds != 0)
return true;
string[] dateOrTimeParts = str_timestamp.Split(
dateTimeSeparators,
StringSplitOptions.RemoveEmptyEntries);
bool hasTimePart = dateOrTimeParts.Any(part =>
part.Split(
timeSeparators,
StringSplitOptions.RemoveEmptyEntries).Length > 1);
return hasTimePart;
}
This approach:
detects explicit midnight times (e.g. "2015-02-26T00:00");
only searches the string when TimeOfDay indicates midnight or no explicit time; and
finds explicit midnight times in local format and any non midnight time in any format that .net can parse.
Limitations:
explicit midnight times in non culture local format are not detected;
explicit midnight times with less than two parts are not detected; and
less simple and elegant than the approaches of Guru Kara and Patipol Paripoonnanonda.
Here's what I'm going with for now. It may not be perfect, but likely better than considering any 12am datetime as not having a time. The premise is that if I tack a full time specification on the end it will parse if it's just a date but fail if it already has a time component.
I had to make the assumption that there's not some valid, date/time that has 7 non-white-space characters or less. It appears that "1980/10" parses, but not "1980/10 01:01:01.001".
I've included various test cases. Feel free to add your own and let me know if they fail.
public static bool IsValidDateTime(this string dateString, bool requireTime = false)
{
DateTime outDate;
if(!DateTime.TryParse(dateString, out outDate)) return false;
if (!requireTime) return true;
else
{
return Regex.Replace(dateString, #"\s", "").Length > 7
&& !DateTime.TryParse(dateString + " 01:01:01.001", out outDate);
}
}
public void DateTest()
{
var withTimes = new[]{
"1980/10/11 01:01:01.001",
"02/01/1980 01:01:01.001",
"1980-01-01 01:01:01.001",
"1980/10/11 00:00",
"1980/10/11 1pm",
"1980-01-01 00:00:00"};
//Make sure our ones with time pass both tests
foreach(var date in withTimes){
Assert.IsTrue(date.IsValidDateTime(), String.Format("date: {0} isn't valid.", date));
Assert.IsTrue(date.IsValidDateTime(true), String.Format("date: {0} does have time.", date));
}
var withoutTimes = new[]{
"1980/10/11",
"1980/10",
"1980/10 ",
"10/1980",
"1980 01",
"1980/10/11 ",
"02/01/1980",
"1980-01-01"};
//Make sure our ones without time pass the first and fail the second
foreach (var date in withoutTimes)
{
Assert.IsTrue(date.IsValidDateTime(), String.Format("date: {0} isn't valid.", date));
Assert.IsFalse(date.IsValidDateTime(true), String.Format("date: {0} doesn't have time.", date) );
}
var bogusTimes = new[]{
"1980",
"1980 01:01",
"80 01:01",
"1980T01",
"80T01:01",
"1980-01-01T01",
};
//Make sure our ones without time pass the first and fail the second
foreach (var date in bogusTimes)
{
DateTime parsedDate;
DateTime.TryParse(date, out parsedDate);
Assert.IsFalse(date.IsValidDateTime(), String.Format("date: {0} is valid. {1}", date, parsedDate));
Assert.IsFalse(date.IsValidDateTime(true), String.Format("date: {0} is valid. {1}", date, parsedDate));
}
}
Would this work for your use case:
bool noTime = _datetimevalue.TimeOfDay.Ticks == 0;
If no time component exists in the DateTime instance, Ticks will be 0.
Date and time are always separated by a space bar. The easiest way would be:
if (timestamp_string.Split(' ').Length == 2)
{
// timestamp_string has both date and time
}
else
{
// timestamp_string only has the date
}
This code assumes the date always exists.
If you want take it further (in case the date does not exist), you can do:
if (timestamp_string.Split(' ')
.Select(item => item.Split(':').Length > 1)
.Any(item => item))
{
// this would work for any string format that contains date, for example:
// 2012/APRIL/03 12:00:05 -> this would work
// 2013/04/05 09:00:01 -> this would work
// 08:50:45 2013/01/01 -> this would also work
// 08:50:50 -> this would also work
}
else
{
// no date in the timestamp_string at all
}
Hope this helps!

Categories