How to calculate age (in years) based on Date of Birth [duplicate] - c#
Given a DateTime representing a person's birthday, how do I calculate their age in years?
An easy to understand and simple solution.
// Save today's date.
var today = DateTime.Today;
// Calculate the age.
var age = today.Year - birthdate.Year;
// Go back to the year in which the person was born in case of a leap year
if (birthdate.Date > today.AddYears(-age)) age--;
However, this assumes you are looking for the western idea of the age and not using East Asian reckoning.
This is a strange way to do it, but if you format the date to yyyymmdd and subtract the date of birth from the current date then drop the last 4 digits you've got the age :)
I don't know C#, but I believe this will work in any language.
20080814 - 19800703 = 280111
Drop the last 4 digits = 28.
C# Code:
int now = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
int dob = int.Parse(dateOfBirth.ToString("yyyyMMdd"));
int age = (now - dob) / 10000;
Or alternatively without all the type conversion in the form of an extension method. Error checking omitted:
public static Int32 GetAge(this DateTime dateOfBirth)
{
var today = DateTime.Today;
var a = (today.Year * 100 + today.Month) * 100 + today.Day;
var b = (dateOfBirth.Year * 100 + dateOfBirth.Month) * 100 + dateOfBirth.Day;
return (a - b) / 10000;
}
Here is a test snippet:
DateTime bDay = new DateTime(2000, 2, 29);
DateTime now = new DateTime(2009, 2, 28);
MessageBox.Show(string.Format("Test {0} {1} {2}",
CalculateAgeWrong1(bDay, now), // outputs 9
CalculateAgeWrong2(bDay, now), // outputs 9
CalculateAgeCorrect(bDay, now), // outputs 8
CalculateAgeCorrect2(bDay, now))); // outputs 8
Here you have the methods:
public int CalculateAgeWrong1(DateTime birthDate, DateTime now)
{
return new DateTime(now.Subtract(birthDate).Ticks).Year - 1;
}
public int CalculateAgeWrong2(DateTime birthDate, DateTime now)
{
int age = now.Year - birthDate.Year;
if (now < birthDate.AddYears(age))
age--;
return age;
}
public int CalculateAgeCorrect(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 int CalculateAgeCorrect2(DateTime birthDate, DateTime now)
{
int age = now.Year - birthDate.Year;
// For leap years we need this
if (birthDate > now.AddYears(-age))
age--;
// Don't use:
// if (birthDate.AddYears(age) > now)
// age--;
return age;
}
The simple answer to this is to apply AddYears as shown below because this is the only native method to add years to the 29th of Feb. of leap years and obtain the correct result of the 28th of Feb. for common years.
Some feel that 1th of Mar. is the birthday of leaplings but neither .Net nor any official rule supports this, nor does common logic explain why some born in February should have 75% of their birthdays in another month.
Further, an Age method lends itself to be added as an extension to DateTime. By this you can obtain the age in the simplest possible way:
List item
int age = birthDate.Age();
public static class DateTimeExtensions
{
/// <summary>
/// Calculates the age in years of the current System.DateTime object today.
/// </summary>
/// <param name="birthDate">The date of birth</param>
/// <returns>Age in years today. 0 is returned for a future date of birth.</returns>
public static int Age(this DateTime birthDate)
{
return Age(birthDate, DateTime.Today);
}
/// <summary>
/// Calculates the age in years of the current System.DateTime object on a later date.
/// </summary>
/// <param name="birthDate">The date of birth</param>
/// <param name="laterDate">The date on which to calculate the age.</param>
/// <returns>Age in years on a later day. 0 is returned as minimum.</returns>
public static int Age(this DateTime birthDate, DateTime laterDate)
{
int age;
age = laterDate.Year - birthDate.Year;
if (age > 0)
{
age -= Convert.ToInt32(laterDate.Date < birthDate.Date.AddYears(age));
}
else
{
age = 0;
}
return age;
}
}
Now, run this test:
class Program
{
static void Main(string[] args)
{
RunTest();
}
private static void RunTest()
{
DateTime birthDate = new DateTime(2000, 2, 28);
DateTime laterDate = new DateTime(2011, 2, 27);
string iso = "yyyy-MM-dd";
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
Console.WriteLine("Birth date: " + birthDate.AddDays(i).ToString(iso) + " Later date: " + laterDate.AddDays(j).ToString(iso) + " Age: " + birthDate.AddDays(i).Age(laterDate.AddDays(j)).ToString());
}
}
Console.ReadKey();
}
}
The critical date example is this:
Birth date: 2000-02-29 Later date: 2011-02-28 Age: 11
Output:
{
Birth date: 2000-02-28 Later date: 2011-02-27 Age: 10
Birth date: 2000-02-28 Later date: 2011-02-28 Age: 11
Birth date: 2000-02-28 Later date: 2011-03-01 Age: 11
Birth date: 2000-02-29 Later date: 2011-02-27 Age: 10
Birth date: 2000-02-29 Later date: 2011-02-28 Age: 11
Birth date: 2000-02-29 Later date: 2011-03-01 Age: 11
Birth date: 2000-03-01 Later date: 2011-02-27 Age: 10
Birth date: 2000-03-01 Later date: 2011-02-28 Age: 10
Birth date: 2000-03-01 Later date: 2011-03-01 Age: 11
}
And for the later date 2012-02-28:
{
Birth date: 2000-02-28 Later date: 2012-02-28 Age: 12
Birth date: 2000-02-28 Later date: 2012-02-29 Age: 12
Birth date: 2000-02-28 Later date: 2012-03-01 Age: 12
Birth date: 2000-02-29 Later date: 2012-02-28 Age: 11
Birth date: 2000-02-29 Later date: 2012-02-29 Age: 12
Birth date: 2000-02-29 Later date: 2012-03-01 Age: 12
Birth date: 2000-03-01 Later date: 2012-02-28 Age: 11
Birth date: 2000-03-01 Later date: 2012-02-29 Age: 11
Birth date: 2000-03-01 Later date: 2012-03-01 Age: 12
}
My suggestion
int age = (int) ((DateTime.Now - bday).TotalDays/365.242199);
That seems to have the year changing on the right date. (I spot tested up to age 107.)
Another function, not by me but found on the web and refined it a bit:
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;
}
Just two things that come into my mind: What about people from countries that do not use the Gregorian calendar? DateTime.Now is in the server-specific culture I think. I have absolutely zero knowledge about actually working with Asian calendars and I do not know if there is an easy way to convert dates between calendars, but just in case you're wondering about those Chinese guys from the year 4660 :-)
2 Main problems to solve are:
1. Calculate Exact age - in years, months, days, etc.
2. Calculate Generally perceived age - people usually do not care how old they exactly are, they just care when their birthday in the current year is.
Solution for 1 is obvious:
DateTime birth = DateTime.Parse("1.1.2000");
DateTime today = DateTime.Today; //we usually don't care about birth time
TimeSpan age = today - birth; //.NET FCL should guarantee this as precise
double ageInDays = age.TotalDays; //total number of days ... also precise
double daysInYear = 365.2425; //statistical value for 400 years
double ageInYears = ageInDays / daysInYear; //can be shifted ... not so precise
Solution for 2 is the one which is not so precise in determing total age, but is perceived as precise by people. People also usually use it, when they calculate their age "manually":
DateTime birth = DateTime.Parse("1.1.2000");
DateTime today = DateTime.Today;
int age = today.Year - birth.Year; //people perceive their age in years
if (today.Month < birth.Month ||
((today.Month == birth.Month) && (today.Day < birth.Day)))
{
age--; //birthday in current year not yet reached, we are 1 year younger ;)
//+ no birthday for 29.2. guys ... sorry, just wrong date for birth
}
Notes to 2.:
This is my preferred solution
We cannot use DateTime.DayOfYear or TimeSpans, as they shift number of days in leap years
I have put there little more lines for readability
Just one more note ... I would create 2 static overloaded methods for it, one for universal usage, second for usage-friendliness:
public static int GetAge(DateTime bithDay, DateTime today)
{
//chosen solution method body
}
public static int GetAge(DateTime birthDay)
{
return GetAge(birthDay, DateTime.Now);
}
The best way that I know of because of leap years and everything is:
DateTime birthDate = new DateTime(2000,3,1);
int age = (int)Math.Floor((DateTime.Now - birthDate).TotalDays / 365.25D);
Here's a one-liner:
int age = new DateTime(DateTime.Now.Subtract(birthday).Ticks).Year-1;
This is the version we use here. It works, and it's fairly simple. It's the same idea as Jeff's but I think it's a little clearer because it separates out the logic for subtracting one, so it's a little easier to understand.
public static int GetAge(this DateTime dateOfBirth, DateTime dateAsAt)
{
return dateAsAt.Year - dateOfBirth.Year - (dateOfBirth.DayOfYear < dateAsAt.DayOfYear ? 0 : 1);
}
You could expand the ternary operator to make it even clearer, if you think that sort of thing is unclear.
Obviously this is done as an extension method on DateTime, but clearly you can grab that one line of code that does the work and put it anywhere. Here we have another overload of the Extension method that passes in DateTime.Now, just for completeness.
This gives "more detail" to this question. Maybe this is what you're looking for
DateTime birth = new DateTime(1974, 8, 29);
DateTime today = DateTime.Now;
TimeSpan span = today - birth;
DateTime age = DateTime.MinValue + span;
// Make adjustment due to MinValue equalling 1/1/1
int years = age.Year - 1;
int months = age.Month - 1;
int days = age.Day - 1;
// Print out not only how many years old they are but give months and days as well
Console.Write("{0} years, {1} months, {2} days", years, months, days);
I use this:
public static class DateTimeExtensions
{
public static int Age(this DateTime birthDate)
{
return Age(birthDate, DateTime.Now);
}
public static int Age(this DateTime birthDate, DateTime offsetDate)
{
int result=0;
result = offsetDate.Year - birthDate.Year;
if (offsetDate.DayOfYear < birthDate.DayOfYear)
{
result--;
}
return result;
}
}
Here's yet another answer:
public static int AgeInYears(DateTime birthday, DateTime today)
{
return ((today.Year - birthday.Year) * 372 + (today.Month - birthday.Month) * 31 + (today.Day - birthday.Day)) / 372;
}
This has been extensively unit-tested. It does look a bit "magic". The number 372 is the number of days there would be in a year if every month had 31 days.
The explanation of why it works (lifted from here) is:
Let's set Yn = DateTime.Now.Year, Yb = birthday.Year, Mn = DateTime.Now.Month, Mb = birthday.Month, Dn = DateTime.Now.Day, Db = birthday.Day
age = Yn - Yb + (31*(Mn - Mb) + (Dn - Db)) / 372
We know that what we need is either Yn-Yb if the date has already been reached, Yn-Yb-1 if it has not.
a) If Mn<Mb, we have -341 <= 31*(Mn-Mb) <= -31 and -30 <= Dn-Db <= 30
-371 <= 31*(Mn - Mb) + (Dn - Db) <= -1
With integer division
(31*(Mn - Mb) + (Dn - Db)) / 372 = -1
b) If Mn=Mb and Dn<Db, we have 31*(Mn - Mb) = 0 and -30 <= Dn-Db <= -1
With integer division, again
(31*(Mn - Mb) + (Dn - Db)) / 372 = -1
c) If Mn>Mb, we have 31 <= 31*(Mn-Mb) <= 341 and -30 <= Dn-Db <= 30
1 <= 31*(Mn - Mb) + (Dn - Db) <= 371
With integer division
(31*(Mn - Mb) + (Dn - Db)) / 372 = 0
d) If Mn=Mb and Dn>Db, we have 31*(Mn - Mb) = 0 and 1 <= Dn-Db <= 30
With integer division, again
(31*(Mn - Mb) + (Dn - Db)) / 372 = 0
e) If Mn=Mb and Dn=Db, we have 31*(Mn - Mb) + Dn-Db = 0
and therefore (31*(Mn - Mb) + (Dn - Db)) / 372 = 0
I have created a SQL Server User Defined Function to calculate someone's age, given their birthdate. This is useful when you need it as part of a query:
using System;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[SqlFunction(DataAccess = DataAccessKind.Read)]
public static SqlInt32 CalculateAge(string strBirthDate)
{
DateTime dtBirthDate = new DateTime();
dtBirthDate = Convert.ToDateTime(strBirthDate);
DateTime dtToday = DateTime.Now;
// get the difference in years
int years = dtToday.Year - dtBirthDate.Year;
// subtract another year if we're before the
// birth day in the current year
if (dtToday.Month < dtBirthDate.Month || (dtToday.Month == dtBirthDate.Month && dtToday.Day < dtBirthDate.Day))
years=years-1;
int intCustomerAge = years;
return intCustomerAge;
}
};
I've spent some time working on this and came up with this to calculate someone's age in years, months and days. I've tested against the Feb 29th problem and leap years and it seems to work, I'd appreciate any feedback:
public void LoopAge(DateTime myDOB, DateTime FutureDate)
{
int years = 0;
int months = 0;
int days = 0;
DateTime tmpMyDOB = new DateTime(myDOB.Year, myDOB.Month, 1);
DateTime tmpFutureDate = new DateTime(FutureDate.Year, FutureDate.Month, 1);
while (tmpMyDOB.AddYears(years).AddMonths(months) < tmpFutureDate)
{
months++;
if (months > 12)
{
years++;
months = months - 12;
}
}
if (FutureDate.Day >= myDOB.Day)
{
days = days + FutureDate.Day - myDOB.Day;
}
else
{
months--;
if (months < 0)
{
years--;
months = months + 12;
}
days +=
DateTime.DaysInMonth(
FutureDate.AddMonths(-1).Year, FutureDate.AddMonths(-1).Month
) + FutureDate.Day - myDOB.Day;
}
//add an extra day if the dob is a leap day
if (DateTime.IsLeapYear(myDOB.Year) && myDOB.Month == 2 && myDOB.Day == 29)
{
//but only if the future date is less than 1st March
if (FutureDate >= new DateTime(FutureDate.Year, 3, 1))
days++;
}
}
Do we need to consider people who is smaller than 1 year? as Chinese culture, we describe small babies' age as 2 months or 4 weeks.
Below is my implementation, it is not as simple as what I imagined, especially to deal with date like 2/28.
public static string HowOld(DateTime birthday, DateTime now)
{
if (now < birthday)
throw new ArgumentOutOfRangeException("birthday must be less than now.");
TimeSpan diff = now - birthday;
int diffDays = (int)diff.TotalDays;
if (diffDays > 7)//year, month and week
{
int age = now.Year - birthday.Year;
if (birthday > now.AddYears(-age))
age--;
if (age > 0)
{
return age + (age > 1 ? " years" : " year");
}
else
{// month and week
DateTime d = birthday;
int diffMonth = 1;
while (d.AddMonths(diffMonth) <= now)
{
diffMonth++;
}
age = diffMonth-1;
if (age == 1 && d.Day > now.Day)
age--;
if (age > 0)
{
return age + (age > 1 ? " months" : " month");
}
else
{
age = diffDays / 7;
return age + (age > 1 ? " weeks" : " week");
}
}
}
else if (diffDays > 0)
{
int age = diffDays;
return age + (age > 1 ? " days" : " day");
}
else
{
int age = diffDays;
return "just born";
}
}
This implementation has passed below test cases.
[TestMethod]
public void TestAge()
{
string age = HowOld(new DateTime(2011, 1, 1), new DateTime(2012, 11, 30));
Assert.AreEqual("1 year", age);
age = HowOld(new DateTime(2011, 11, 30), new DateTime(2012, 11, 30));
Assert.AreEqual("1 year", age);
age = HowOld(new DateTime(2001, 1, 1), new DateTime(2012, 11, 30));
Assert.AreEqual("11 years", age);
age = HowOld(new DateTime(2012, 1, 1), new DateTime(2012, 11, 30));
Assert.AreEqual("10 months", age);
age = HowOld(new DateTime(2011, 12, 1), new DateTime(2012, 11, 30));
Assert.AreEqual("11 months", age);
age = HowOld(new DateTime(2012, 10, 1), new DateTime(2012, 11, 30));
Assert.AreEqual("1 month", age);
age = HowOld(new DateTime(2008, 2, 28), new DateTime(2009, 2, 28));
Assert.AreEqual("1 year", age);
age = HowOld(new DateTime(2008, 3, 28), new DateTime(2009, 2, 28));
Assert.AreEqual("11 months", age);
age = HowOld(new DateTime(2008, 3, 28), new DateTime(2009, 3, 28));
Assert.AreEqual("1 year", age);
age = HowOld(new DateTime(2009, 1, 28), new DateTime(2009, 2, 28));
Assert.AreEqual("1 month", age);
age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 3, 1));
Assert.AreEqual("1 month", age);
// NOTE.
// new DateTime(2008, 1, 31).AddMonths(1) == new DateTime(2009, 2, 28);
// new DateTime(2008, 1, 28).AddMonths(1) == new DateTime(2009, 2, 28);
age = HowOld(new DateTime(2009, 1, 31), new DateTime(2009, 2, 28));
Assert.AreEqual("4 weeks", age);
age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 2, 28));
Assert.AreEqual("3 weeks", age);
age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 3, 1));
Assert.AreEqual("1 month", age);
age = HowOld(new DateTime(2012, 11, 5), new DateTime(2012, 11, 30));
Assert.AreEqual("3 weeks", age);
age = HowOld(new DateTime(2012, 11, 1), new DateTime(2012, 11, 30));
Assert.AreEqual("4 weeks", age);
age = HowOld(new DateTime(2012, 11, 20), new DateTime(2012, 11, 30));
Assert.AreEqual("1 week", age);
age = HowOld(new DateTime(2012, 11, 25), new DateTime(2012, 11, 30));
Assert.AreEqual("5 days", age);
age = HowOld(new DateTime(2012, 11, 29), new DateTime(2012, 11, 30));
Assert.AreEqual("1 day", age);
age = HowOld(new DateTime(2012, 11, 30), new DateTime(2012, 11, 30));
Assert.AreEqual("just born", age);
age = HowOld(new DateTime(2000, 2, 29), new DateTime(2009, 2, 28));
Assert.AreEqual("8 years", age);
age = HowOld(new DateTime(2000, 2, 29), new DateTime(2009, 3, 1));
Assert.AreEqual("9 years", age);
Exception e = null;
try
{
age = HowOld(new DateTime(2012, 12, 1), new DateTime(2012, 11, 30));
}
catch (ArgumentOutOfRangeException ex)
{
e = ex;
}
Assert.IsTrue(e != null);
}
Hope it's helpful.
The simplest way I've ever found is this. It works correctly for the US and western europe locales. Can't speak to other locales, especially places like China. 4 extra compares, at most, following the initial computation of age.
public int AgeInYears(DateTime birthDate, DateTime referenceDate)
{
Debug.Assert(referenceDate >= birthDate,
"birth date must be on or prior to the reference date");
DateTime birth = birthDate.Date;
DateTime reference = referenceDate.Date;
int years = (reference.Year - birth.Year);
//
// an offset of -1 is applied if the birth date has
// not yet occurred in the current year.
//
if (reference.Month > birth.Month);
else if (reference.Month < birth.Month)
--years;
else // in birth month
{
if (reference.Day < birth.Day)
--years;
}
return years ;
}
I was looking over the answers to this and noticed that nobody has made reference to regulatory/legal implications of leap day births. For instance, per Wikipedia, if you're born on February 29th in various jurisdictions, you're non-leap year birthday varies:
In the United Kingdom and Hong Kong: it's the ordinal day of the year, so the next day, March 1st is your birthday.
In New Zealand: it's the previous day, February 28th for the purposes of driver licencing, and March 1st for other purposes.
Taiwan: it's February 28th.
And as near as I can tell, in the US, the statutes are silent on the matter, leaving it up to the common law and to how various regulatory bodies define things in their regulations.
To that end, an improvement:
public enum LeapDayRule
{
OrdinalDay = 1 ,
LastDayOfMonth = 2 ,
}
static int ComputeAgeInYears(DateTime birth, DateTime reference, LeapYearBirthdayRule ruleInEffect)
{
bool isLeapYearBirthday = CultureInfo.CurrentCulture.Calendar.IsLeapDay(birth.Year, birth.Month, birth.Day);
DateTime cutoff;
if (isLeapYearBirthday && !DateTime.IsLeapYear(reference.Year))
{
switch (ruleInEffect)
{
case LeapDayRule.OrdinalDay:
cutoff = new DateTime(reference.Year, 1, 1)
.AddDays(birth.DayOfYear - 1);
break;
case LeapDayRule.LastDayOfMonth:
cutoff = new DateTime(reference.Year, birth.Month, 1)
.AddMonths(1)
.AddDays(-1);
break;
default:
throw new InvalidOperationException();
}
}
else
{
cutoff = new DateTime(reference.Year, birth.Month, birth.Day);
}
int age = (reference.Year - birth.Year) + (reference >= cutoff ? 0 : -1);
return age < 0 ? 0 : age;
}
It should be noted that this code assumes:
A western (European) reckoning of age, and
A calendar, like the Gregorian calendar that inserts a single leap day at the end of a month.
Keeping it simple (and possibly stupid:)).
DateTime birth = new DateTime(1975, 09, 27, 01, 00, 00, 00);
TimeSpan ts = DateTime.Now - birth;
Console.WriteLine("You are approximately " + ts.TotalSeconds.ToString() + " seconds old.");
This is not a direct answer, but more of a philosophical reasoning about the problem at hand from a quasi-scientific point of view.
I would argue that the question does not specify the unit nor culture in which to measure age, most answers seem to assume an integer annual representation. The SI-unit for time is second, ergo the correct generic answer should be (of course assuming normalized DateTime and taking no regard whatsoever to relativistic effects):
var lifeInSeconds = (DateTime.Now.Ticks - then.Ticks)/TickFactor;
In the Christian way of calculating age in years:
var then = ... // Then, in this case the birthday
var now = DateTime.UtcNow;
int age = now.Year - then.Year;
if (now.AddYears(-age) < then) age--;
In finance there is a similar problem when calculating something often referred to as the Day Count Fraction, which roughly is a number of years for a given period. And the age issue is really a time measuring issue.
Example for the actual/actual (counting all days "correctly") convention:
DateTime start, end = .... // Whatever, assume start is before end
double startYearContribution = 1 - (double) start.DayOfYear / (double) (DateTime.IsLeapYear(start.Year) ? 366 : 365);
double endYearContribution = (double)end.DayOfYear / (double)(DateTime.IsLeapYear(end.Year) ? 366 : 365);
double middleContribution = (double) (end.Year - start.Year - 1);
double DCF = startYearContribution + endYearContribution + middleContribution;
Another quite common way to measure time generally is by "serializing" (the dude who named this date convention must seriously have been trippin'):
DateTime start, end = .... // Whatever, assume start is before end
int days = (end - start).Days;
I wonder how long we have to go before a relativistic age in seconds becomes more useful than the rough approximation of earth-around-sun-cycles during one's lifetime so far :) Or in other words, when a period must be given a location or a function representing motion for itself to be valid :)
TimeSpan diff = DateTime.Now - birthdayDateTime;
string age = String.Format("{0:%y} years, {0:%M} months, {0:%d}, days old", diff);
I'm not sure how exactly you'd like it returned to you, so I just made a readable string.
Here is a solution.
DateTime dateOfBirth = new DateTime(2000, 4, 18);
DateTime currentDate = DateTime.Now;
int ageInYears = 0;
int ageInMonths = 0;
int ageInDays = 0;
ageInDays = currentDate.Day - dateOfBirth.Day;
ageInMonths = currentDate.Month - dateOfBirth.Month;
ageInYears = currentDate.Year - dateOfBirth.Year;
if (ageInDays < 0)
{
ageInDays += DateTime.DaysInMonth(currentDate.Year, currentDate.Month);
ageInMonths = ageInMonths--;
if (ageInMonths < 0)
{
ageInMonths += 12;
ageInYears--;
}
}
if (ageInMonths < 0)
{
ageInMonths += 12;
ageInYears--;
}
Console.WriteLine("{0}, {1}, {2}", ageInYears, ageInMonths, ageInDays);
This is one of the most accurate answers that is able to resolve the birthday of 29th of Feb compared to any year of 28th Feb.
public int GetAge(DateTime birthDate)
{
int age = DateTime.Now.Year - birthDate.Year;
if (birthDate.DayOfYear > DateTime.Now.DayOfYear)
age--;
return age;
}
I have a customized method to calculate age, plus a bonus validation message just in case it helps:
public void GetAge(DateTime dob, DateTime now, out int years, out int months, out int days)
{
years = 0;
months = 0;
days = 0;
DateTime tmpdob = new DateTime(dob.Year, dob.Month, 1);
DateTime tmpnow = new DateTime(now.Year, now.Month, 1);
while (tmpdob.AddYears(years).AddMonths(months) < tmpnow)
{
months++;
if (months > 12)
{
years++;
months = months - 12;
}
}
if (now.Day >= dob.Day)
days = days + now.Day - dob.Day;
else
{
months--;
if (months < 0)
{
years--;
months = months + 12;
}
days += DateTime.DaysInMonth(now.AddMonths(-1).Year, now.AddMonths(-1).Month) + now.Day - dob.Day;
}
if (DateTime.IsLeapYear(dob.Year) && dob.Month == 2 && dob.Day == 29 && now >= new DateTime(now.Year, 3, 1))
days++;
}
private string ValidateDate(DateTime dob) //This method will validate the date
{
int Years = 0; int Months = 0; int Days = 0;
GetAge(dob, DateTime.Now, out Years, out Months, out Days);
if (Years < 18)
message = Years + " is too young. Please try again on your 18th birthday.";
else if (Years >= 65)
message = Years + " is too old. Date of Birth must not be 65 or older.";
else
return null; //Denotes validation passed
}
Method call here and pass out datetime value (MM/dd/yyyy if server set to USA locale). Replace this with anything a messagebox or any container to display:
DateTime dob = DateTime.Parse("03/10/1982");
string message = ValidateDate(dob);
lbldatemessage.Visible = !StringIsNullOrWhitespace(message);
lbldatemessage.Text = message ?? ""; //Ternary if message is null then default to empty string
Remember you can format the message any way you like.
How about this solution?
static string CalcAge(DateTime birthDay)
{
DateTime currentDate = DateTime.Now;
int approximateAge = currentDate.Year - birthDay.Year;
int daysToNextBirthDay = (birthDay.Month * 30 + birthDay.Day) -
(currentDate.Month * 30 + currentDate.Day) ;
if (approximateAge == 0 || approximateAge == 1)
{
int month = Math.Abs(daysToNextBirthDay / 30);
int days = Math.Abs(daysToNextBirthDay % 30);
if (month == 0)
return "Your age is: " + daysToNextBirthDay + " days";
return "Your age is: " + month + " months and " + days + " days"; ;
}
if (daysToNextBirthDay > 0)
return "Your age is: " + --approximateAge + " Years";
return "Your age is: " + approximateAge + " Years"; ;
}
private int GetAge(int _year, int _month, int _day
{
DateTime yourBirthDate= new DateTime(_year, _month, _day);
DateTime todaysDateTime = DateTime.Today;
int noOfYears = todaysDateTime.Year - yourBirthDate.Year;
if (DateTime.Now.Month < yourBirthDate.Month ||
(DateTime.Now.Month == yourBirthDate.Month && DateTime.Now.Day < yourBirthDate.Day))
{
noOfYears--;
}
return noOfYears;
}
This classic question is deserving of a Noda Time solution.
static int GetAge(LocalDate dateOfBirth)
{
Instant now = SystemClock.Instance.Now;
// The target time zone is important.
// It should align with the *current physical location* of the person
// you are talking about. When the whereabouts of that person are unknown,
// then you use the time zone of the person who is *asking* for the age.
// The time zone of birth is irrelevant!
DateTimeZone zone = DateTimeZoneProviders.Tzdb["America/New_York"];
LocalDate today = now.InZone(zone).Date;
Period period = Period.Between(dateOfBirth, today, PeriodUnits.Years);
return (int) period.Years;
}
Usage:
LocalDate dateOfBirth = new LocalDate(1976, 8, 27);
int age = GetAge(dateOfBirth);
You might also be interested in the following improvements:
Passing in the clock as an IClock, instead of using SystemClock.Instance, would improve testability.
The target time zone will likely change, so you'd want a DateTimeZone parameter as well.
See also my blog post on this subject: Handling Birthdays, and Other Anniversaries
SQL version:
declare #dd smalldatetime = '1980-04-01'
declare #age int = YEAR(GETDATE())-YEAR(#dd)
if (#dd> DATEADD(YYYY, -#age, GETDATE())) set #age = #age -1
print #age
The following approach (extract from Time Period Library for .NET class DateDiff) considers the calendar of the culture info:
// ----------------------------------------------------------------------
private static int YearDiff( DateTime date1, DateTime date2 )
{
return YearDiff( date1, date2, DateTimeFormatInfo.CurrentInfo.Calendar );
} // YearDiff
// ----------------------------------------------------------------------
private static int YearDiff( DateTime date1, DateTime date2, Calendar calendar )
{
if ( date1.Equals( date2 ) )
{
return 0;
}
int year1 = calendar.GetYear( date1 );
int month1 = calendar.GetMonth( date1 );
int year2 = calendar.GetYear( date2 );
int month2 = calendar.GetMonth( date2 );
// find the the day to compare
int compareDay = date2.Day;
int compareDaysPerMonth = calendar.GetDaysInMonth( year1, month1 );
if ( compareDay > compareDaysPerMonth )
{
compareDay = compareDaysPerMonth;
}
// build the compare date
DateTime compareDate = new DateTime( year1, month2, compareDay,
date2.Hour, date2.Minute, date2.Second, date2.Millisecond );
if ( date2 > date1 )
{
if ( compareDate < date1 )
{
compareDate = compareDate.AddYears( 1 );
}
}
else
{
if ( compareDate > date1 )
{
compareDate = compareDate.AddYears( -1 );
}
}
return year2 - calendar.GetYear( compareDate );
} // YearDiff
Usage:
// ----------------------------------------------------------------------
public void CalculateAgeSamples()
{
PrintAge( new DateTime( 2000, 02, 29 ), new DateTime( 2009, 02, 28 ) );
// > Birthdate=29.02.2000, Age at 28.02.2009 is 8 years
PrintAge( new DateTime( 2000, 02, 29 ), new DateTime( 2012, 02, 28 ) );
// > Birthdate=29.02.2000, Age at 28.02.2012 is 11 years
} // CalculateAgeSamples
// ----------------------------------------------------------------------
public void PrintAge( DateTime birthDate, DateTime moment )
{
Console.WriteLine( "Birthdate={0:d}, Age at {1:d} is {2} years", birthDate, moment, YearDiff( birthDate, moment ) );
} // PrintAge
I used ScArcher2's solution for an accurate Year calculation of a persons age but I needed to take it further and calculate their Months and Days along with the Years.
public static Dictionary<string,int> CurrentAgeInYearsMonthsDays(DateTime? ndtBirthDate, DateTime? ndtReferralDate)
{
//----------------------------------------------------------------------
// Can't determine age if we don't have a dates.
//----------------------------------------------------------------------
if (ndtBirthDate == null) return null;
if (ndtReferralDate == null) return null;
DateTime dtBirthDate = Convert.ToDateTime(ndtBirthDate);
DateTime dtReferralDate = Convert.ToDateTime(ndtReferralDate);
//----------------------------------------------------------------------
// Create our Variables
//----------------------------------------------------------------------
Dictionary<string, int> dYMD = new Dictionary<string,int>();
int iNowDate, iBirthDate, iYears, iMonths, iDays;
string sDif = "";
//----------------------------------------------------------------------
// Store off current date/time and DOB into local variables
//----------------------------------------------------------------------
iNowDate = int.Parse(dtReferralDate.ToString("yyyyMMdd"));
iBirthDate = int.Parse(dtBirthDate.ToString("yyyyMMdd"));
//----------------------------------------------------------------------
// Calculate Years
//----------------------------------------------------------------------
sDif = (iNowDate - iBirthDate).ToString();
iYears = int.Parse(sDif.Substring(0, sDif.Length - 4));
//----------------------------------------------------------------------
// Store Years in Return Value
//----------------------------------------------------------------------
dYMD.Add("Years", iYears);
//----------------------------------------------------------------------
// Calculate Months
//----------------------------------------------------------------------
if (dtBirthDate.Month > dtReferralDate.Month)
iMonths = 12 - dtBirthDate.Month + dtReferralDate.Month - 1;
else
iMonths = dtBirthDate.Month - dtReferralDate.Month;
//----------------------------------------------------------------------
// Store Months in Return Value
//----------------------------------------------------------------------
dYMD.Add("Months", iMonths);
//----------------------------------------------------------------------
// Calculate Remaining Days
//----------------------------------------------------------------------
if (dtBirthDate.Day > dtReferralDate.Day)
//Logic: Figure out the days in month previous to the current month, or the admitted month.
// Subtract the birthday from the total days which will give us how many days the person has lived since their birthdate day the previous month.
// then take the referral date and simply add the number of days the person has lived this month.
//If referral date is january, we need to go back to the following year's December to get the days in that month.
if (dtReferralDate.Month == 1)
iDays = DateTime.DaysInMonth(dtReferralDate.Year - 1, 12) - dtBirthDate.Day + dtReferralDate.Day;
else
iDays = DateTime.DaysInMonth(dtReferralDate.Year, dtReferralDate.Month - 1) - dtBirthDate.Day + dtReferralDate.Day;
else
iDays = dtReferralDate.Day - dtBirthDate.Day;
//----------------------------------------------------------------------
// Store Days in Return Value
//----------------------------------------------------------------------
dYMD.Add("Days", iDays);
return dYMD;
}
This is simple and appears to be accurate for my needs. I am making an assumption for the purpose of leap years that regardless of when the person chooses to celebrate the birthday they are not technically a year older until 365 days have passed since their last birthday (i.e 28th February does not make them a year older).
DateTime now = DateTime.Today;
DateTime birthday = new DateTime(1991, 02, 03);//3rd feb
int age = now.Year - birthday.Year;
if (now.Month < birthday.Month || (now.Month == birthday.Month && now.Day < birthday.Day))//not had bday this year yet
age--;
return age;
Related
Check for a valid date
So I'm trying to figure out if there is another kind of way to check if a date is valid. So the idea is that if the date is valid then it continue's using the given date, if the date is invalid is uses the date of today. This is what I got at the moment: public void setBirthdate(int year, int month, int day) { if (month < 1 || month > 12 || day < 1 || day > DateTime.DaysInMonth(year, month)) { Birthdate = DateTime.Today; } else Birthdate = new DateTime(year, month, day); } So is there any shorter/more readable way of doing this? Thanks in advance
You could use the values to try constructing a valid DateTime, then catch the ArgumentOutOfRangeException that occurs if the arguments are out of range: public void setBirthdate(int year, int month, int day) { try { Birthdate = new DateTime(year, month, day); } catch (ArgumentOutOfRangeException) { Birthdate = DateTime.Today; } } Some may disagree with using exceptions like this, but I'm just letting the DateTime class do its own checks, instead of recreating them myself. From the documentation, an ArgumentOutOfRangeException occurs if: Year is less than 1 or greater than 9999, or Month is less than 1 or greater than 12, or Day is less than 1 or greater than the number of days in month. Alternatively, you could copy the logic from the DateTime class: (reference) public void setBirthdate(int year, int month, int day) { if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) { int[] days = DateTime.IsLeapYear(year) ? new[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365} : new[] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; if (day >= 1 && day <= days[month] - days[month - 1]) Birthdate = new DateTime(year, month, day); } else Birthdate = DateTime.Today; }
I would use the TryParse (MSDN) method over exception catching (which can be high overhead if called frequently with invalid values): DateTime date; if (DateTime.TryParse(string.Format("{0}-{1}-{2}", year, month, day), out date)) { // Date was valid. // date variable now contains a value. } else { // Date is not valid, default to today. date = DateTime.Today; }
Try this: public void setBirthdate(int year, int month, int day) { try { Birthdate = new DateTime(year, month, day); } catch (Exception ex) { Birthdate = DateTime.Now; } }
protected DateTime CheckDate(String date) { DateTime dt; try{ dt = DateTime.Parse(date); }catch(Exception ex){ dt = DateTime.now(); // may raise an exception } finally{ return dt; } }
Grant Winney's code second part has a bug. The assignation after the IsLeapYear check is swapped. Correct code: public static bool IsValidYearMonthDay(int year, int month, int day) { if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) { int[] days = DateTime.IsLeapYear(year) ? new[] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } : new[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; if (day >= 1 && day <= days[month] - days[month - 1]) { return true; } } return false; } And the test: [TestCase(2019, 10,21,true)] [TestCase(2019, 11, 31, false)] //november doesnt have 31, only 30 [TestCase(2016, 2, 29, true)] // is leap [TestCase(2014, 2, 29, false)] // is nop leap public static void ValidateYearMonthDay(int year, int month, int day, bool expectedresult) { var result = Date.IsValidYearMonthDay(year, month, day); Assert.AreEqual(expectedresult, result); }
Datediff without Weekends [duplicate]
In C#, how can I calculate the number of business (or weekdays) days between two dates?
I've had such a task before and I've got the solution. I would avoid enumerating all days in between when it's avoidable, which is the case here. I don't even mention creating a bunch of DateTime instances, as I saw in one of the answers above. This is really waste of processing power. Especially in the real world situation, when you have to examine time intervals of several months. See my code, with comments, below. /// <summary> /// Calculates number of business days, taking into account: /// - weekends (Saturdays and Sundays) /// - bank holidays in the middle of the week /// </summary> /// <param name="firstDay">First day in the time interval</param> /// <param name="lastDay">Last day in the time interval</param> /// <param name="bankHolidays">List of bank holidays excluding weekends</param> /// <returns>Number of business days during the 'span'</returns> public static int BusinessDaysUntil(this DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays) { firstDay = firstDay.Date; lastDay = lastDay.Date; if (firstDay > lastDay) throw new ArgumentException("Incorrect last day " + lastDay); TimeSpan span = lastDay - firstDay; int businessDays = span.Days + 1; int fullWeekCount = businessDays / 7; // find out if there are weekends during the time exceedng the full weeks if (businessDays > fullWeekCount*7) { // we are here to find out if there is a 1-day or 2-days weekend // in the time interval remaining after subtracting the complete weeks int firstDayOfWeek = (int) firstDay.DayOfWeek; int lastDayOfWeek = (int) lastDay.DayOfWeek; if (lastDayOfWeek < firstDayOfWeek) lastDayOfWeek += 7; if (firstDayOfWeek <= 6) { if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval businessDays -= 2; else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval businessDays -= 1; } else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval businessDays -= 1; } // subtract the weekends during the full weeks in the interval businessDays -= fullWeekCount + fullWeekCount; // subtract the number of bank holidays during the time interval foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; if (firstDay <= bh && bh <= lastDay) --businessDays; } return businessDays; } Edit by Slauma, August 2011 Great answer! There is little bug though. I take the freedom to edit this answer since the answerer is absent since 2009. The code above assumes that DayOfWeek.Sunday has the value 7 which is not the case. The value is actually 0. It leads to a wrong calculation if for example firstDay and lastDay are both the same Sunday. The method returns 1 in this case but it should be 0. Easiest fix for this bug: Replace in the code above the lines where firstDayOfWeek and lastDayOfWeek are declared by the following: int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)firstDay.DayOfWeek; int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)lastDay.DayOfWeek; Now the result is: Friday to Friday -> 1 Saturday to Saturday -> 0 Sunday to Sunday -> 0 Friday to Saturday -> 1 Friday to Sunday -> 1 Friday to Monday -> 2 Saturday to Monday -> 1 Sunday to Monday -> 1 Monday to Monday -> 1
Ok. I think it's time to post the right answer: public static double GetBusinessDays(DateTime startD, DateTime endD) { double calcBusinessDays = 1 + ((endD - startD).TotalDays * 5 - (startD.DayOfWeek - endD.DayOfWeek) * 2) / 7; if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--; if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--; return calcBusinessDays; } Original Source: http://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/
I know this question is already solved, but I thought I could provide a more straightforward-looking answer that may help other visitors in the future. Here's my take at it: public int GetWorkingDays(DateTime from, DateTime to) { var dayDifference = (int)to.Subtract(from).TotalDays; return Enumerable .Range(1, dayDifference) .Select(x => from.AddDays(x)) .Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday); } This was my original submission: public int GetWorkingDays(DateTime from, DateTime to) { var totalDays = 0; for (var date = from; date < to; date = date.AddDays(1)) { if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday) totalDays++; } return totalDays; }
Define an Extension Method on DateTime like so: public static class DateTimeExtensions { public static bool IsWorkingDay(this DateTime date) { return date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday; } } Then, use is within a Where clause to filter a broader list of dates: var allDates = GetDates(); // method which returns a list of dates // filter dates by working day's var countOfWorkDays = allDates .Where(day => day.IsWorkingDay()) .Count() ;
I used the following code to also take in to account bank holidays: public class WorkingDays { public List<DateTime> GetHolidays() { var client = new WebClient(); var json = client.DownloadString("https://www.gov.uk/bank-holidays.json"); var js = new JavaScriptSerializer(); var holidays = js.Deserialize <Dictionary<string, Holidays>>(json); return holidays["england-and-wales"].events.Select(d => d.date).ToList(); } public int GetWorkingDays(DateTime from, DateTime to) { var totalDays = 0; var holidays = GetHolidays(); for (var date = from.AddDays(1); date <= to; date = date.AddDays(1)) { if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday && !holidays.Contains(date)) totalDays++; } return totalDays; } } public class Holidays { public string division { get; set; } public List<Event> events { get; set; } } public class Event { public DateTime date { get; set; } public string notes { get; set; } public string title { get; set; } } And Unit Tests: [TestClass] public class WorkingDays { [TestMethod] public void SameDayIsZero() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 12); Assert.AreEqual(0, service.GetWorkingDays(from, from)); } [TestMethod] public void CalculateDaysInWorkingWeek() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 12); var to = new DateTime(2013, 8, 16); Assert.AreEqual(4, service.GetWorkingDays(from, to), "Mon - Fri = 4"); Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Mon - Tues = 1"); } [TestMethod] public void NotIncludeWeekends() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 9); var to = new DateTime(2013, 8, 16); Assert.AreEqual(5, service.GetWorkingDays(from, to), "Fri - Fri = 5"); Assert.AreEqual(2, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Fri - Tues = 2"); Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 12)), "Fri - Mon = 1"); } [TestMethod] public void AccountForHolidays() { var service = new WorkingDays(); var from = new DateTime(2013, 8, 23); Assert.AreEqual(0, service.GetWorkingDays(from, new DateTime(2013, 8, 26)), "Fri - Mon = 0"); Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 27)), "Fri - Tues = 1"); } }
I searched a lot for a, easy to digest, algorithm to calculate the working days between 2 dates, and also to exclude the national holidays, and finally I decide to go with this approach: public static int NumberOfWorkingDaysBetween2Dates(DateTime start,DateTime due,IEnumerable<DateTime> holidays) { var dic = new Dictionary<DateTime, DayOfWeek>(); var totalDays = (due - start).Days; for (int i = 0; i < totalDays + 1; i++) { if (!holidays.Any(x => x == start.AddDays(i))) dic.Add(start.AddDays(i), start.AddDays(i).DayOfWeek); } return dic.Where(x => x.Value != DayOfWeek.Saturday && x.Value != DayOfWeek.Sunday).Count(); } Basically I wanted to go with each date and evaluate my conditions: Is not Saturday Is not Sunday Is not national holiday but also I wanted to avoid iterating dates. By running and measuring the time need it to evaluate 1 full year, I go the following result: static void Main(string[] args) { var start = new DateTime(2017, 1, 1); var due = new DateTime(2017, 12, 31); var sw = Stopwatch.StartNew(); var days = NumberOfWorkingDaysBetween2Dates(start, due,NationalHolidays()); sw.Stop(); Console.WriteLine($"Total working days = {days} --- time: {sw.Elapsed}"); Console.ReadLine(); // result is: // Total working days = 249-- - time: 00:00:00.0269087 } Edit: a new method more simple: public static int ToBusinessWorkingDays(this DateTime start, DateTime due, DateTime[] holidays) { return Enumerable.Range(0, (due - start).Days) .Select(a => start.AddDays(a)) .Where(a => a.DayOfWeek != DayOfWeek.Sunday) .Where(a => a.DayOfWeek != DayOfWeek.Saturday) .Count(a => !holidays.Any(x => x == a)); }
This solution avoids iteration, works for both +ve and -ve weekday differences and includes a unit test suite to regression against the slower method of counting weekdays. I've also include a concise method to add weekdays also works in the same non-iterative way. Unit tests cover a few thousand date combinations in order to exhaustively test all start/end weekday combinations with both small and large date ranges. Important: We make the assumption that we are counting days by excluding the start date and including the end date. This is important when counting weekdays as the specific start/end days that you include/exclude affect the result. This also ensures that the difference between two equal days is always zero and that we only include full working days as typically you want the answer to be correct for any time on the current start date (often today) and include the full end date (e.g. a due date). NOTE: This code needs an additional adjustment for holidays but in keeping with the above assumption, this code must exclude holidays on the start date. Add weekdays: private static readonly int[,] _addOffset = { // 0 1 2 3 4 {0, 1, 2, 3, 4}, // Su 0 {0, 1, 2, 3, 4}, // M 1 {0, 1, 2, 3, 6}, // Tu 2 {0, 1, 4, 5, 6}, // W 3 {0, 1, 4, 5, 6}, // Th 4 {0, 3, 4, 5, 6}, // F 5 {0, 2, 3, 4, 5}, // Sa 6 }; public static DateTime AddWeekdays(this DateTime date, int weekdays) { int extraDays = weekdays % 5; int addDays = weekdays >= 0 ? (weekdays / 5) * 7 + _addOffset[(int)date.DayOfWeek, extraDays] : (weekdays / 5) * 7 - _addOffset[6 - (int)date.DayOfWeek, -extraDays]; return date.AddDays(addDays); } Compute weekday difference: static readonly int[,] _diffOffset = { // Su M Tu W Th F Sa {0, 1, 2, 3, 4, 5, 5}, // Su {4, 0, 1, 2, 3, 4, 4}, // M {3, 4, 0, 1, 2, 3, 3}, // Tu {2, 3, 4, 0, 1, 2, 2}, // W {1, 2, 3, 4, 0, 1, 1}, // Th {0, 1, 2, 3, 4, 0, 0}, // F {0, 1, 2, 3, 4, 5, 0}, // Sa }; public static int GetWeekdaysDiff(this DateTime dtStart, DateTime dtEnd) { int daysDiff = (int)(dtEnd - dtStart).TotalDays; return daysDiff >= 0 ? 5 * (daysDiff / 7) + _diffOffset[(int) dtStart.DayOfWeek, (int) dtEnd.DayOfWeek] : 5 * (daysDiff / 7) - _diffOffset[6 - (int) dtStart.DayOfWeek, 6 - (int) dtEnd.DayOfWeek]; } I found that most other solutions on stack overflow were either slow (iterative) or overly complex and many were just plain incorrect. Moral of the story is ... Don't trust it unless you've exhaustively tested it!! Unit tests based on NUnit Combinatorial testing and ShouldBe NUnit extension. [TestFixture] public class DateTimeExtensionsTests { /// <summary> /// Exclude start date, Include end date /// </summary> /// <param name="dtStart"></param> /// <param name="dtEnd"></param> /// <returns></returns> private IEnumerable<DateTime> GetDateRange(DateTime dtStart, DateTime dtEnd) { Console.WriteLine(#"dtStart={0:yy-MMM-dd ddd}, dtEnd={1:yy-MMM-dd ddd}", dtStart, dtEnd); TimeSpan diff = dtEnd - dtStart; Console.WriteLine(diff); if (dtStart <= dtEnd) { for (DateTime dt = dtStart.AddDays(1); dt <= dtEnd; dt = dt.AddDays(1)) { Console.WriteLine(#"dt={0:yy-MMM-dd ddd}", dt); yield return dt; } } else { for (DateTime dt = dtStart.AddDays(-1); dt >= dtEnd; dt = dt.AddDays(-1)) { Console.WriteLine(#"dt={0:yy-MMM-dd ddd}", dt); yield return dt; } } } [Test, Combinatorial] public void TestGetWeekdaysDiff( [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int startDay, [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int endDay, [Values(7)] int startMonth, [Values(7)] int endMonth) { // Arrange DateTime dtStart = new DateTime(2016, startMonth, startDay); DateTime dtEnd = new DateTime(2016, endMonth, endDay); int nDays = GetDateRange(dtStart, dtEnd) .Count(dt => dt.DayOfWeek != DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday); if (dtEnd < dtStart) nDays = -nDays; Console.WriteLine(#"countBusDays={0}", nDays); // Act / Assert dtStart.GetWeekdaysDiff(dtEnd).ShouldBe(nDays); } [Test, Combinatorial] public void TestAddWeekdays( [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int startDay, [Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)] int weekdays) { DateTime dtStart = new DateTime(2016, 7, startDay); DateTime dtEnd1 = dtStart.AddWeekdays(weekdays); // ADD dtStart.GetWeekdaysDiff(dtEnd1).ShouldBe(weekdays); DateTime dtEnd2 = dtStart.AddWeekdays(-weekdays); // SUBTRACT dtStart.GetWeekdaysDiff(dtEnd2).ShouldBe(-weekdays); } }
Well this has been beaten to death. :) However I'm still going to provide another answer because I needed something a bit different. This solution is different in that it returns a Business TimeSpan between the start and end, and you can set the business hours of the day, and add holidays. So you can use it to calculate if it happens within a day, across days, over weekends, and even holidays. And you can get just the business days or not by just getting what you need from the returned TimeSpan object. And the way it uses lists of days, you can see how very easy it would be to add the list of non-work days if it's not the typical Sat and Sun. And I tested for a year, and it seems super fast. I just hope the pasting of the code is accurate. But I know it works. public static TimeSpan GetBusinessTimespanBetween( DateTime start, DateTime end, TimeSpan workdayStartTime, TimeSpan workdayEndTime, List<DateTime> holidays = null) { if (end < start) throw new ArgumentException("start datetime must be before end datetime."); // Just create an empty list for easier coding. if (holidays == null) holidays = new List<DateTime>(); if (holidays.Where(x => x.TimeOfDay.Ticks > 0).Any()) throw new ArgumentException("holidays can not have a TimeOfDay, only the Date."); var nonWorkDays = new List<DayOfWeek>() { DayOfWeek.Saturday, DayOfWeek.Sunday }; var startTime = start.TimeOfDay; // If the start time is before the starting hours, set it to the starting hour. if (startTime < workdayStartTime) startTime = workdayStartTime; var timeBeforeEndOfWorkDay = workdayEndTime - startTime; // If it's after the end of the day, then this time lapse doesn't count. if (timeBeforeEndOfWorkDay.TotalSeconds < 0) timeBeforeEndOfWorkDay = new TimeSpan(); // If start is during a non work day, it doesn't count. if (nonWorkDays.Contains(start.DayOfWeek)) timeBeforeEndOfWorkDay = new TimeSpan(); else if (holidays.Contains(start.Date)) timeBeforeEndOfWorkDay = new TimeSpan(); var endTime = end.TimeOfDay; // If the end time is after the ending hours, set it to the ending hour. if (endTime > workdayEndTime) endTime = workdayEndTime; var timeAfterStartOfWorkDay = endTime - workdayStartTime; // If it's before the start of the day, then this time lapse doesn't count. if (timeAfterStartOfWorkDay.TotalSeconds < 0) timeAfterStartOfWorkDay = new TimeSpan(); // If end is during a non work day, it doesn't count. if (nonWorkDays.Contains(end.DayOfWeek)) timeAfterStartOfWorkDay = new TimeSpan(); else if (holidays.Contains(end.Date)) timeAfterStartOfWorkDay = new TimeSpan(); // Easy scenario if the times are during the day day. if (start.Date.CompareTo(end.Date) == 0) { if (nonWorkDays.Contains(start.DayOfWeek)) return new TimeSpan(); else if (holidays.Contains(start.Date)) return new TimeSpan(); return endTime - startTime; } else { var timeBetween = end - start; var daysBetween = (int)Math.Floor(timeBetween.TotalDays); var dailyWorkSeconds = (int)Math.Floor((workdayEndTime - workdayStartTime).TotalSeconds); var businessDaysBetween = 0; // Now the fun begins with calculating the actual Business days. if (daysBetween > 0) { var nextStartDay = start.AddDays(1).Date; var dayBeforeEnd = end.AddDays(-1).Date; for (DateTime d = nextStartDay; d <= dayBeforeEnd; d = d.AddDays(1)) { if (nonWorkDays.Contains(d.DayOfWeek)) continue; else if (holidays.Contains(d.Date)) continue; businessDaysBetween++; } } var dailyWorkSecondsToAdd = dailyWorkSeconds * businessDaysBetween; var output = timeBeforeEndOfWorkDay + timeAfterStartOfWorkDay; output = output + new TimeSpan(0, 0, dailyWorkSecondsToAdd); return output; } } And here is test code: Note that you just have to put this function in a class called DateHelper for the test code to work. [TestMethod] public void TestGetBusinessTimespanBetween() { var workdayStart = new TimeSpan(8, 0, 0); var workdayEnd = new TimeSpan(17, 0, 0); var holidays = new List<DateTime>() { new DateTime(2018, 1, 15), // a Monday new DateTime(2018, 2, 15) // a Thursday }; var testdata = new[] { new { expectedMinutes = 0, start = new DateTime(2016, 10, 19, 9, 50, 0), end = new DateTime(2016, 10, 19, 9, 50, 0) }, new { expectedMinutes = 10, start = new DateTime(2016, 10, 19, 9, 50, 0), end = new DateTime(2016, 10, 19, 10, 0, 0) }, new { expectedMinutes = 5, start = new DateTime(2016, 10, 19, 7, 50, 0), end = new DateTime(2016, 10, 19, 8, 5, 0) }, new { expectedMinutes = 5, start = new DateTime(2016, 10, 19, 16, 55, 0), end = new DateTime(2016, 10, 19, 17, 5, 0) }, new { expectedMinutes = 15, start = new DateTime(2016, 10, 19, 16, 50, 0), end = new DateTime(2016, 10, 20, 8, 5, 0) }, new { expectedMinutes = 10, start = new DateTime(2016, 10, 19, 16, 50, 0), end = new DateTime(2016, 10, 20, 7, 55, 0) }, new { expectedMinutes = 5, start = new DateTime(2016, 10, 19, 17, 10, 0), end = new DateTime(2016, 10, 20, 8, 5, 0) }, new { expectedMinutes = 0, start = new DateTime(2016, 10, 19, 17, 10, 0), end = new DateTime(2016, 10, 20, 7, 5, 0) }, new { expectedMinutes = 545, start = new DateTime(2016, 10, 19, 12, 10, 0), end = new DateTime(2016, 10, 20, 12, 15, 0) }, // Spanning multiple weekdays new { expectedMinutes = 835, start = new DateTime(2016, 10, 19, 12, 10, 0), end = new DateTime(2016, 10, 21, 8, 5, 0) }, // Spanning multiple weekdays new { expectedMinutes = 1375, start = new DateTime(2016, 10, 18, 12, 10, 0), end = new DateTime(2016, 10, 21, 8, 5, 0) }, // Spanning from a Thursday to a Tuesday, 5 mins short of complete day. new { expectedMinutes = 1615, start = new DateTime(2016, 10, 20, 12, 10, 0), end = new DateTime(2016, 10, 25, 12, 5, 0) }, // Spanning from a Thursday to a Tuesday, 5 mins beyond complete day. new { expectedMinutes = 1625, start = new DateTime(2016, 10, 20, 12, 10, 0), end = new DateTime(2016, 10, 25, 12, 15, 0) }, // Spanning from a Friday to a Monday, 5 mins beyond complete day. new { expectedMinutes = 545, start = new DateTime(2016, 10, 21, 12, 10, 0), end = new DateTime(2016, 10, 24, 12, 15, 0) }, // Spanning from a Friday to a Monday, 5 mins short complete day. new { expectedMinutes = 535, start = new DateTime(2016, 10, 21, 12, 10, 0), end = new DateTime(2016, 10, 24, 12, 5, 0) }, // Spanning from a Saturday to a Monday, 5 mins short complete day. new { expectedMinutes = 245, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 24, 12, 5, 0) }, // Spanning from a Saturday to a Sunday, 5 mins beyond complete day. new { expectedMinutes = 0, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 23, 12, 15, 0) }, // Times within the same Saturday. new { expectedMinutes = 0, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 23, 12, 15, 0) }, // Spanning from a Saturday to the Sunday next week. new { expectedMinutes = 2700, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2016, 10, 30, 12, 15, 0) }, // Spanning a year. new { expectedMinutes = 143355, start = new DateTime(2016, 10, 22, 12, 10, 0), end = new DateTime(2017, 10, 30, 12, 15, 0) }, // Spanning a year with 2 holidays. new { expectedMinutes = 142815, start = new DateTime(2017, 10, 22, 12, 10, 0), end = new DateTime(2018, 10, 30, 12, 15, 0) }, }; foreach (var item in testdata) { Assert.AreEqual(item.expectedMinutes, DateHelper.GetBusinessTimespanBetween( item.start, item.end, workdayStart, workdayEnd, holidays) .TotalMinutes); } }
Here's some code for that purpose, with swedish holidays but you can adapt what holidays to count. Note that I added a limit you might want to remove, but it was for a web-based system and I didnt want anyone to enter some huge date to hog the process public static int GetWorkdays(DateTime from ,DateTime to) { int limit = 9999; int counter = 0; DateTime current = from; int result = 0; if (from > to) { DateTime temp = from; from = to; to = temp; } if (from >= to) { return 0; } while (current <= to && counter < limit) { if (IsSwedishWorkday(current)) { result++; } current = current.AddDays(1); counter++; } return result; } public static bool IsSwedishWorkday(DateTime date) { return (!IsSwedishHoliday(date) && date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday); } public static bool IsSwedishHoliday(DateTime date) { return ( IsSameDay(GetEpiphanyDay(date.Year), date) || IsSameDay(GetMayDay(date.Year), date) || IsSameDay(GetSwedishNationalDay(date.Year), date) || IsSameDay(GetChristmasDay(date.Year), date) || IsSameDay(GetBoxingDay(date.Year), date) || IsSameDay(GetGoodFriday(date.Year), date) || IsSameDay(GetAscensionDay(date.Year), date) || IsSameDay(GetAllSaintsDay(date.Year), date) || IsSameDay(GetMidsummersDay(date.Year), date) || IsSameDay(GetPentecostDay(date.Year), date) || IsSameDay(GetEasterMonday(date.Year), date) || IsSameDay(GetNewYearsDay(date.Year), date) || IsSameDay(GetEasterDay(date.Year), date) ); } // Trettondagen public static DateTime GetEpiphanyDay(int year) { return new DateTime(year, 1, 6); } // Första maj public static DateTime GetMayDay(int year) { return new DateTime(year,5,1); } // Juldagen public static DateTime GetSwedishNationalDay(int year) { return new DateTime(year, 6, 6); } // Juldagen public static DateTime GetNewYearsDay(int year) { return new DateTime(year,1,1); } // Juldagen public static DateTime GetChristmasDay(int year) { return new DateTime(year,12,25); } // Annandag jul public static DateTime GetBoxingDay(int year) { return new DateTime(year, 12, 26); } // Långfredagen public static DateTime GetGoodFriday(int year) { return GetEasterDay(year).AddDays(-3); } // Kristi himmelsfärdsdag public static DateTime GetAscensionDay(int year) { return GetEasterDay(year).AddDays(5*7+4); } // Midsommar public static DateTime GetAllSaintsDay(int year) { DateTime result = new DateTime(year,10,31); while (result.DayOfWeek != DayOfWeek.Saturday) { result = result.AddDays(1); } return result; } // Midsommar public static DateTime GetMidsummersDay(int year) { DateTime result = new DateTime(year, 6, 20); while (result.DayOfWeek != DayOfWeek.Saturday) { result = result.AddDays(1); } return result; } // Pingstdagen public static DateTime GetPentecostDay(int year) { return GetEasterDay(year).AddDays(7 * 7); } // Annandag påsk public static DateTime GetEasterMonday(int year) { return GetEasterDay(year).AddDays(1); } public static DateTime GetEasterDay(int y) { double c; double n; double k; double i; double j; double l; double m; double d; c = System.Math.Floor(y / 100.0); n = y - 19 * System.Math.Floor(y / 19.0); k = System.Math.Floor((c - 17) / 25.0); i = c - System.Math.Floor(c / 4) - System.Math.Floor((c - k) / 3) + 19 * n + 15; i = i - 30 * System.Math.Floor(i / 30); i = i - System.Math.Floor(i / 28) * (1 - System.Math.Floor(i / 28) * System.Math.Floor(29 / (i + 1)) * System.Math.Floor((21 - n) / 11)); j = y + System.Math.Floor(y / 4.0) + i + 2 - c + System.Math.Floor(c / 4); j = j - 7 * System.Math.Floor(j / 7); l = i - j; m = 3 + System.Math.Floor((l + 40) / 44);// month d = l + 28 - 31 * System.Math.Floor(m / 4);// day double days = ((m == 3) ? d : d + 31); DateTime result = new DateTime(y, 3, 1).AddDays(days-1); return result; }
Works and without loops This method doesn't use any loops and is actually quite simple. It expands the date range to full weeks since we know that each week has 5 business days. It then uses a lookup table to find the number of business days to subtract from the start and end to get the right result. I've expanded out the calculation to help show what's going on, but the whole thing could be condensed into a single line if needed. Anyway, this works for me and so I thought I'd post it here in case it might help others. Happy coding. Calculation t : Total number of days between dates (1 if min = max) a + b : Extra days needed to expand total to full weeks k : 1.4 is number of weekdays per week, i.e., (t / 7) * 5 c : Number of weekdays to subtract from the total m : A lookup table used to find the value of "c" for each day of the week Culture Code assumes a Monday to Friday work week. For other cultures, such as Sunday to Thursday, you'll need to offset the dates prior to calculation. Method public int Weekdays(DateTime min, DateTime max) { if (min.Date > max.Date) throw new Exception("Invalid date span"); var t = (max.AddDays(1).Date - min.Date).TotalDays; var a = (int) min.DayOfWeek; var b = 6 - (int) max.DayOfWeek; var k = 1.4; var m = new int[]{0, 0, 1, 2, 3, 4, 5}; var c = m[a] + m[b]; return (int)((t + a + b) / k) - c; }
Here's a quick sample code. It's a class method, so will only work inside of your class. If you want it to be static, change the signature to private static (or public static). private IEnumerable<DateTime> GetWorkingDays(DateTime sd, DateTime ed) { for (var d = sd; d <= ed; d = d.AddDays(1)) if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday) yield return d; } This method creates a loop variable d, initializes it to the start day, sd, then increments by one day each iteration (d = d.AddDays(1)). It returns the desired values using yield, which creates an iterator. The cool thing about iterators is that they don't hold all of the values of the IEnumerable in memory, only calling each one sequentially. This means that you can call this method from the dawn of time to now without having to worry about running out of memory.
This method returns the number of business days between two dates: Here I use the DayOfWeek enum for checking weekends. private static int BusinessDaysLeft(DateTime first, DateTime last) { var count = 0; while (first.Date != last.Date) { if(first.DayOfWeek != DayOfWeek.Saturday && first.DayOfWeek != DayOfWeek.Sunday) count++; first = first.AddDays(1); } return count; }
I think none of the above answers are actually correct. None of them solves all the special cases such as when the dates starts and ends on the middle of a weekend, when the date starts on a Friday and ends on next Monday, etc. On top of that, they all round the calculations to whole days, so if the start date is in the middle of a saturday for example, it will substract a whole day from the working days, giving wrong results... Anyway, here is my solution that is quite efficient and simple and works for all cases. The trick is just to find the previous Monday for start and end dates, and then do a small compensation when start and end happens during the weekend: public double WorkDays(DateTime startDate, DateTime endDate){ double weekendDays; double days = endDate.Subtract(startDate).TotalDays; if(days<0) return 0; DateTime startMonday = startDate.AddDays(DayOfWeek.Monday - startDate.DayOfWeek).Date; DateTime endMonday = endDate.AddDays(DayOfWeek.Monday - endDate.DayOfWeek).Date; weekendDays = ((endMonday.Subtract(startMonday).TotalDays) / 7) * 2; // compute fractionary part of weekend days double diffStart = startDate.Subtract(startMonday).TotalDays - 5; double diffEnd = endDate.Subtract(endMonday).TotalDays - 5; // compensate weekenddays if(diffStart>0) weekendDays -= diffStart; if(diffEnd>0) weekendDays += diffEnd; return days - weekendDays; }
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { DateTime start = new DateTime(2014, 1, 1); DateTime stop = new DateTime(2014, 12, 31); int totalWorkingDays = GetNumberOfWorkingDays(start, stop); Console.WriteLine("There are {0} working days.", totalWorkingDays); } private static int GetNumberOfWorkingDays(DateTime start, DateTime stop) { TimeSpan interval = stop - start; int totalWeek = interval.Days / 7; int totalWorkingDays = 5 * totalWeek; int remainingDays = interval.Days % 7; for (int i = 0; i <= remainingDays; i++) { DayOfWeek test = (DayOfWeek)(((int)start.DayOfWeek + i) % 7); if (test >= DayOfWeek.Monday && test <= DayOfWeek.Friday) totalWorkingDays++; } return totalWorkingDays; } } }
Here is the function which we can use to calculate business days between two date. I'm not using holiday list as it can vary accross country/region. If we want to use it anyway we can take third argument as list of holiday and before incrementing count we should check that list does not contains d public static int GetBussinessDaysBetweenTwoDates(DateTime StartDate, DateTime EndDate) { if (StartDate > EndDate) return -1; int bd = 0; for (DateTime d = StartDate; d < EndDate; d = d.AddDays(1)) { if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday) bd++; } return bd; }
By using marinjw library, And solution provided by Alec Pojidaev i created below, this will skip public (you need to specify country) holidays: Please Note that if you use actual DateTime and have hours present then you have to add .Date and best to reformat FromDate and ToDate as FromDate = FromDate.Date and ToDate = ToDate.Date public static int GetProcessingTime(this DateTime? FromDate, DateTime? ToDate) { if (FromDate == null) return 0; if (ToDate == null) return 0; List<DateTime> Holidays = new List<DateTime>(); int startyear = FromDate.Value.Year; int endyear = ToDate.Value.Year; //loop years to extract all holidays for(int i = startyear; i <= endyear; i++) { IList<DateTime> Temp = new PolandPublicHoliday().PublicHolidays(i); Holidays.AddRange(Temp.ToList()); } //exclude holidays outside of range Holidays = Holidays.Where(x => x >= FromDate && x <= ToDate).ToList(); //exclude holidays that are set to be on sunday/saturday Holidays = Holidays.Where(x => x.DayOfWeek != DayOfWeek.Sunday && x.DayOfWeek != DayOfWeek.Saturday).ToList(); //calculate date difference without sundays and saturdays Value need to be added as I am using nullable DateTime double calcBusinessDays = 1 + ((FromDate.Value - ToDate.Value).TotalDays * 5 - (FromDate.Value.DayOfWeek - ToDate.Value.DayOfWeek) * 2) / 7; if (FromDate.Value.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--; if (ToDate.Value.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--; //remove left holidays if (Holidays!=null) calcBusinessDays -= Holidays.Count; return (int)calcBusinessDays; }
I'll just share my solution. It worked for me, maybe I just don't notice/know that theres a bug. I started by getting the first incomplete week if there's any. a complete week was from sunday for saturday, so if the (int)_now.DayOfWeek was not 0(Sunday), the first week was incomplete. I just subtract 1 to first weeks count for the first week's saturday then add it to new count; Then I get the last incomplete week, then subtract 1 for it's sunday then add to new count. Then finally, the number of complete weeks multiply by 5(weekdays) was added to new count. public int RemoveNonWorkingDays(int numberOfDays){ int workingDays = 0; int firstWeek = 7 - (int)_now.DayOfWeek; if(firstWeek < 7){ if(firstWeek > numberOfDays) return numberOfDays; workingDays += firstWeek-1; numberOfDays -= firstWeek; } int lastWeek = numberOfDays % 7; if(lastWeek > 0){ numberOfDays -= lastWeek; workingDays += lastWeek - 1; } workingDays += (numberOfDays/7)*5; return workingDays; }
I was having trouble finding a solid TSQL version of this code. Below is essentially a conversion of the C# code here with addition of the Holiday table which should be used to pre-calculate holidays. CREATE TABLE dbo.Holiday ( HolidayDt DATE NOT NULL, Name NVARCHAR(50) NOT NULL, IsWeekday BIT NOT NULL, CONSTRAINT PK_Holiday PRIMARY KEY (HolidayDt) ) GO CREATE INDEX IDX_Holiday ON Holiday (HolidayDt, IsWeekday) GO CREATE function dbo.GetBusinessDays ( #FirstDay datetime, #LastDay datetime ) RETURNS INT AS BEGIN DECLARE #BusinessDays INT, #FullWeekCount INT SELECT #FirstDay = CONVERT(DATETIME,CONVERT(DATE,#FirstDay)) , #LastDay = CONVERT(DATETIME,CONVERT(DATE,#LastDay)) IF #FirstDay > #LastDay RETURN NULL; SELECT #BusinessDays = DATEDIFF(DAY, #FirstDay, #LastDay) + 1 SELECT #FullWeekCount = #BusinessDays / 7; -- find out if there are weekends during the time exceedng the full weeks IF #BusinessDays > (#FullWeekCount * 7) BEGIN -- we are here to find out if there is a 1-day or 2-days weekend -- in the time interval remaining after subtracting the complete weeks DECLARE #firstDayOfWeek INT, #lastDayOfWeek INT; SELECT #firstDayOfWeek = DATEPART(DW, #FirstDay), #lastDayOfWeek = DATEPART(DW, #LastDay); IF #lastDayOfWeek < #firstDayOfWeek SELECT #lastDayOfWeek = #lastDayOfWeek + 7; IF #firstDayOfWeek <= 6 BEGIN IF (#lastDayOfWeek >= 7) --Both Saturday and Sunday are in the remaining time interval BEGIN SELECT #BusinessDays = #BusinessDays - 2 END ELSE IF #lastDayOfWeek>=6 --Only Saturday is in the remaining time interval BEGIN SELECT #BusinessDays = #BusinessDays - 1 END END ELSE IF #firstDayOfWeek <= 7 AND #lastDayOfWeek >=7 -- Only Sunday is in the remaining time interval BEGIN SELECT #BusinessDays = #BusinessDays - 1 END END -- subtract the weekends during the full weeks in the interval DECLARE #Holidays INT; SELECT #Holidays = COUNT(*) FROM Holiday WHERE HolidayDt BETWEEN #FirstDay AND #LastDay AND IsWeekday = CAST(1 AS BIT) SELECT #BusinessDays = #BusinessDays - (#FullWeekCount + #FullWeekCount) -- - #Holidays RETURN #BusinessDays END
int BusinessDayDifference(DateTime Date1, DateTime Date2) { int Sign = 1; if (Date2 > Date1) { Sign = -1; DateTime TempDate = Date1; Date1 = Date2; Date2 = TempDate; } int BusDayDiff = (int)(Date1.Date - Date2.Date).TotalDays; if (Date1.DayOfWeek == DayOfWeek.Saturday) BusDayDiff -= 1; if (Date2.DayOfWeek == DayOfWeek.Sunday) BusDayDiff -= 1; int Week1 = GetWeekNum(Date1); int Week2 = GetWeekNum(Date2); int WeekDiff = Week1 - Week2; BusDayDiff -= WeekDiff * 2; foreach (DateTime Holiday in Holidays) if (Date1 >= Holiday && Date2 <= Holiday) BusDayDiff--; BusDayDiff *= Sign; return BusDayDiff; } private int GetWeekNum(DateTime Date) { return (int)(Date.AddDays(-(int)Date.DayOfWeek).Ticks / TimeSpan.TicksPerDay / 7); }
Here is one very simple solution for this problem. We have starting date, end date and "for loop" for encreasing the day and calculating to see if it's a workday or a weekend by converting to string DayOfWeek. class Program { static void Main(string[] args) { DateTime day = new DateTime(); Console.Write("Inser your end date (example: 01/30/2015): "); DateTime endDate = DateTime.Parse(Console.ReadLine()); int numberOfDays = 0; for (day = DateTime.Now.Date; day.Date < endDate.Date; day = day.Date.AddDays(1)) { string dayToString = Convert.ToString(day.DayOfWeek); if (dayToString != "Saturday" && dayToString != "Sunday") numberOfDays++; } Console.WriteLine("Number of working days (not including local holidays) between two dates is "+numberOfDays); } }
Based on the comment marked as answer and patch recommended , as well as -> This version wants to convert the Days to Business-Hours ... Considers Same day hours as well. /// <summary> /// Calculates number of business days, taking into account: /// - weekends (Saturdays and Sundays) /// - bank holidays in the middle of the week /// </summary> /// <param name="firstDay">First day in the time interval</param> /// <param name="lastDay">Last day in the time interval</param> /// <param name="bankHolidays">List of bank holidays excluding weekends</param> /// <returns>Number of business hours during the 'span'</returns> public static int BusinessHoursUntil(DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays) { var original_firstDay = firstDay; var original_lastDay = lastDay; firstDay = firstDay.Date; lastDay = lastDay.Date; if (firstDay > lastDay) return -1; //// throw new ArgumentException("Incorrect last day " + lastDay); TimeSpan span = lastDay - firstDay; int businessDays = span.Days + 1; int fullWeekCount = businessDays / 7; // find out if there are weekends during the time exceedng the full weeks if (businessDays > fullWeekCount * 7) { // we are here to find out if there is a 1-day or 2-days weekend // in the time interval remaining after subtracting the complete weeks int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)firstDay.DayOfWeek; int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)lastDay.DayOfWeek; if (lastDayOfWeek < firstDayOfWeek) lastDayOfWeek += 7; if (firstDayOfWeek <= 6) { if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval businessDays -= 2; else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval businessDays -= 1; } else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval businessDays -= 1; } // subtract the weekends during the full weeks in the interval businessDays -= fullWeekCount + fullWeekCount; if (bankHolidays != null && bankHolidays.Any()) { // subtract the number of bank holidays during the time interval foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; if (firstDay <= bh && bh <= lastDay) --businessDays; } } int total_business_hours = 0; if (firstDay.Date == lastDay.Date) {//If on the same day, go granular with Hours from the Orginial_*Day values total_business_hours = (int)(original_lastDay - original_firstDay).TotalHours; } else {//Convert Business-Days to TotalHours total_business_hours = (int)(firstDay.AddDays(businessDays).AddHours(firstDay.Hour) - firstDay).TotalHours; } return total_business_hours; }
I just improved #Alexander and #Slauma answer to support a business week as a parameter, for cases where saturday is a business day, or even cases where there is just a couple of days of the week that are considered business days: /// <summary> /// Calculate the number of business days between two dates, considering: /// - Days of the week that are not considered business days. /// - Holidays between these two dates. /// </summary> /// <param name="fDay">First day of the desired 'span'.</param> /// <param name="lDay">Last day of the desired 'span'.</param> /// <param name="BusinessDaysOfWeek">Days of the week that are considered to be business days, if NULL considers monday, tuesday, wednesday, thursday and friday as business days of the week.</param> /// <param name="Holidays">Holidays, if NULL, considers no holiday.</param> /// <returns>Number of business days during the 'span'</returns> public static int BusinessDaysUntil(this DateTime fDay, DateTime lDay, DayOfWeek[] BusinessDaysOfWeek = null, DateTime[] Holidays = null) { if (BusinessDaysOfWeek == null) BusinessDaysOfWeek = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }; if (Holidays == null) Holidays = new DateTime[] { }; fDay = fDay.Date; lDay = lDay.Date; if (fDay > lDay) throw new ArgumentException("Incorrect last day " + lDay); int bDays = (lDay - fDay).Days + 1; int fullWeekCount = bDays / 7; int fullWeekCountMult = 7 - WeekDays.Length; // Find out if there are weekends during the time exceedng the full weeks if (bDays > (fullWeekCount * 7)) { int fDayOfWeek = (int)fDay.DayOfWeek; int lDayOfWeek = (int)lDay.DayOfWeek; if (fDayOfWeek > lDayOfWeek) lDayOfWeek += 7; // If they are the same, we already covered it right before the Holiday subtraction if (lDayOfWeek != fDayOfWeek) { // Here we need to see if any of the days between are considered business days for (int i = fDayOfWeek; i <= lDayOfWeek; i++) if (!WeekDays.Contains((DayOfWeek)(i > 6 ? i - 7 : i))) bDays -= 1; } } // Subtract the days that are not in WeekDays[] during the full weeks in the interval bDays -= (fullWeekCount * fullWeekCountMult); // Subtract the number of bank holidays during the time interval bDays = bDays - Holidays.Select(x => x.Date).Count(x => fDay <= x && x <= lDay); return bDays; }
I believe this could be a simpler way: public int BusinessDaysUntil(DateTime start, DateTime end, params DateTime[] bankHolidays) { int tld = (int)((end - start).TotalDays) + 1; //including end day int not_buss_day = 2 * (tld / 7); //Saturday and Sunday int rest = tld % 7; //rest. if (rest > 0) { int tmp = (int)start.DayOfWeek - 1 + rest; if (tmp == 6 || start.DayOfWeek == DayOfWeek.Sunday) not_buss_day++; else if (tmp > 6) not_buss_day += 2; } foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; if (!(bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) && (start <= bh && bh <= end)) { not_buss_day++; } } return tld - not_buss_day; }
Here's yet another idea - this method allows to specify any working week and holidays. The idea here is that we find the core of the date range from the first first working day of the week to the last weekend day of the week. This enables us to calculate the whole weeks easily (without iterating over all of the dates). All we need to do then is to add the working days that fall before the start and end of this core range. public static int CalculateWorkingDays( DateTime startDate, DateTime endDate, IList<DateTime> holidays, DayOfWeek firstDayOfWeek, DayOfWeek lastDayOfWeek) { // Make sure the defined working days run contiguously if (lastDayOfWeek < firstDayOfWeek) { throw new Exception("Last day of week cannot fall before first day of week!"); } // Create a list of the days of the week that make-up the weekend by working back // from the firstDayOfWeek and forward from lastDayOfWeek to get the start and end // the weekend var weekendStart = lastDayOfWeek == DayOfWeek.Saturday ? DayOfWeek.Sunday : lastDayOfWeek + 1; var weekendEnd = firstDayOfWeek == DayOfWeek.Sunday ? DayOfWeek.Saturday : firstDayOfWeek - 1; var weekendDays = new List<DayOfWeek>(); var w = weekendStart; do { weekendDays.Add(w); if (w == weekendEnd) break; w = (w == DayOfWeek.Saturday) ? DayOfWeek.Sunday : w + 1; } while (true); // Force simple dates - no time startDate = startDate.Date; endDate = endDate.Date; // Ensure a progessive date range if (endDate < startDate) { var t = startDate; startDate = endDate; endDate = t; } // setup some working variables and constants const int daysInWeek = 7; // yeah - really! var actualStartDate = startDate; // this will end up on startOfWeek boundary var actualEndDate = endDate; // this will end up on weekendEnd boundary int workingDaysInWeek = daysInWeek - weekendDays.Count; int workingDays = 0; // the result we are trying to find int leadingDays = 0; // the number of working days leading up to the firstDayOfWeek boundary int trailingDays = 0; // the number of working days counting back to the weekendEnd boundary // Calculate leading working days // if we aren't on the firstDayOfWeek we need to step forward to the nearest if (startDate.DayOfWeek != firstDayOfWeek) { var d = startDate; do { if (d.DayOfWeek == firstDayOfWeek || d >= endDate) { actualStartDate = d; break; } if (!weekendDays.Contains(d.DayOfWeek)) { leadingDays++; } d = d.AddDays(1); } while(true); } // Calculate trailing working days // if we aren't on the weekendEnd we step back to the nearest if (endDate >= actualStartDate && endDate.DayOfWeek != weekendEnd) { var d = endDate; do { if (d.DayOfWeek == weekendEnd || d < actualStartDate) { actualEndDate = d; break; } if (!weekendDays.Contains(d.DayOfWeek)) { trailingDays++; } d = d.AddDays(-1); } while(true); } // Calculate the inclusive number of days between the actualStartDate and the actualEndDate var coreDays = (actualEndDate - actualStartDate).Days + 1; var noWeeks = coreDays / daysInWeek; // add together leading, core and trailing days workingDays += noWeeks * workingDaysInWeek; workingDays += leadingDays; workingDays += trailingDays; // Finally remove any holidays that fall within the range. if (holidays != null) { workingDays -= holidays.Count(h => h >= startDate && (h <= endDate)); } return workingDays; }
Since I can't comment. There is one more issue with the accepted solution where bank holidays are subtracted even when they are situated in the weekend. Seeing how other input is checked, it is only fitting that this is as well. The foreach should therefore be: // subtract the number of bank holidays during the time interval foreach (DateTime bankHoliday in bankHolidays) { DateTime bh = bankHoliday.Date; // Do not subtract bank holidays when they fall in the weekend to avoid double subtraction if (bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) continue; if (firstDay <= bh && bh <= lastDay) --businessDays; }
Here is an approach if you are using MVC. I have also calculated national holidays or any festive days to be excluded by fetching it from holidayscalendar which you will need to make one. foreach (DateTime day in EachDay(model)) { bool key = false; foreach (LeaveModel ln in holidaycalendar) { if (day.Date == ln.Date && day.DayOfWeek != DayOfWeek.Saturday && day.DayOfWeek != DayOfWeek.Sunday) { key = true; break; } } if (day.DayOfWeek == DayOfWeek.Saturday || day.DayOfWeek == DayOfWeek.Sunday) { key = true; } if (key != true) { leavecount++; } } Leavemodel is a list here
Here is an helper function I wrote for that task. it also returns the count of weekends via the out parameter. if you wish you can customize the "weekend" days in run time for countries that use different weekend days or to include holidays trough the weekendDays[] optional parameter : public static int GetNetworkDays(DateTime startDate, DateTime endDate,out int totalWeekenDays, DayOfWeek[] weekendDays = null) { if (startDate >= endDate) { throw new Exception("start date can not be greater then or equel to end date"); } DayOfWeek[] weekends = new DayOfWeek[] { DayOfWeek.Sunday, DayOfWeek.Saturday }; if (weekendDays != null) { weekends = weekendDays; } var totaldays = (endDate - startDate).TotalDays + 1; // add one to include the first day to var counter = 0; var workdaysCounter = 0; var weekendsCounter = 0; for (int i = 0; i < totaldays; i++) { if (weekends.Contains(startDate.AddDays(counter).DayOfWeek)) { weekendsCounter++; } else { workdaysCounter++; } counter++; } totalWeekenDays = weekendsCounter; return workdaysCounter; }
I came up with the following solution var dateStart = new DateTime(2019,01,10); var dateEnd = new DateTime(2019,01,31); var timeBetween = (dateEnd - dateStart).TotalDays + 1; int numberOf7DayWeeks = (int)(timeBetween / 7); int numberOfWeekendDays = numberOf7DayWeeks * 2; int workingDays =(int)( timeBetween - numberOfWeekendDays); if(dateStart.DayOfWeek == DayOfWeek.Saturday || dateEnd.DayOfWeek == DayOfWeek.Sunday){ workingDays -=2; } if(dateStart.DayOfWeek == DayOfWeek.Sunday || dateEnd.DayOfWeek == DayOfWeek.Saturday){ workingDays -=1; }
You just have to iterate through each day in the time range and subtract a day from the counter if its a Saturday or a Sunday. private float SubtractWeekend(DateTime start, DateTime end) { float totaldays = (end.Date - start.Date).Days; var iterationVal = totalDays; for (int i = 0; i <= iterationVal; i++) { int dayVal = (int)start.Date.AddDays(i).DayOfWeek; if(dayVal == 6 || dayVal == 0) { // saturday or sunday totalDays--; } } return totalDays; }
public static int CalculateBusinessDaysInRange(this DateTime startDate, DateTime endDate, params DateTime[] holidayDates) { endDate = endDate.Date; if(startDate > endDate) throw new ArgumentException("The end date can not be before the start date!", nameof(endDate)); int accumulator = 0; DateTime itterator = startDate.Date; do { if(itterator.DayOfWeek != DayOfWeek.Saturday && itterator.DayOfWeek != DayOfWeek.Sunday && !holidayDates.Any(hol => hol.Date == itterator)) { accumulator++; } } while((itterator = itterator.AddDays(1)).Date <= endDate); return accumulator } I'm only posting this because despite all of the excellent answers that have been given, none of the math made sense to me. This is definitely a KISS method that should work and be fairly maintainable. Granted if you are calculating ranges that are greater than 2-3 months this will not be the most effective way. We simply determine if it is a Saturday or Sunday or the date is a given holiday date. If it's not we add a business day. If it is then everything is fine. I'm sure this could be even more so simplified with LINQ, but this way is much easier to understand.
Calculate the start date of a Financial Quarter a date is in
Assume Financial Quarters always start on the 1st of a month and they are always 3 calendar months long. Different organisations start their Financial Year (FY) in different months - some may be 1st April , some may be 1st July or could be just 1st Jan (which will match normal Calendar Quarters). Given a date and a month that the FY starts on how can you determine the start of the quarter that the date falls in. E.g. DateTime getStartOfFinancialQtr(DateTime date, int monthFinancialYearStartsOn) 15th Jan when FY starts Jan would = 1st Jan getStartOfFinancialQtr(new DateTime(2013,1,15), 1) == new DateTime(2013,1,1) 15th August when FY starts April would be 1st July getStartOfFinancialQtr(new DateTime(2013,8,15), 4) == new DateTime(2013,7,1) BUT 15th Jan 2013 when FY starts February would be 1st November 2012 getStartOfFinancialQtr(new DateTime(2013,1,15), 2) == new DateTime(2012,11,1)
The following solution is the most simple implementation I could think of and works without any - unnecessary - loops: DateTime getStartOfFinancialQtr(DateTime date, int monthFinancialYearStartsOn) { var actualMonth = date.Month; var financialYear = date.Year; var difference = actualMonth - monthFinancialYearStartsOn; if(difference < 0) { --financialYear; difference += 12; } var quarter = difference / 3; return new DateTime(financialYear, monthFinancialYearStartsOn, 1).AddMonths(quarter * 3); }
Isn't it as simple as this? Am I missing something to this? A quarter is defined as a period of three months, so you just have to find where the given date is, and then compute where the quarter begins based off that given month of the date. public DateTime GetStartOfFinancialQtr(DateTime dtGiven, int startMonth) { DateTime dtQuarter = new DateTime(dtGiven.Year, startMonth, 1); // Start Q is less than the given date if(startMonth > dtGiven.Month) { while(dtQuarter > dtGiven) { dtQuarter = dtQuarter.AddMonths(-3); } } // Start Q is larger than the given date else { while(dtQuarter.Month + 3 <= dtGiven.Month) { dtQuarter = dtQuarter.AddMonths(3); } } return dtQuarter; } Below is the testing I ran: Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 1, 15), 1).ToString()); Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 8, 15), 4).ToString()); Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 1, 15), 2).ToString()); Console output: 01/01/2013 000000 07/01/2013 000000 11/01/2012 000000
You can use the Year class of the Time Period Library for .NET: // ---------------------------------------------------------------------- public void FiscalYearRange() { // calendar TimeCalendar fiscalYearCalendar = new TimeCalendar( new TimeCalendarConfig { YearBaseMonth = YearMonth.April, YearType = YearType.FiscalYear } ); // time range TimeRange timeRange = new TimeRange( new DateTime( 2007, 10, 1 ), new DateTime( 2012, 2, 25 ) ); Console.WriteLine( "Time range: " + timeRange ); Console.WriteLine(); // fiscal quarter Console.WriteLine( "Start Quarter: " + new Quarter( timeRange.Start, fiscalYearCalendar ) ); Console.WriteLine( "End Quarter: " + new Quarter( timeRange.End, fiscalYearCalendar ) ); Console.WriteLine(); // fiscal year Year year = new Year( timeRange.Start, fiscalYearCalendar ); while ( year.Start < timeRange.End ) { Console.WriteLine( "Fiscal Year: " + year ); year = year.GetNextYear(); } } // FiscalYearRange
As mentioned, you can easily obtain the answer from Nearest Completed quarter. Here's how you make the modification: public static class DateTimeExtensions { public static DateTime NearestQuarterEnd( this DateTime date, int firstMonthOfFiscalYear ) { IEnumerable<DateTime> candidates = QuartersInYear(date.Year, firstMonthOfFiscalYear) .Concat(QuartersInYear(date.Year - 1, firstMonthOfFiscalYear)); return candidates.SkipWhile(d => d > date).First(); } static Dictionary<Tuple<int, int>, List<DateTime>> dict = new Dictionary<Tuple<int, int>, List<DateTime>>(); static IEnumerable<DateTime> QuartersInYear( int year, int firstMonthOfFiscalYear ) { Contract.Requires(firstMonthOfFiscalYear >= 1 && firstMonthOfFiscalYear <= 12); var key = Tuple.Create(year, firstMonthOfFiscalYear); if(dict.ContainsKey(key)) { return dict[key]; } else { var value = Enumerable .Range(0, 4) .Select(k => firstMonthOfFiscalYear + 3 * k) .Select(m => m <= 12 ? m : m % 12) .Select(m => new DateTime(year, m, 1)) .OrderByDescending(d => d) .ToList(); dict.Add(key, value); return value; } } } Usage: Console.WriteLine(new DateTime(2013, 1, 15).NearestQuarterEnd(1)); Console.WriteLine(new DateTime(2013, 8, 15).NearestQuarterEnd(4)); Console.WriteLine(new DateTime(2013, 1, 15).NearestQuarterEnd(2)); Output: 1/1/2013 12:00:00 AM 7/1/2013 12:00:00 AM 11/1/2012 12:00:00 AM This passes all three of your test cases.
how to find year and month difference between two dates?
DateTime dayStart; DateTime dateEnd; TimeSpan ts = dateEnt - dateStart; Print : ... Year(s) and ... Month(s) how can I calculate it? .net framework 2.0 c# asp.net project.
You should first read this article from Jon Skeet, specially from the text "Introducing periods and period arithmetic" it gets interesting for you. So, you have to define when a certain period is a change in month, in year etc. Noda-time already contains a lot of functions for this. But I don't think it is released yet.
Following will calculate the age in years, months, days DateTime dob = "10/18/1981"; // date of birth DateTime now = DateTime.Now; // Swap them if one is bigger than the other if (now < dob) { DateTime date3 = now; now = dob; dob = date3; } TimeSpan ts = now - dob; //Debug.WriteLine(ts.TotalDays); int years = 0; int months = 0, days=0; if ((now.Month <= dob.Month) && (now.Day < dob.Day)) // i.e. now = 03Jan15, dob = 23dec14 { // example: March 2010 (3) and January 2011 (1); this should be 10 months. // 12 - 3 + 1 = 10 years = now.Year - dob.Year-1; months = 12 - dob.Month + now.Month-1; days = DateTime.DaysInMonth(dob.Year, dob.Month) - dob.Day + now.Day; if(months==12) { months=0; years +=1; } } else if ((now.Month <= dob.Month) && (now.Day >= dob.Day)) // i.e. now = 23Jan15, dob = 20dec14 { // example: March 2010 (3) and January 2011 (1); this should be 10 months. // 12 - 3 + 1 = 10 years = now.Year - dob.Year - 1; months = 12 - dob.Month + now.Month; days = now.Day - dob.Day; if (months == 12) { months = 0; years += 1; } } else if ((now.Month > dob.Month) && (now.Day < dob.Day)) // i.e. now = 18oct15, dob = 22feb14 { years = now.Year - dob.Year; months = now.Month - dob.Month-1; days = DateTime.DaysInMonth(dob.Year, dob.Month) - dob.Day + now.Day; } else if ((now.Month > dob.Month) && (now.Day >= dob.Day)) // i.e. now = 22oct15, dob = 18feb14 { years = now.Year - dob.Year; months = now.Month - dob.Month; days = now.Day - dob.Day; } Debug.WriteLine("Years: {0}, Months: {1}, Days: {2}", years, months, days);
That depends on what you want to calculate exactly. You can't translate the value in a TimeSpan to exact years and months, as the length of years and months varies. You can calculate approximate years and months like this: int years = ts.Days / 365; int months = (ts.Days % 365) / 31; If you want the exact difference, you have to compare the DateTime values.
I think something like this would do it: DateTime date1 = new DateTime(1973, 07, 20); DateTime date2 = new DateTime(2010, 01, 10); // Swap them if one is bigger than the other if (date2 < date1) { DateTime date3 = date2; date2 = date1; date1 = date3; } // Now date2 >= date1. TimeSpan ts = date2 - date1; // Total days Console.WriteLine(ts.TotalDays); // Total years int years = date2.Year - date1.Year; int months = 0; // Total monts if (date2.Month < date1.Month) { // example: March 2010 (3) and January 2011 (1); this should be 10 monts // 12 - 3 + 1 = 10 // Take the 12 months of a year into account months = 12 - date1.Month + date2.Month; } else { months = date2.Month - date1.Month; } Console.WriteLine("Years: {0}, Months: {1}", years, months); Edit To clarify: There's no need for complicated date algorhitms or any of that kind of stuff, because there are always 12 months in a year (at least in our calendar).
How to check if a DateTime range is within another 3 month DateTime range
Hi I have a Start Date and End Date per record in a db. I need to check to see where the time period falls in a 2 year period broken into two lots of quarters then display what quarters each record falls into. Quarter 1 includes June 09, Jul 09, Aug 09 Quarter 2 includes Sept 09, Oct 09, Nov 09 Quarter 3 includes Dec 09, Jan 10, Feb 10 Quarter 4 includes Mar 10, Apr 10, May 10 Quaretr 5 includes Jun 10, Jul 10... e.g. 01/10/09 - 01/06/10 would fall into quarters 2, 3, 4 & 5 I am very new to .NET so any examples would be much appreciated.
This should work for you also. class Range { public DateTime Begin { get; private set; } public DateTime End { get; private set; } public Range(DateTime begin, DateTime end) { Begin = begin; End = end; } public bool Contains(Range range) { return range.Begin >= Begin && range.End <= End; } } and then to use it List<Range> ranges = new List<Range>(); ranges.Add(new Range(DateTime.Now, DateTime.Now.AddMonths(3))); ranges.Add(new Range(DateTime.Now.AddMonths(3), DateTime.Now.AddMonths(6))); Range test = new Range(DateTime.Now.AddMonths(1), DateTime.Now.AddMonths(2)); var hits = ranges.Where(range => range.Contains(test)); MessageBox.Show(hits.Count().ToString());
You would call IntervalInQuarters as follows: IntervalInQuarters(new DateTime(2007, 10, 10), new DateTime(2009, 10, 11)); The function returns a list of quarter start dates. Note that the range of quarters searched is defined within the function itself. Please edit as appropriate for your situation. They key point is to make sure the interval/quarter intersection logic is right. private List<DateTime> IntervalInQuarters(DateTime myStartDate, DateTime myEndDate) { DateTime quarterStart = new DateTime(2006, 06, 01); DateTime nextQuarterStart = new DateTime(2006, 09, 01); DateTime finalDate = new DateTime(2011, 01, 01); List<DateTime> foundQuarters = new List<DateTime>(); while (quarterStart < finalDate) { // quarter intersects interval if: // its start/end date is within our interval // our start/end date is within quarter interval DateTime quarterEnd = nextQuarterStart.AddDays(-1); if (DateInInterval(myStartDate, quarterStart, quarterEnd) || DateInInterval(myEndDate, quarterStart, quarterEnd) || DateInInterval(quarterStart, myStartDate, myEndDate) || DateInInterval(quarterEnd, myStartDate, myEndDate)) { foundQuarters.Add(quarterStart); } quarterStart = nextQuarterStart; nextQuarterStart = nextQuarterStart.AddMonths(3); } return foundQuarters; } private bool DateInInterval(DateTime myDate, DateTime intStart, DateTime intEnd) { return ((intStart <= myDate) && (myDate <= intEnd)); }
private void Form1_Load(object sender, EventArgs e) { DateTime[,] ranges = new DateTime[3,2]; //Range 1 - Jan to March ranges[0, 0] = new DateTime(2010, 1, 1); ranges[0, 1] = new DateTime(2010, 3, 1); //Range 2 - April to July ranges[1, 0] = new DateTime(2010, 4, 1); ranges[1, 1] = new DateTime(2010, 7, 1); //Range 3 - March to June ranges[2, 0] = new DateTime(2010, 3, 1); ranges[2, 1] = new DateTime(2010, 6, 1); DateTime checkDate = new DateTime(2010, 4, 1); string validRanges = string.Empty; for (int i = 0; i < ranges.GetLength(0); i++) { if (DateWithin(ranges[i,0], ranges[i,1], checkDate)) { validRanges += i.ToString() + " "; } } MessageBox.Show(validRanges); } private bool DateWithin(DateTime dateStart, DateTime dateEnd, DateTime checkDate) { if (checkDate.CompareTo(dateStart) < 0 || checkDate.CompareTo(dateEnd) > 0) { return false; } return true; }
You may have to take a look at: http://msdn.microsoft.com/en-us/library/03ybds8y(v=VS.100).aspx This may start you up FindQuarter(DateTime startDate, DateTime endDate) // 01-10-09, 01-06-10 { startDateQuarter = GetQuarter(startDate.Month); // 2 endDateQuarter = GetQuarter(endDate.Month); // 1 endDateQuarter += (endDate.Year - startDate.Year) * 4; // 5 // fill up startDateQuarter to endDateQuarter into a list // and return it // 2,3,4,5 } GetQuarter(int month) // 6 { int quarter; // check the month value and accordingly assign one of the basic quarters // using if-else construct ie, if(month>=6 && month<=8){ quarter = 1 }; return quarter; // 1 } Instead of GetQuarter() method, you can also use a dictionary to store your month to quarter mappings Dictionary<int, int> quarter = new Dictionary<int, int>(); quarter.Add(1,1); //of the format Add(month,quarter) quarter.Add(2,1); ... Now instead of GetQuarter(someDate.Month); you can use quarter[someDate.Month];
If you want to compare two dates you should find out the first day of the quarter corresponds every of this dates, then you can compare this two dates: using System; namespace DataTime { class Program { static int GetQuarter (DateTime dt) { int Month = dt.Month; // from 1 to 12 return Month / 3 + 1; } static DateTime GetQuarterFirstDay (DateTime dt) { int monthsOfTheFirstDayOfQuarter = (GetQuarter (dt) - 1) * 3 + 1; return new DateTime(dt.Year, monthsOfTheFirstDayOfQuarter, 1); // it can be changed to // return new DateTime(dt.Year, (dt.Month/3)*3 + 1, 1); } static void Main (string[] args) { DateTime dt1 = new DateTime (2009, 6, 9), dt2 = new DateTime (2009, 7, 9), dt3 = new DateTime (2009, 8, 9), dt4 = new DateTime (2009, 8, 9); Console.WriteLine ("dt1={0}", dt1.AddMonths (1)); Console.WriteLine ("dt2={0}", dt2.AddMonths (1)); Console.WriteLine ("dt3={0}", dt3.AddMonths (1)); DateTime startDate = DateTime.Now, endDate1 = startDate.AddMonths(24).AddDays(1), endDate2 = startDate.AddMonths(24).AddDays(-1), endDate3 = startDate.AddMonths(28); Console.WriteLine ("Now we have={0}", startDate); Console.WriteLine ("endDate1={0}", endDate1); Console.WriteLine ("endDate2={0}", endDate2); Console.WriteLine ("endDate3={0}", endDate3); Console.WriteLine ("GetQuarterFirstDay(startDate)={0}", GetQuarterFirstDay (startDate)); Console.WriteLine ("GetQuarterFirstDay(endDate1)={0}", GetQuarterFirstDay (endDate1)); Console.WriteLine ("GetQuarterFirstDay(endDate2)={0}", GetQuarterFirstDay (endDate2)); Console.WriteLine ("GetQuarterFirstDay(endDate3)={0}", GetQuarterFirstDay (endDate3)); if (DateTime.Compare (GetQuarterFirstDay (endDate2), GetQuarterFirstDay (startDate).AddMonths (24)) > 0) Console.WriteLine ("> 2 Yeas"); else Console.WriteLine ("<= 2 Yeas"); if (DateTime.Compare (GetQuarterFirstDay (endDate3), GetQuarterFirstDay (startDate).AddMonths (24)) > 0) Console.WriteLine ("> 2 Yeas"); else Console.WriteLine ("<= 2 Yeas"); } } } produce dt1=09.07.2009 00:00:00 dt2=09.08.2009 00:00:00 dt3=09.09.2009 00:00:00 Now we have=22.04.2010 11:21:45 endDate1=23.04.2012 11:21:45 endDate2=21.04.2012 11:21:45 endDate3=22.08.2012 11:21:45 GetQuarterFirstDay(startDate)=01.04.2010 00:00:00 GetQuarterFirstDay(endDate1)=01.04.2012 00:00:00 GetQuarterFirstDay(endDate2)=01.04.2012 00:00:00 GetQuarterFirstDay(endDate3)=01.07.2012 00:00:00 <= 2 Yeas > 2 Yeas EDITED: I fixed an error from the first version. Now it should works correct.