calculating age in c# simple yet - c#

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?

Related

To get the date difference between a datetime and nullable datetime

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);
}

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.

Timespan Specified cast is not valid

I am trying to display the time in a gridview column but when loading the information I get this error:
Specified cast is not valid.
protected override void FillObject(DataRow dr)
{
ID = Convert.ToInt32(dr["ID"]);
if (dr["Company_ID"] != DBNull.Value)
CompanyID = Convert.ToInt32(dr["Company_ID"]);
if (dr["LoginTime"] != DBNull.Value)
LoginTime = (TimeSpan)(dr["LoginTime"]); //error
}
Get/Set code:
public TimeSpan LoginTime { get; set; }
As #TimS. pointed out, you need to use a DateTime variable in your code to consume a Sql Server DATETIME. From that, you can convert it into a TimeSpan if necessary.
public DateTime LoginTime { get; set; }
LoginTime = (DateTime)(dr["LoginTime"]);
To convert between the two, see How to get TimeSpan from DateTime

IComparable sorting with DateTime

I had a similar question answered earlier, then my boss told me to sort it based on date so here I am again.
I have this -
List<User> users;
protected class User : IComparable<User>
{
public string name;
public string email;
public decimal total;
public string address;
public string company;
public string placed;
public string fulfilled;
public string origin;
public int CompareTo(User b)
{
// Alphabetic sort name[A to Z]
return this.placed.CompareTo(b.placed);
}
}
My datetime format is MM/DD/YYYY, so it goes by the month, not by the whole thing. What's the best way to get it to sort based on the full date? Thanks!
You know it is a date, then why store it as string? use dedicated DateTime type. It will work as expected.
If you can't change the field to DateTime for some reason, you can always do the following
public int CompareTo(User b)
{
DateTime x = DateTime.ParseExact(this.placed,...);
DateTime y = DateTime.ParseExact(b.placed,...);
return x.CompareTo(y);
}

Create an absolute or relative time period class in C#

I want to create some class that can calculate absolute or relative time period.
Meaning: the constructor will get either time period and units ( 3 weeks ) or two DateTime start and finish time. I have wrote the following code :
public class TimePeriod
{
public State TimePerriodState { get; private set; }
public RelativeTime RelativeTimePeriod { get; private set; }
public int UnitsOfTime { get; private set; }
public DateTime? StartTime { get; private set; }
public DateTime? EndTime { get; private set; }
public TimePeriod(RelativeTime relativeTime, int unitsOfTime)
{
TimePerriodState = State.Absolute;
RelativeTimePeriod = relativeTime;
UnitsOfTime = unitsOfTime;
}
public TimePeriod(DateTime startTime , DateTime endTime)
{
TimePerriodState = State.Relative;
StartTime = startTime;
EndTime = endTime;
}
public enum State
{
None,
Absolute,
Relative
}
public enum RelativeTime
{
None,
Hours,
Days,
Weeks,
Months,
Year
}
}
But I don't like that the usage is base on the state.
In the end the data will appear in the UI as two different controls. Is there a better way of making the API little better? Maybe pass in the data and calculate the dateTime on the fly or some thing like that?
UPDATE :
the usage is relative to any time its being used. meaning enter a timespan is not possible. lets say we need fo have the relative time prior 10 days from now or fixed days from X to Y
The TimeSpan will be calculated in a different BL class (this is a POCO class)
You're basically trying to store the same data twice.
You can define any period of time by either:
A startdate and enddate, duration can be calculated by subtraction.
A startdate and duration (TimeSpan), Enddate can be calculated by adding the two.
You could do it with a duration and enddate but that's making things harder.
What you want to do is pick one way of storing it. If you want it the other way, write a function to calculate it. E.g:
public TimePeriod(DateTime startTime , DateTime endTime)
{
StartTime = startTime;
EndTime = endTime;
}
public TimePeriod(DateTime startTime , TimeSpan length)
{
StartTime = startTime;
EndTime = startTime + length;
}
public TimeSpan GetDuration()
{
return EndTime - StartTime;
}
//Else you can just get the StartTime or EndTime variable (DateTime)
I think you have a problem with your terminology / domain understanding. Time periods are always relative by definition. An absolute time period would be a fixed date!
Can you instead use the TimeSpan struct which is part of the framework? You can then 'decorate' it in some way to indicate where this time-span was calculated from.
First of all I doubt that using some sort of UnitsOfTime is a very good idea. Try use TimeSpan instead and UI will show weeks or months. Try something like this:
public class TimePeriod
{
private readonly DateTime? _startDate;
private readonly DateTime? _endDate;
private readonly TimeSpan _timeSpan;
public TimePeriod(DateTime startDate, DateTime endDate)
{
if (_startDate > _endDate)
{
throw new ArgumentException();
}
_startDate = startDate;
_endDate = endDate;
_timeSpan = endDate - startDate;
}
public TimePeriod(TimeSpan timeSpan)
{
_timeSpan = timeSpan;
}
public TimeSpan TimeSpan
{
get { return _timeSpan; }
}
public DateTime? StartDate
{
get { return _startDate; }
}
public DateTime? EndDate
{
get { return _endDate; }
}
public bool IsAbsolute
{
get { return _startDate.HasValue; }
}
}
Also could you explain the reason why you need this class? It could help.

Categories

Resources