Validate DateTime before inserting it into SQL Server database - c#

Is there any way to validate datetime field before inserting it into appropriate table?
Trying to insert with try/catch block is not a way.
Thanks,

Not sure if I'm being overly pedantic there, but DateTime.TryParse will validate whether a value is a valid DateTime object. OP asked about verifying a value before inserting into SQL Server datetime. The range of acceptable values for a SQL Server datetime is "January 1, 1753, through December 31, 9999" That does not hold true for DateTime .NET objects. This script assigns a value of "1/1/0001 12:00:00 AM" to badDateTime and it successfully parses.
DateTime d = DateTime.MinValue;
string badDateTime = DateTime.MinValue.ToString();
Console.WriteLine(badDateTime);
DateTime.TryParse(badDateTime, out d);
However, if you attempted to store that into a datetime field, it would fail with "The conversion of a varchar data type to a datetime data type resulted in an out-of-range value."
A commenter asked why I used 997 for milliseconds, this is covered under SQL Server 2008 and milliseconds but saving you a click, 997 is the largest value you can store in a datetime datatype. 998 will be rounded up to 1 second with 000 milliseconds
/// <summary>
/// An initial pass at a method to verify whether a value is
/// kosher for SQL Server datetime
/// </summary>
/// <param name="someval">A date string that may parse</param>
/// <returns>true if the parameter is valid for SQL Sever datetime</returns>
static bool IsValidSqlDatetime(string someval)
{
bool valid = false;
DateTime testDate = DateTime.MinValue;
DateTime minDateTime = DateTime.MaxValue;
DateTime maxDateTime = DateTime.MinValue;
minDateTime = new DateTime(1753, 1, 1);
maxDateTime = new DateTime(9999, 12, 31, 23, 59, 59, 997);
if (DateTime.TryParse(someval, out testDate))
{
if (testDate >= minDateTime && testDate <= maxDateTime)
{
valid = true;
}
}
return valid;
}
This is probably a better approach as this will attempt to cast the DateTime object into an actual sql datetime data type
/// <summary>
/// An better method to verify whether a value is
/// kosher for SQL Server datetime. This uses the native library
/// for checking range values
/// </summary>
/// <param name="someval">A date string that may parse</param>
/// <returns>true if the parameter is valid for SQL Sever datetime</returns>
static bool IsValidSqlDateTimeNative(string someval)
{
bool valid = false;
DateTime testDate = DateTime.MinValue;
System.Data.SqlTypes.SqlDateTime sdt;
if (DateTime.TryParse(someval, out testDate))
{
try
{
// take advantage of the native conversion
sdt = new System.Data.SqlTypes.SqlDateTime(testDate);
valid = true;
}
catch (System.Data.SqlTypes.SqlTypeException ex)
{
// no need to do anything, this is the expected out of range error
}
}
return valid;
}

Try this without hardcoding sql dateTime value:
public bool IsValidSqlDateTime(DateTime? dateTime)
{
if (dateTime == null) return true;
DateTime minValue = (DateTime)System.Data.SqlTypes.SqlDateTime.MinValue;
DateTime maxValue = (DateTime)System.Data.SqlTypes.SqlDateTime.MaxValue;
if (minValue > dateTime.Value || maxValue < dateTime.Value)
return false;
return true;
}

This is another take on billinkc's answer. However, in this method the .Value property of the min/max is used to avoid parsing and try/catch. Someone mentioned they wanted to ensure they are inserting a valid date into SQL Server. So, I took the approach of returning a date that is valid for SQL Server. This could easily be changed to a boolean method that checks to see if the dateToVerify is a valid SQL Server date.
protected DateTime EnsureValidDatabaseDate(DateTime dateToVerify)
{
if (dateToVerify < System.Data.SqlTypes.SqlDateTime.MinValue.**Value**)
{
return System.Data.SqlTypes.SqlDateTime.MinValue.Value;
}
else if (dateToVerify > System.Data.SqlTypes.SqlDateTime.MaxValue.**Value**)
{
return System.Data.SqlTypes.SqlDateTime.MaxValue.Value;
}
else
{
return dateToVerify;
}
}

<asp:RangeValidator runat="server" ID="rgvalDate" ControlToValidate="txtDate" Text="[Invalid]" Type="Date" MinimumValue="1/1/1753" MaximumValue="12/31/9999" />
OR
custom validator:
protected void cvalDOB_ServerValidate(object sender, ServerValidateEventArgs e)
{
e.IsValid = IsValidSqlDateTime(e.Value);
}
public static bool IsValidSqlDateTime(object Date)
{
try
{
System.Data.SqlTypes.SqlDateTime.Parse(Date.ToString());
return true;
}
catch
{
return false;
}
}

Here is a class with an extension method to allow a check such as if(myDateTime.IsValidSqlDateTime()) { ... }:
public static class DateTimeExtensionMethods
{
public static bool IsValidSqlDateTime(this DateTime dateTime)
{
return !(dateTime < (DateTime) SqlDateTime.MinValue ||
dateTime > (DateTime) SqlDateTime.MaxValue);
}
}

Could you provide a bt more information on where the datetime value is coming from; a web form?
You could simply add a CompareValidator as follows
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="txtDate"
Type="Date"
ErrorMessage="CompareValidator">
</asp:CompareValidator>

If you are mentioning about server side validation of your DateTime field, use DateTime.TryParse. A quick and dirty example will be
DateTime dateValue;
string dateString = "05/01/2009 14:57:32.8";
if (DateTime.TryParse(dateString, out dateValue))
{
// valid date comes here.
// use dateValue for this
}
else
{
// valid date comes here
}

DateTime.TryParse is the best validator
DateTime temp;
if(DateTime.TryParse(txtDate.Text, out temp))
//Works
else
// Doesnt work

Related

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.

DateTime.TryParse -> "Ghost ticks"

i've the following unit test, which fails on the machine of one of our developers (He get some ticks in the result variable, while the datetime variable is at zero ticks), but runs well on every other machine.
[TestMethod]
public void DateTimeStringDateTimeMinCurrentCultureToNullableDateTimeSuccessTest()
{
var dateTime = new DateTime(1, 1, 1);
string value = dateTime.ToString();
var result = value.ToNullableDateTime();
Assert.AreEqual(dateTime, result);
}
Here's the used extension method:
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// Uses the current culture.
/// </summary>
public static DateTime? ToNullableDateTime(this string s)
{
//Don't use CultureInfo.CurrentCulture to override user changes of the cultureinfo.
return s.ToNullableDateTime(CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name));
}
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// </summary>
public static DateTime? ToNullableDateTime(this string s, CultureInfo cultureInfo)
{
if (String.IsNullOrEmpty(s)) return null;
DateTime i;
if (DateTime.TryParse(s, cultureInfo, DateTimeStyles.None, out i)) return i;
return null;
}
I think this might be related to some windows date time settings he uses. In theory the ToNullableDateTime(string) should create a new culture info, which is user machine neutral. GetCultureInfo should call new CultureInfo(name, false). The only thing I could come up with, is that there's a cached culture info, which contains some kind of user machine related modified datetime in the s_NameCachedCultures which is checked in the GetCultureInfoHelper (http://referencesource.microsoft.com/#mscorlib/system/globalization/cultureinfo.cs,5fe58d4ecbba7689).
I know, that the CreateSpecificCulture method can return a user modified datetime, if you call it with the same culture than the windows machine. But I always thought, the GetDateTime would return a unmodified datetime in any case.
So there are two questions:
Could it be possible, that a modified CultureInfo be stored in the internal cache?
If so, is the only way to get a unmodified CultureInfo by manually calling new CultrueInfo("xy", false)?
When you do
string value = dateTime.ToString();
this will use CultureInfo.CurrentCulture. You then try and parse this string using...
CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name);
So you are specifically using a culture to parse the string that is different to the one you've created the string with. Of course there will be instances where this doesn't pass.
I'd suggest the issue is on most peoples machines
Assert.AreEqual(CultureInfo.CurrentCulture, CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name));
would pass but on the machine in question it doesn't and neither do your strings.
I'd suggest you probably want to use CultureInfo.InvariantCulture. So...
[TestMethod]
public void DateTimeStringDateTimeMinCurrentCultureToNullableDateTimeSuccessTest()
{
DateTime dateTime = new DateTime(1, 1, 1);
string value = dateTime.ToStringInvariant();
var result = value.ToNullableDateTime();
Assert.AreEqual(dateTime, result);
}
public static string ToStringInvariant(this DateTime? date)
{
if (date.HasValue)
return date.Value.ToStringInvariant();
return null;
}
public static string ToStringInvariant(this DateTime date)
{
return date.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// Uses the current culture.
/// </summary>
public static DateTime? ToNullableDateTime(this string s)
{
//Don't use CultureInfo.CurrentCulture to override user changes of the cultureinfo.
return s.ToNullableDateTime(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// </summary>
public static DateTime? ToNullableDateTime(this string s, CultureInfo cultureInfo)
{
if (String.IsNullOrEmpty(s)) return null;
DateTime i;
if (DateTime.TryParse(s, cultureInfo, DateTimeStyles.None, out i)) return i;
return null;
}
I've overseen a little detail. The problem wasn't related to the GetCultureInfo returning a modified datetime, the problem already startet with the dateTime.ToString(); which uses the Thread.CurrentThread.CultureInfo (which equals to the windows culture, which can be modified). The developers machine translates the DateTime(1, 1, 1) to 01.01.01 00:00:00. On any other machine the output is 01.01.0001 00:00:00. So an abbreviated version of the year is used, which seems to be interpreted as the "year 1 of the current century" in the TryParse method (quick sidenode: So it's not possible for users older than 100 years to tell their birthdate with the abbreviated year version).
It's actually an interesting behaviour..
for (int i = 0; i < 100; i++)
{
var year = 1900 + i;
DateTime date = new DateTime(year, 1, 1);
var parsedDate = DateTime.ParseExact(date.ToString("yy"), "yy", CultureInfo.InvariantCulture);
Console.WriteLine("{0}: {1}", year, parsedDate.ToString("yyyy"));
}
leads to:
[...]
1928: 2028
1929: 2029
1930: 1930
1931: 1931
[...]
So, the abbreviated birth date for anyone older 86 would lead to a date in the feature.. but this moves away from the questions context ..
I think there's no real solution the actual problem (beside telling developers to not use local CultureInfos outside of UI strings and never use abbreviated dates for inputs).
We don't have such problems in our code itself, because we use CultureInfo.InvariantCulture for all internal stuff. I just think about the unit test .. I think the test itself is correct. It shows, that the function actually doesn't work correct with abbreviated dates. I will change the behaviour of the ToNullableDateTime() to throw an exception, if a datetime string with a abbreviated year is recognized.
I guess, most probably the CultureInfo is in a cache. This would be very easy to check in a unit test (AreSame).
Instead of DateTime(1, 1, 1) use DateTime(1, 2, 3) and check the ghost ticks. Are they found in the string somewhere? Did you try the same culture (hard-coded culture name) on two machines which had different results?
Also check if the difference is the offset to UTC in your culture.
You probably can use TryParseExact.

Trouble with DateTime.ParseExact

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.

Convert the TimeSpan datatype to DateTime?

I'm converting a small MSAccess application to a web-based ASP.NET app, using C# 3.5. I was wondering what's the best way to work with dates in C#, when converting some of this VBA code over to C#.
Here is an example of the VBA Code:
Coverage1=IIf(IsNull([EffDate1]),0,IIf([CurrDate]<=[EndDate1],[CurrDate]-[EffDate1],[EndDate1]-[EffDate1]+1))
Here is what my current C# code looks like with the errors denoted in the commented code:
public DateTime CalculateCoverageOne(DateTime dateEffDateOne, DateTime dateCurrentDate, DateTime dateEndDateOne)
{
if (dateCurrentDate.Date <= dateEndDateOne.Date)
{
return null; //Get "cannot convert null to System.DateTime because it is a non-nullable value type" error
}
else
{
if (dateCurrentDate.Date <= dateEndDateOne)
{
return dateCurrentDate.Subtract(dateEffDateOne); //Gets error "cannot implicitly convert system.timepsan to system.datetime
}
else
{
return dateEndDateOne.Subtract(dateEffDateOne.AddDays(1)); //Gets error "cannot implicitly convert system.timepsan to system.datetime
}
}
}
cannot convert null to System.DateTime because it is a non-nullable value type" error
The DateTime type is a value type, which means that it cannot hold a null value. To get around this you can do one of two things; either return DateTime.MinValue, and test for that when you want to use the value, or change the function to return DateTime? (note the question mark), which is a nullable DateTime. The nullable date can be used like this:
DateTime? nullable = DateTime.Now;
if (nullable.HasValue)
{
// do something with nullable.Value
}
cannot implicitly convert system.timepsan to system.datetime
When you subtract a DateTime from another DateTime, the result is a TimeSpan, representing the amount of time between them. The TimeSpan does not represent a specific point in time, but the span itself. In order to get the date, you can use the Add method or the Subtract method overload of a DateTime object that accepts a TimeSpan. Exactly how that should look I can't say, since I don't know what the different dates in your code represent.
In the last case, you can simply use the return value from the AddDays method, but with a negative value (in order to subtract one day, instead of adding one):
return dateEffDateOne.AddDays(-1);
It looks like your VB is actually returning a time span, presumably in days. Here's the closest direct translation:
public TimeSpan CalculateCoverageOne(DateTime EffDate1, DateTime CurrDate, DateTime? EndDate1)
{
return (EndDate1 == null) ? TimeSpan.Zero :
(CurrDate < EndDate1) ? (CurrDate - EffDate1) :
(EndDate1.AddDays(1) - EffDate1);
}
If instead you just wanted a count of days, just return the TimeSpan's Days property:
public int CalculateCoverageOne(DateTime EffDate1, DateTime CurrDate, DateTime? EndDate1)
{
return ((EndDate1 == null) ? TimeSpan.Zero :
(CurrDate < EndDate1) ? (CurrDate - EffDate1) :
(EndDate1.AddDays(1) - EffDate1)).Days;
}
And for good measure, this is how I would clean up your final version:
public int CalculateCoverageOne(DateTime dateCurrentDate, DateTime dateEffectiveDate, DateTime dateEffDateOne, DateTime dateEndDateOne)
{
TimeSpan ts;
if (dateEffDateOne == DateTime.MinValue)
{
ts = TimeSpan.Zero;
}
else if (dateEffectiveDate <= dateEndDateOne)
{
ts = dateCurrentDate - dateEffDateOne;
}
else
{
ts = (dateEndDateOne - dateEffDateOne) + new TimeSpan(1, 0, 0, 0);
}
return ts.Days;
}
Get the TimeSpan, then subtract that from the DateTime to get the date you want. For your inner IF statement, it would look like this:
TimeSpan estSpan = dateCurrentDate.Subtract(dateEffDateOne);
return dateCurrentDate.Subtract(estSpan);
EDIT: You may also want to return DateTime.MaxValue and have the calling function check for the max value, instead of returning null.
DateTime is a value type. So, you cannot assign null to DateTime.
But you can use a special value like DateTime.MinValue to indicate whatever you were trying to indicate by null.
DateTime represents a date (and time), like "July 22, 2009". This means, you shouldn't use this type to represent time interval, like, "9 days". TimeSpan is the type intended for this.
dateCurrentDate.Subtract(dateEffDateOne) (or, equivalently, dateCurrentDate-dateEffDateOne) is a difference between two dates, that is, time interval. So, I suggest you to change return type of your function to TimeSpan.
TimeSpan is also a value type, so you could use, for instance, TimeSpan.Zero instead of null.
After some excellent answers (I've up-voted you guys), I've finally hammered out what I think is my answer. Turns out that returning an int, as the number of days, is what worked for me in this situation.
Thanks everyone, for providing your awesome answers. It helped me get on the right track.
public int CalculateCoverageOne(DateTime dateCurrentDate, DateTime dateEffectiveDate, DateTime dateEffDateOne, DateTime dateEndDateOne)
{
//Coverage1=
//IIf(IsNull([EffDate1]),0,
//IIf([CurrDate]<=[EndDate1],
//[CurrDate]-[EffDate1],
//[EndDate1]-[EffDate1]+1))
if (dateEffDateOne.Equals(TimeSpan.Zero))
{
return (TimeSpan.Zero).Days;
}
else
{
if (dateEffectiveDate <= dateEndDateOne)
{
return (dateCurrentDate - dateEffDateOne).Days;
}
else
{
return (dateEndDateOne - dateEffDateOne).Add(new TimeSpan(1, 0, 0, 0)).Days;
}
}
}

C# Julian Date Parser

I have a cell in a spreadsheet that is a date object in Excel but becomes a double (something like 39820.0 for 1/7/2009) when it comes out of C1's xls class. I read this is a Julian date format. Can someone tell me how to parse it back into a DateTime in C#?
Update: It looks like I might not have a Julian date, but instead the number of days since Dec 30, 1899.
I think Excel is just using the standard OLE Automation DATE type which can be converted with the DateTime.FromOADate method.
This block of code,
using System;
namespace DateFromDouble
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(DateTime.FromOADate(39820.0));
}
}
}
outputs:
1/7/2009 12:00:00 AM
There's a JulianCalendar class in System.Globalization; Here's how you would use it:
JulianCalendar c = new JulianCalendar();
DateTime time = c.ToDateTime(2009, 1, 7, 0, 0, 0, 0);
Console.WriteLine(time.ToShortDateString());
EDIT:
If it is in fact days since "1900" here's how you can do it:
public static DateTime DaysSince1900(int days)
{
return new DateTime(1900, 1, 1).AddDays(days);
}
DateTime time = DaysSince1900(39820);
Console.WriteLine(time.ToShortDateString()); //will result in "1/9/2009"
That number looks like a 'number of days since 1900' value.
There are two ways to do this CONVERSION --
OLE Automation Date method --
double doubleDate = 43153.0;
DateTime normalDate = DateTime.FromOADate(doubleDate);
/* {2/22/2018 12:00:00 AM} */
DateAdd() Method, using this method, you can specify the kind of date to convert to
double doubleDate = 43153.0;
DateTime normalDate = new DateTime(1899, 12, 30, 0, 0, 0, DateTimeKind.Utc).AddDays(doubleDate);
/* {2/22/2018 12:00:00 AM} */
Note: This date constant {12h December 31, 1899} is called the Dublin Julian Day documented in Julian Day
When dealing with Excel dates, the date may be the string representation of a date, or it may be an OA date. This is an extension method I wrote a while back to help facilitate the date conversion:
/// <summary>
/// Sometimes the date from Excel is a string, other times it is an OA Date:
/// Excel stores date values as a Double representing the number of days from January 1, 1900.
/// Need to use the FromOADate method which takes a Double and converts to a Date.
/// OA = OLE Automation compatible.
/// </summary>
/// <param name="date">a string to parse into a date</param>
/// <returns>a DateTime value; if the string could not be parsed, returns DateTime.MinValue</returns>
public static DateTime ParseExcelDate( this string date )
{
DateTime dt;
if( DateTime.TryParse( date, out dt ) )
{
return dt;
}
double oaDate;
if( double.TryParse( date, out oaDate ) )
{
return DateTime.FromOADate( oaDate );
}
return DateTime.MinValue;
}
Just format the cell(s) in question as Date, use CTRL+1, and select the your desired format.

Categories