To get the date difference between a datetime and nullable datetime - c#

I have two dates where one field is a datetime field and the other is a nullable datetime field. I want to subtract date2 from date 1 and if date2 is null subtract it from today.
My code :
Model :
public class SampleViewModel
{
public DateTime Date1 {get;set;}
public DateTime? Date2 {get;set;}
public int Diff {get;set;}
}
Controller Action :
public ActionResult Index()
{
var today = DateTime.Now;
SampleViewModel model = new SampleViewModel();
model.Date1=today;
model.Date2 = today.AddDays(5);
model.Diff = (model.Date1 - (model.Date2 != null ? model.Date2 : today)).Days;//Error
return View(model);
}
I am getting a compilation error
Error CS1061 'TimeSpan?' does not contain a definition for 'Days' and no accessible extension method 'Days' accepting a first argument of type 'TimeSpan?' could be found (are you missing a using directive or an assembly reference?)
What am I doing wrong?

TimeSpan? means Nullable<TimeSpan> which is a different structure from TimeSpan and it doesn't have a Days property.
Instead of that, you can use it's Value property and you can call it's Days after it like;
model.Diff = (model.Date1 - (model.Date2 != null ? model.Date2 : today)).Value.Days;
Or as commented by juharr, you can use model.Date2 ?? today which returns model.Date2 if it's not null or returns today if model.Date2 is null like;
model.Diff = (model.Date1 - (model.Date2 ?? today)).Days;

public class SampleViewModel
{
public DateTime Date1 {get;set;}
public DateTime? Date2 {get;set;}
public int Diff {get;set;}
}
public ActionResult Index()
{
var today = DateTime.Now;
SampleViewModel model = new SampleViewModel();
model.Date1=today;
model.Date2 = today.AddDays(5);
model.Diff = (int)(model.Date1 - (model.Date2 ?? today)).TotalDays;
return View(model);
}

You can use the null coalescing operator (??) to make sure you are using a non-nullable value in the calculation like this.
model.Diff = (model.Date1 - (model.Date2 ?? today)).Days;
This ensures that the calculation is done on two DateTimes instead of a DateTime and a DateTime?. When one value in a calculation is nullable the result is going to be nullable.

I would suggest to move your logic inside your model and use the null coalescing operator :
public class SampleViewModel
{
public SampleViewModel(DateTime date1, DateTime? date2 = null){
Date1 = date1;
Date2 = date2;
}
public DateTime Date1 { get; set; }
public DateTime? Date2 { get; set; }
public int Diff => (Date1 - (Date2 ?? DateTime.Now)).Days;
}
Usage:
public ActionResult Index()
{
var today = DateTime.Now;
var model = new SampleViewModel(today, today.AddDays(5));
return View(model);
}

Related

MVC Form Validation | Custom "Validation Attribute" not receiving the right "value"

THE CONTEXT
I have an MVC form that includes two date pickers, for which I have written a custom validation attribute to ensure that both selected dates are not in the past (>= today).
THE PROBLEM
The validation attribute works only for the first date but not for the second, because the value passed to the "value" object in the validation attribute is always reset to the value set in the form class (HomeForm.cs) constructor (HomeForm( )).
Example (VS + browser screenshot):
The picked date is the 10/08/2021 but the Object value for dateB is 16/08/2021.
issue example
THE CODE
This is my form (HomeForm.cs):
public class HomeForm
{
public HomeForm()
{
dateA = DateTime.Now.Date;
TimeSpan time = new TimeSpan(1, 0, 0, 0);
dateB = DateTime.Now.Date + time;
}
[Required(ErrorMessage = "You must select a departure date.")]
[DataType(DataType.Date)]
[DisplayName("Departure date:")]
[DateTimeRange]
public DateTime dateA { get; set; }
[Required(ErrorMessage = "You must select a return date.")]
[DataType(DataType.Date)]
[DisplayName("Return date:")]
[DateTimeRange]
public DateTime dateB { get; set; }
};
A custom Validation Attribute "DateTimeRange" is applied to dateA and dateB:
[AttributeUsage(AttributeTargets.Property)]
public class DateTimeRangeAttribute : ValidationAttribute
{
private readonly DateTime _today;
public DateTimeRangeAttribute()
{
_today = DateTime.Now.Date;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value is not DateTime)
return new ValidationResult("Date format is not valid.");
if (!IsValid((DateTime)value))
return new ValidationResult("Chosen date is in the past.");
if(value.ToString().Count() != 19)
return new ValidationResult("Not a valid date. Choose a different one.");
return ValidationResult.Success;
}
private bool IsValid(DateTime value)
{
value = value.Date;
return value >= _today;
}
}

How should I use the nullable type if it has a value

I have my Model:
[DataType(DataType.Date)]
public DateTime? Date1 { get; set; }
[DataType(DataType.Date)]
public DateTime? Date2 { get; set; }
[DataType(DataType.Date)]
public DateTime? Date3 { get; set; }
It's important for Data1 to Data3 to be DateTime?
My problem:
//Return right value
(model.Date1.ToString().AsDateTime().ToShortDateString() != "01.01.0001" ? model.Date1.ToString().AsDateTime().ToShortDateString() : "Is not known")
//Return bad value
(model.Date2.ToString().AsDateTime().ToShortDateString() != "01.01.0001" ? model.Date1.ToString().AsDateTime().ToShortDateString() : "Není známo")
//Return bad value
(model.Date3.ToString().AsDateTime().ToShortDateString() != "01.01.0001" ? model.Date3.ToString().AsDateTime().ToShortDateString() : "Is not known")
I dont know how or why but input is same but Date2 and Date3 returns bad value...
Thanks for any help
You can use the nullable types as follows.
model.Date1.HasValue ? model.Date1.Value.ToShortDateString() : "Unknown";
You can also compare with default datetime value by model.Date1.GetValueOrDefault() != default(DateTime).
GetValueOrDefault will return default value if Date1 is null.
model.Date1.GetValueOrDefault() != default(DateTime) ? model.Date1.Value.ToShortDateString() : "Unknown";

Conditional logic in the class property

public class test
{
public datetime date1 {get;set;}
public datetime date2 {get;set;}
public string status {get;set;}
}
Now the value of property status is calculated
based on the values for date1 and date2
For example
if date1 > dataetime.today
status ="active"
else
status = "inactive"
I think I need to write the logic in the set of the property status.
How do I achieve this ?
public class test
{
public datetime date1 {get;set;}
public datetime date2 {get;set;}
public string status {
get{
if (date1 > dataetime.today)
return "active";
else
return "inactive" ;
}
}
}
If you want it to always be synchronized with date1 then you should make status a getter.
public string status
{
get
{
return date1 > DateTime.Today ? "Active" : "Inactive";
}
}
Note: I would strongly recomend you to follow C# Capitalization Conventions
public string Status
{
get
{
// your code
}
set
{
// your code
}
}
You can read more about get and set accessors here.

calculating age in c# simple yet

I have a property DateOfBirth and a property Age.
DateOfBirth is DateTime datatype and Age is int datatype.
I want to calculate the person's age inside the constructor, and I have:
private int CalculateAge(DateTime birthDate, DateTime now)
{
int age = now.Year - birthDate.Year;
if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day))
{
age--;
}
return age;
}
public virtual DateTime? Dob { get; set; }
public virtual int Age { get; set; }
public MyObject()
{
Age = CalculateAge(Dob, DateTime.Now);
}
At compile time I'm getting the following errors:
The best overloaded method match for ... has some invalid arguments
and
cannot convert from 'System.DateTime?' to System.DateTime
The best overloaded method match for .... has some invalid arguments and cannot convert from 'System.DateTime?' to System.DateTime
So what did you try to solve this? The error is pretty clear: you're passing a System.DateTime? parameter to a function that accepts a System.DateTime.
To fix it, either change the method signature
CalculateAge(DateTime? birthDate, DateTime now)
{
if (!birthDate.HasValue)
{
return -1; // ?
}
}
But as you see, that's quite useless. So change the call:
if (Dob.HasValue)
{
Age = CalculateAge(Dob.Value, DateTime.Now);
}
Ultimately you'd just want to use a property for this:
public virtual int Age {
get
{
if (!Dob.HasValue)
{
throw new Exception(); // ?
return -1; // ?
}
return CalculateAge(Dob.Value);
}
}
As you see it doesn't matter where you solve this: you just have to check somewhere whether the nullable (?) date of birth contains a value.
You should pass a DateTime not a nullable DateTime
Age = CalculateAge((Dob.HasValue ? Dob.Value : DateTime.Now), DateTime.Now);
Or change the receiving method
private int CalculateAge(DateTime? birthDate, DateTime now)
and apply all the check needed to avoid NullReferenceExceptions
You CalculateAge method accepts a DateTime parameter, and you are passing it a DateTime? (nullable DateTime). You must change one of these, or cast to a DateTime.
Futhermore, there is no real reason for the second parameter, as DateTime.Now can be calculated inside the method.
Thirdly, see similar questions on SO for calculating age: Calculate age in C#
Look at your method declaration
private int CalculateAge(DateTime birthDate, DateTime now)
And DateOfBirth declaration
public virtual DateTime? Dob { get; set; }
You cannot use nullable DateTime property as a first parameter. Change declaration to
private int CalculateAge(DateTime? birthDate, DateTime now)
or remove nullability from Dob property
public virtual DateTime Dob { get; set; }
You can use
public static int GetAge(DateTime birthDate)
{
DateTime n = DateTime.Now; // To avoid a race condition around midnight
int age = n.Year - birthDate.Year;
if (n.Month < birthDate.Month || (n.Month == birthDate.Month && n.Day < birthDate.Day))
age--;
return age;
}
use private int CalculateAge(DateTime? birthDate, DateTime now)
instead of
private int CalculateAge(DateTime birthDate, DateTime now)
Use TimeSpan to get the difference between the two dates as mentioned here:
private int CalculateAge(DateTime birthDate, DateTime now)
{
TimeSpan span = now.Subtract(birthDate);
return (int)span.TotalDays / 365;
}
Change method definition and check if birthDate has value (is not null)
private int CalculateAge(DateTime? birthDate, DateTime now)
{
if(birthDate.HasValue)
{
int age = now.Year - birthDate.Year;
if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day))
{
age--;
}
return age;
}
else
return 0;
}
You will have to cast your DateTime? to DateTime like so
(DateTime)Dob
But why bother making Dob nullable in the first place if you are not handling the possibility of a null date anywhere in your code?

How to compare properties between two objects

I have two similar classes : Person , PersonDto
public class Person
{
public string Name { get; set; }
public long Serial { get; set; }
public DateTime Date1 { get; set; }
public DateTime? Date2 { get; set; }
}
&
public class PersonDto
{
public string Name { get; set; }
public long Serial { get; set; }
public DateTime Date1 { get; set; }
public DateTime? Date2 { get; set; }
}
I have two objects of both by equal values.
var person = new Person { Name = null , Serial = 123, Date1 = DateTime.Now.Date, Date2 = DateTime.Now.Date };
var dto = new PersonDto { Name = "AAA", Serial = 123, Date1 = DateTime.Now.Date, Date2 = DateTime.Now.Date };
I need to check value of all properties in two classes by reflection. My final goal is defined difference value of this properties.
IList diffProperties = new ArrayList();
foreach (var item in person.GetType().GetProperties())
{
if (item.GetValue(person, null) != dto.GetType().GetProperty(item.Name).GetValue(dto, null))
diffProperties.Add(item);
}
I did this, but result is not satisfactory. Count of diffProperties for result was 4but count of my expect was 1.
Of course all properties can have null values.
I need to a solution generic.
What must do I?
If you want to stick with comparison via reflection you should not use != (reference equality which will fail most of comparisons for boxed results of GetProperty calls) but instead use static Object.Equals method.
Sample how to use Equals method to compare two object in your reflection code.
if (!Object.Equals(
item.GetValue(person, null),
dto.GetType().GetProperty(item.Name).GetValue(dto, null)))
{
diffProperties.Add(item);
}
You may consider making the Person class implement the IComparable interface and implementing the CompareTo(Object obj) method.
Implement IEquatable interface
Looking at you classes not all values can be null you have a nullable long.
But that said.
I also made something like this and used this website for it. Just make it so that it can accept 2 different objects. I can't share my code because of licensing sorry.

Categories

Resources