Nullable DateTimes and the AddDays() extension - c#

I have a DateTime variable that can either be null or a Datetime. I figured a nullable DateTime type would work, but I am getting an error telling me that said
Nullable<DateTime> does not have a definition for AddDays
. Is there any way to resolve this error?
DateTime? lastInvite = (DateTime?)Session["LastInviteSent"];
if ((string)Session["InviteNudgeFlag"] == "False" && ((lastInvite == null && DateTime.Now >= AcctCreation.AddDays(7)) || (int)Session["InviteCount"] > 0 && DateTime.Now >= lastInvite.AddDays(7)))
{
// Non important code here
}

You need to go through the "Value" property:
lastInvite.Value.AddDays(7)
Note that this will throw an exception if the DateTime is actually null. Luckily, there is another property you can use to test for this, "HasValue".
if (lastInvite.HasValue){ /* code */ }

You should consider trying to make your logic more readable:
var inviteNudgeFlag = bool.Parse((string) Session["InviteNudgeFlag"]);
if(!inviteNudgeFlag)
{
return;
}
var lastInvite = (DateTime?) Session["LastInviteSent"];
var inviteCount = (int) Session["InviteCount"];
var numOfDays = 7;
var now = DateTime.Now;
var weekSinceLastInvite = lastInvite.HasValue
? now >= lastInvite.Value.AddDays(numOfDays)
: now >= AcctCreation.AddDays(numOfDays);
var hasInvites = !lastInvite.HasValue || inviteCount > 0;
var canInvite = hasInvites && weekSinceLastInvite;
if(!canInvite)
{
return;
}

There are multiple ways to resolve this error.
Use Convert.ToDateTime() method which handles null datetime values.
Convert.ToDateTime(lastInvite).AddDays(7);
Use Value property
lastInvite.Value.AddDays(7)
Use GetValueOrDefault() method
lastInvite.GetValueOrDefault().AddDays(7)

If you look at the documentation for the Nullable<T> type, you'll see it only has a few members. The Value member will get you the T inside, if the Nullable<T> container has a value. The Nullable<T> doesn't borrow any members from the type T it wraps.

Related

csharp Value of '01/01/0001 00:00:00' is not valid how to handle?

if (File.Exists(settingsFile))
{
string[] lines = File.ReadAllLines(settingsFile);
if (lines.Length > 0)
{
trackBarHours.Value = Convert.ToInt32(optionsfile.GetKey("trackbarhours"));
trackBarMinutes.Value = Convert.ToInt32(optionsfile.GetKey("trackbarminutes"));
trackBarSeconds.Value = Convert.ToInt32(optionsfile.GetKey("trackbarseconds"));
savedMilliseconds = Convert.ToInt32(optionsfile.GetKey("milliseconds"));
dateTimePicker1.Value = Convert.ToDateTime(optionsfile.GetKey("timetargetvalue"));
richTextBox1.Text = optionsfile.GetKey("result");
}
}
because the key "timetargetvalue" is not yet created in the settingsFile because i didn't saved it yet for the first time the value of the key of "timetargetvalue" is '01/01/0001 00:00:00'
in that case that there is no yet the key hwo can i handle the datetime exception ?
dateTimePicker1 is a DateTimePicker control.
the exception is on the line :
dateTimePicker1.Value = Convert.ToDateTime(optionsfile.GetKey("timetargetvalue"));
System.ArgumentOutOfRangeException: 'Value of '01/01/0001 00:00:00' is not valid for 'Value'. 'Value' should be between 'MinDate' and 'MaxDate'.
Parameter name: Value'
what should i check against of so it will not throw the exception ?
DateTimePicker.Value must be above DateTimePicker.MinimumDateTime, which is 'January 1, 1753'.
When you haven't set the timetargetvalue, it will resolve to '01/01/0001 00:00:00', as you have seen, which is too early.
So you need to check the value before assigning it to DateTimePicker.Value.
You can do it like this:
DateTime tempDateTime = Convert.ToDateTime(optionsfile.GetKey("timetargetvalue");
dateTimePicker1.Value = tempDateTime >= DateTimePicker.MinimumDateTime ? tempDateTime : DateTimePicker.MinimumDateTime;
When dealing with a Struct such as DateTime that does not have any value we need to consider that this is not a class and can not be set to null. It must always have some value. (see https://learn.microsoft.com/en-us/dotnet/api/system.datetime?view=net-7.0)
The exception mentions in a round about way that the range of acceptable values is between dateTimePicker1.MinDate and dateTimePicker1.MaxDate so one option is to check if your value is within this range. But it's unlikely to be the best option. (see https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.datetimepicker.mindate?view=windowsdesktop-6.0)
I'm pretty sure that DateTime default value is equal to that of DateTime.Min but if you really wanted to check if the value is default then I would suggest comparing it to default(DateTime) would be better.
This pretty much covers the use of DateTime and value defaults when null is not an option. Which brings up a possibly more desirable option. Encapsulation.
We could instead encapsulate the DateTime struct into a Nullable class. The encapsulating class will be nullable and will also be able to present the encapsulated value through a property called Value. (see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types)
There are two ways to declare such a Nullable class, both of which compile to the same thing:
Nullable<DateTime> myNullableDate = null;
DateTime? anotherNullableDate = null;
Since the DateTime is encapsulated in a Nullable object we can start using a null reference check. We can also call a method on Nullable called HasValue which returns a bool (True if it has a value).
EDIT: I notice that you're not doing any checks before trying to parse the DateTime and then directly setting it into the DateTimePicker.Value which can accept a null value. (although setting null won't clear a previously set value).
As such perhaps what you might want to do is handle the scenario a bit better and then use a DateTime.TryParse() instead. (see https://learn.microsoft.com/en-us/dotnet/api/system.datetime.tryparse?view=net-7.0)
e.g. (not the most optimized code, but I think it's easier to follow along in a more verbose form)
private DateTime? LoadDateFromOptions(string key)
{
var rawValue = optionsfile.GetKey(key);
if (!string.IsNullOrEmpty(rawValue))
{
return null;
}
DateTime dateValue;
bool isSuccess = DateTime.TryParse(rawValue, out dateValue);
if (isSuccess)
{
return dateValue;
}
else
{
return null;
}
}
and then instead of having that exception you can load the value optionally a bit more like this:
var timeTarget = LoadDateFromOptions("timetargetvalue");
if (timeTarget != null)
{
dateTimePicker1.Value = timeTarget;
}

Assert Variable is not Null

I have a Variable with type DateTime?
In a Function I check it for being null and want to use it afterwards without always having to ?. every call. In e.g. Kotlin the IDE recognizes a check like that and asserts that the variable cannot be null afterwards. Is there a way to do this in C#?
DateTime? BFreigabe = getDateTime();
if (BFreigabe == null) return false;
TimeSpan span = BFreigabe - DateTime.Now;
//Shows Error because it.BFreigabe has the type DateTime?, even though it can't be null
Edit:
When using
TimeSpan span = BFreigabe.Value - DateTime.Now;
instead it works in this case because .Value doesn't have nullsafety at all. However, considering that this would compile even without the null check and just produce an error, the general question still remains. How can one persuade C# that a former nullable variable isn't nullable any more?
Edit 2
Casting DateTime on the Variable works.
TimeSpan span = (DateTime)BFreigabe - DateTime.Now;
Still not as safe as in Kotlin, but similar enough.
If you have the previous check, you can access the value. Nullable types always have two properties: HasValue and Value.
You could either cast to DateTime (Without the ?) or use the value property.
DateTime? BFreigabe = getDateTime();
if (!BFreigabe.HasValue == null)
return false;
TimeSpan span = BFreigabe.Value - DateTime.Now;
Or store the nullable variable in a non nullable variable:
DateTime? BFreigabe = getDateTime();
if (BFreigabe.HasValue == null)
{
DateTime neverNull = BFreigabe.Value;
TimeSpan span = neverNull - DateTime.Now;
}
This will get full editor support and guarantee that there is no NullReferenceExcpetion.
EDIT: Because your question states Assert. Assert usually means that we will throw an exception if the state is invalid.
In this case, omit the check for nullness. If you access var.Value while var is null, this will throw a NullReferenceException. This moves the responsibility to the caller.
Another option would be to not use the nullable variable. Either by converting it (see the second listing) or by not accepting Nullable types as a parameter.
function TimeSpan Calc(DateTime time)
{
// here we know for sure, that time is never null
}
How about this?
DateTime? BFreigabe = getDateTime();
if (!BFreigabe.HasValue) return false;
DateTime BFreigabeValue = BFreigabe.Value;
TimeSpan span = BFreigabeValue - DateTime.Now;
Try to convert NULL value to any value, that is irrelevant.
DateTime? BFreigabe = getDateTime();
if (BFreigabe == null) return false;
TimeSpan span = (BFreigabe??DateTime.Now) - DateTime.Now;

Nullable DateTime in C#

I have two questions related to DateTime assingments
DateTime? y = 1 == 1 ? null: DateTime.MaxValue;
DateTime? y = null; // assignment works as expected
Why the first assignment issues error of type conversion between null and DateTime?
Which is the preferred way for null assignments of DateTime? in c#.
DateTime? x = default(DateTime?); //prints null on console
DateTime? x = null; // prints null on console
DateTime? x = DateTime.MinValue; //print 01/01/0001
The second statement DateTime? y = null; is only an assignment of null to a nullable object.
Whereas the first is a conditional assignment, which assigns some value for the true state and some other value for the false; Here you are using the conditional operator for evaluating the condition. according to MSDN first_expression (executes if true) and second_expression*(executes if false)* must be of same type or an implicit conversion must exist from one type to the other. In our case both are different so The simple solution is doing an explicit conversion as like this:
DateTime? y = 1 == 1 ?(DateTime?) null : DateTime.MaxValue;
A1. Because in ternary operator both expressions/results should be of same type.
Acc. to MSDN Either the type of first_expression and second_expression must be the same, or an implicit conversion must exist from one type to the other.
In your question, null and DateTime.MinValue do not match and hence the error conversion between null and DateTime.
You can do
DateTime? y = 1 == 1 ? null : (DateTime?)DateTime.MaxValue;
This way both answers return an answer whose type is DateTime?.
A2. Normally there is no said/preferred way of assigning this. This depends on user convention. All three are good and depend on user requirements.
Because ?: Operator operator expects same type on both sides.
Either the type of first_expression and second_expression must be the same, or an implicit conversion must exist from one type to the other.
So solution will be like below:
DateTime? y = 1 == 1 ? (DateTime?)null : DateTime.MaxValue;
And for second question, this will be good way for null assignment
DateTime? x = null;
DateTime? y = 1 == 1 ? null: DateTime.MaxValue;
This statement is giving an assignment error not because of the null assignment to a variable it is because of using null assignment in ternary operator and as you are using a class type here you the ternary operator do not lead you to do this illegal stuff as per CLR specifications mentioned,It might give you a straight compilation error.
//Which is the preferred way for null assignments of DateTime? in c#.
DateTime? x = default(DateTime?); //prints null on console
DateTime? x = null; // prints null on console
DateTime? x = DateTime.MinValue; //print 01/01/0001
As per Specifications and guidelines provided the Class Types should not be assigned null in any scenario so as per standard you can use the min value(though you can use default value too but it might effect in type conversions when needed)
The second one that you mentioned. You need to cast null value in this time asmensioned by Sir Nikhil Agrawal.
Ternary
int y = 1;
DateTime? dt3 = y == 1 ? (DateTime?)null : DateTime.MinValue;
Traditional way
DateTime? dt3 = null;
if (y == 1)
dt3 = null;
else
dt3 = DateTime.MinValue;
if you want to cast null to nullable datetime then you can use
DateTime? dt = (DateTime?)null;

c# get and set accessors datetime

I am receiving unreachable code detected in my properties. This worked for regular string fields but not for DateTime data type.
private DateTime m_RenewalDate;
public DateTime M_RenewalDate
{
get { return m_RenewalDate != null ? m_RenewalDate : DateTime.MinValue; }
set { m_RenewalDate = value; }
}
this is my sqldatareader
reader.GetDateTime(reader.GetOrdinal("M_RENEWALDATE"))
DateTime is a value type, and can not be null. Therefore, the code in the getter is unreachable:
return m_RenewalDate != null ? // always evaluates to true
m_RenewalDate : // and therefore always returns this
DateTime.MinValue; // The code never hits this case.
If your field in the database can be null, perhaps you want to declare the property as a nullable DateTime: DateTime?.
DateTime is a value type and cannot be null. To compare with a null value in the database, use DBNull.Value
As driis said in his answer, m_RenewalDate == null is always false as DateTime is a value type and things declared to be value types cannot be null.
To have the get the behavior of returning DateTime.MinValue from M_RenewalDate in the case that M_RENEWALDATE is null your reader code should look more like this
object renewalDate = reader.GetValue(reader.GetOrdinal("M_RENEWALDATE"));
if (Equals(renewalDate, DBNull.Value))
{
yourObject.M_RenewalDate = DateTime.MinValue;
}
else
{
yourObject.M_RenewalDate = (DateTime) renewalDate;
}

Convert null field to zero before converting to int?

In my program, I'm looping through a datatable to get data from each field. One line of my code looks like this:
int LYSMKWh = Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"]);
Basically, I'm just taking the value that's in the cell and converting it to an int32. I run into a problem, though, when the data in the field is a null. I get an Invalid Cast error message about not being able to convert a "DBNull" to another type.
So, I know that I can just check the value of the field before I try to convert it by doing something like this:
if (resultsDT.Rows[currentRow]["LYSMKWh"] == null)
{
resultsDT.Rows[currentRow]["LYSMKWh"] = 0;
}
But, is there a better way to do this? Is there something I can do "inline" while I'm trying to convert the value without having to resort to using the if ?
EDIT: I did try using this suggested method:
int LYSMKWh = Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"] ?? "0");
Unfortunately, I still received the Invalid Cast error message with regards to the DBNull type. It was suggested that I could have problems using the ?? operator if the types on either side were different.
Also, since I have complete control over the data that I'm building my datatable with, I changed it so that no null values would be written to any field. So, that pretty much negates the need to convert a null to an int32, in this case. Thanks for all the advice, everyone!
You could do this:
var LYSMKWh =
resultsDT.Rows[currentRow]["LYSMKWh"].Equals(DBNull.Value)
? 0
: Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"]);
use the ?? operator:
resultsDT.Rows[currentRow][...] ?? "0"
(expecting the field to be a string - if not change the "0")
You may consider using C#'s ?? operator, which checks a value for null and if it's null, assigns a default value to it.
You can use the ?? operator:
object x = null;
int i = (int)(x ?? 0)
You can use a custom convert method:
public static int ConvertToInt32(object value, int defaultValue) {
if (value == null)
return defaultValue;
return Convert.ToInt32(value);
}
You may need overloads that take other types, like string. You may have problems with the ?? operator if the types on either side are different.
You can replace it with a ternary operator:
var rowData = resultsDT.Rows[currentRow]["LYSMKWh"];
int LYSMKWh = rowData != null ? Convert.ToInt32(rowData) : 0;
Alternatively, the ?? operator could work:
int LYSMKWh = Convert.ToInt32(resultsDT.Rows[currentRow]["LYSMKWh"] ?? 0);
But I think that's less readable.
You can use extension Method.
int LYSMKWh = resultsDT.Rows[currentRow]["LYSMKWh"].IfNullThenZero();
Create the below class
public static class Converter
{
public static Int32 IfNullThenZero(this object value)
{
if (value == DBNull.Value)
{
return 0;
}
else
{
return Convert.ToInt32(value);
}
}
}
Check if it's DBNull.Value instead of null:
if (resultsDT.Rows[currentRow]["LYSMKWh"] == DBNull.Value)
{
resultsDT.Rows[currentRow]["LYSMKWh"] = 0;
}
The ternary expression would work like this:
var LYSMKwhField = resultsDT.Rows[currentRow]["LYSMKWh"];
int LYSMKWh = LYSMKwhField != DBNull.Value ? Convert.ToInt32(rowData) : 0;

Categories

Resources