How to get difference between two dates in Year/Month/Week/Day? - c#

How to get difference between two dates in Year/Month/Week/Day in an efficient way?
eg. difference between two dates is 1 Year, 2 Months, 3 Weeks, 4 Days.
Difference represents count of year(s), month(s), week(s) and day(s) between two dates.

This is actually quite tricky. A different total number of days can result in the same result. For example:
19th June 2008 to 19th June 2010 = 2 years, but also 365 * 2 days
19th June 2006 to 19th June 2008 = 2 years, but also 365 + 366 days due to leap years
You may well want to subtract years until you get to the point where you've got two dates which are less than a year apart. Then subtract months until you get to the point where you've got two dates which are less than a month apart.
Further confusion: subtracting (or adding) months is tricky when you might start with a date of "30th March" - what's a month earlier than that?
Even further confusion (may not be relevant): even a day isn't always 24 hours. Daylight saving anyone?
Even further confusion (almost certainly not relevant): even a minute isn't always 60 seconds. Leap seconds are highly confusing...
I don't have the time to work out the exact right way of doing this right now - this answer is mostly to raise the fact that it's not nearly as simple as it might sound.
EDIT: Unfortunately I'm not going to have enough time to answer this fully. I would suggest you start off by defining a struct representing a Period:
public struct Period
{
private readonly int days;
public int Days { get { return days; } }
private readonly int months;
public int Months { get { return months; } }
private readonly int years;
public int Years { get { return years; } }
public Period(int years, int months, int days)
{
this.years = years;
this.months = months;
this.days = days;
}
public Period WithDays(int newDays)
{
return new Period(years, months, newDays);
}
public Period WithMonths(int newMonths)
{
return new Period(years, newMonths, days);
}
public Period WithYears(int newYears)
{
return new Period(newYears, months, days);
}
public static DateTime operator +(DateTime date, Period period)
{
// TODO: Implement this!
}
public static Period Difference(DateTime first, DateTime second)
{
// TODO: Implement this!
}
}
I suggest you implement the + operator first, which should inform the Difference method - you should make sure that first + (Period.Difference(first, second)) == second for all first/second values.
Start with writing a whole slew of unit tests - initially "easy" cases, then move on to tricky ones involving leap years. I know the normal approach is to write one test at a time, but I'd personally brainstorm a bunch of them before you start any implementation work.
Allow yourself a day to implement this properly. It's tricky stuff.
Note that I've omitted weeks here - that value at least is easy, because it's always 7 days. So given a (positive) period, you'd have:
int years = period.Years;
int months = period.Months;
int weeks = period.Days / 7;
int daysWithinWeek = period.Days % 7;
(I suggest you avoid even thinking about negative periods - make sure everything is positive, all the time.)

Partly as a preparation for trying to answer this question correctly (and maybe even definitively...), partly to examine how much one can trust code that is pasted on SO, and partly as an exercise in finding bugs, I created a bunch of unit tests for this question, and applied them to many proposed solutions from this page and a couple of duplicates.
The results are conclusive: not a single one of the code contributions accurately answers the question. Update: I now have four correct solutions to this question, including my own, see updates below.
Code tested
From this question, I tested code by the following users:
Mohammed Ijas Nasirudeen, ruffin, Malu MN, Dave, pk., Jani, lc.
These were all the answers which provided all three of years, months, and days in their code. Note that two of these, Dave and Jani, gave the total number of days and months, rather than the total number of months left after counting the years, and the total number of days left after counting the months. I think the answers are wrong in terms of what the OP seemed to want, but the unit tests obviously don't tell you much in these cases. (Note that in Jani's case this was my error and his code was actually correct - see Update 4 below)
The answers by Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir, just i saw, Chalkey and Andy, were incomplete. This doesn't mean that the answers weren't any good, in fact several of them are useful contributions towards a solution. It just means that there wasn't code taking two DateTimes and returning 3 ints that I could properly test. Four of these do however talk about using TimeSpan. As many people have mentioned, TimeSpan doesn't return counts of anything larger than days.
The other answers I tested were from
question 3054715 - LukeH, ho1 and this. ___curious_geek
question 6260372 - Chuck Rostance and Jani (same answer as this question)
question 9 (!) - Dylan Hayes, Jon and Rajeshwaran S P
this.___curious_geek's answer is code on a page he linked to, which I don't think he wrote. Jani's answer is the only one which uses an external library, Time Period Library for .Net.
All other answers on all these questions seemed to be incomplete. Question 9 is about age in years, and the three answers are ones which exceeded the brief and calculated years, months and days. If anyone finds further duplicates of this question please let me know.
How I tested
Quite simply: I made an interface
public interface IDateDifference
{
void SetDates(DateTime start, DateTime end);
int GetYears();
int GetMonths();
int GetDays();
}
For each answer I wrote a class implementing this interface, using the copied and pasted code as a basis. Of course I had to adapt functions with different signatures etc, but I tried to make the minimal edits to do so, preserving all the logic code.
I wrote a bunch of NUnit tests in an abstract generic class
[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()
and added an empty derived class
public class Rajeshwaran_S_P_Test : DateDifferenceTests<Rajeshwaran_S_P>
{
}
to the source file for each IDateDifference class.
NUnit is clever enough to do the rest.
The tests
A couple of these were written in advance and the rest were written to try and break seemingly working implementations.
[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()
{
protected IDateDifference ddClass;
[SetUp]
public void Init()
{
ddClass = new DDC();
}
[Test]
public void BasicTest()
{
ddClass.SetDates(new DateTime(2012, 12, 1), new DateTime(2012, 12, 25));
CheckResults(0, 0, 24);
}
[Test]
public void AlmostTwoYearsTest()
{
ddClass.SetDates(new DateTime(2010, 8, 29), new DateTime(2012, 8, 14));
CheckResults(1, 11, 16);
}
[Test]
public void AlmostThreeYearsTest()
{
ddClass.SetDates(new DateTime(2009, 7, 29), new DateTime(2012, 7, 14));
CheckResults(2, 11, 15);
}
[Test]
public void BornOnALeapYearTest()
{
ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 2, 28));
CheckControversialResults(0, 11, 30, 1, 0, 0);
}
[Test]
public void BornOnALeapYearTest2()
{
ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 3, 1));
CheckControversialResults(1, 0, 0, 1, 0, 1);
}
[Test]
public void LongMonthToLongMonth()
{
ddClass.SetDates(new DateTime(2010, 1, 31), new DateTime(2010, 3, 31));
CheckResults(0, 2, 0);
}
[Test]
public void LongMonthToLongMonthPenultimateDay()
{
ddClass.SetDates(new DateTime(2009, 1, 31), new DateTime(2009, 3, 30));
CheckResults(0, 1, 30);
}
[Test]
public void LongMonthToShortMonth()
{
ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 30));
CheckControversialResults(0, 1, 0, 0, 0, 30);
}
[Test]
public void LongMonthToPartWayThruShortMonth()
{
ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 10));
CheckResults(0, 0, 10);
}
private void CheckResults(int years, int months, int days)
{
Assert.AreEqual(years, ddClass.GetYears());
Assert.AreEqual(months, ddClass.GetMonths());
Assert.AreEqual(days, ddClass.GetDays());
}
private void CheckControversialResults(int years, int months, int days,
int yearsAlt, int monthsAlt, int daysAlt)
{
// gives the right output but unhelpful messages
bool success = ((ddClass.GetYears() == years
&& ddClass.GetMonths() == months
&& ddClass.GetDays() == days)
||
(ddClass.GetYears() == yearsAlt
&& ddClass.GetMonths() == monthsAlt
&& ddClass.GetDays() == daysAlt));
Assert.IsTrue(success);
}
}
Most of the names are slightly silly and don't really explain why code might fail the test, however looking at the two dates and the answer(s) should be enough to understand the test.
There are two functions that do all the Asserts, CheckResults() and CheckControversialResults(). These work well to save typing and give the right results, but unfortunately they make it harder to see exactly what went wrong (because the Assert in CheckControversialResults() will fail with "Expected true", rather than telling you which value was incorrect. If anyone has a better way to do this (avoid writing the same checks each time, but have more useful error messages) please let me know.
CheckControversialResults() is used for a couple of cases where there seem to be two different opinions on what is right. I have an opinion of my own, but I thought I should be liberal in what I accepted here. The gist of this is deciding whether one year after Feb 29 is Feb 28 or Mar 1.
These tests are the crux of the matter, and there could very well be errors in them, so please do comment if you find one which is wrong. It would be also good to hear some suggestions for other tests to check any future iterations of answers.
No test involves time of day - all DateTimes are at midnight. Including times, as long as it's clear how rounding up and down to days works (I think it is), might show up even more flaws.
The results
The complete scoreboard of results is as follows:
ChuckRostance_Test 3 failures S S S F S S F S F
Dave_Test 6 failures F F S F F F F S S
Dylan_Hayes_Test 9 failures F F F F F F F F F
ho1_Test 3 failures F F S S S S F S S
Jani_Test 6 failures F F S F F F F S S
Jon_Test 1 failure S S S S S S F S S
lc_Test 2 failures S S S S S F F S S
LukeH_Test 1 failure S S S S S S F S S
Malu_MN_Test 1 failure S S S S S S S F S
Mohammed_Ijas_Nasirudeen_Test 2 failures F S S F S S S S S
pk_Test 6 failures F F F S S F F F S
Rajeshwaran_S_P_Test 7 failures F F S F F S F F F
ruffin_Test 3 failures F S S F S S F S S
this_curious_geek_Test 2 failures F S S F S S S S S
But note that Jani's solution was actually correct and passed all tests - see update 4 below.
The columns are in alphabetical order of test name:
AlmostThreeYearsTest
AlmostTwoYearsTest
BasicTest
BornOnALeapYearTest
BornOnALeapYearTest2
LongMonthToLongMonth
LongMonthToLongMonthPenultimateDay
LongMonthToPartWayThruShortMonth
LongMonthToShortMonth
Three answers failed only 1 test each, Jon's, LukeH's and Manu MN's. Bear in mind these tests were probably written specifically to address flaws in those answers.
Every test was passed by at least one piece of code, which is slightly reassuring that none of the tests are erroneous.
Some answers failed a lot of tests. I hope no-one feels this is a condemnation of that poster's efforts. Firstly the number of successes is fairly arbitrary as the tests don't evenly cover the problem areas of the question space. Secondly this is not production code - answers are posted so people can learn from them, not copy them exactly into their programs. Code which fails a lot of tests can still have great ideas in it. At least one piece which failed a lot of tests had a small bug in it which I didn't fix. I'm grateful to anyone who took the time to share their work with everyone else, for making this project so interesting.
My conclusions
There are three:
Calendars are hard.
I wrote nine tests, including three where two answers are possible. Some of the tests where I only had one answer might not be unanimously agreed with. Just thinking about exactly what we mean when we say '1 month later' or '2 years earlier' is tricky in a lot of situations. And none of this code had to deal with all the complexities of things like working out when leap years are. All of it uses library code to handle dates. If you imagine the 'spec' for telling time in days, weeks, months and years written out, there's all sorts of cruft. Because we know it pretty well since primary school, and use it everyday, we are blind to many of the idiosyncracies. The question is not an academic one - various types of decomposition of time periods into years, quarters and months are essential in accounting software for bonds and other financial products.
Writing correct code is hard.
There were a lot of bugs. In slightly more obscure topics or less popular questions than the chances of a bug existing without having been pointed out by a commenter are much, much higher than for this question. You should really never, never copy code from SO into your program without understanding exactly what it does. The flipside of this is that you probably shouldn't write code in your answer that is ready to be copied and pasted, but rather intelligent and expressive pseudo-code that allows someone to understand the solution and implement their own version (with their own bugs!)
Unit tests are helpful.
I am still meaning to post my own solution to this when I get round to it (for someone else to find the hidden, incorrect assumptions in!) Doing this was a great example of 'saving the bugs' by turning them into unit tests to fix the next version of the code with.
Update
The whole project is now at https://github.com/jwg4/date-difference
This includes my own attempt jwg.cs, which passes all the tests I currently have, including a few new ones which check for proper time of day handling. Feel free to add either more tests to break this and other implementations or better code for answering the question.
Update 2
#MattJohnson has added an implementation which uses Jon Skeet's NodaTime. It passes all the current tests.
Update 3
#KirkWoll's answer to Difference in months between two dates has been added to the project on github. It passes all the current tests.
Update 4
#Jani pointed out in a comment that I had used his code wrongly. He did suggest methods that counted the years, months and days correctly, (alongside some which count the total number of days and months, not the remainders) however I mistakenly used the wrong ones in my test code. I have corrected my wrapper around his code and it now passes all tests. There are now four correct solutions, of which Jani's was the first. Two use libraries (Intenso.TimePeriod and NodaTime) and two are written from scratch.

For the correct difference calculation of Years/Months/Weeks, the Calendar of the CultureInfo must be considered:
leap vs. non-leap years
months with different count of days
years with different count of weeks (varying by the first day of week and the calendar week rule)
The DateDiff class of the Time Period Library for .NET respects all these factors:
// ----------------------------------------------------------------------
public void DateDiffSample()
{
DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
Console.WriteLine( "Date1: {0}", date1 );
// > Date1: 08.11.2009 07:13:59
DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
Console.WriteLine( "Date2: {0}", date2 );
// > Date2: 20.03.2011 19:55:28
DateDiff dateDiff = new DateDiff( date1, date2 );
// differences
Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
// > DateDiff.Years: 1
Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
// > DateDiff.Quarters: 5
Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
// > DateDiff.Months: 16
Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
// > DateDiff.Weeks: 70
Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
// > DateDiff.Days: 497
Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
// > DateDiff.Weekdays: 71
Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
// > DateDiff.Hours: 11940
Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
// > DateDiff.Minutes: 716441
Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
// > DateDiff.Seconds: 42986489
// elapsed
Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
// > DateDiff.ElapsedYears: 1
Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
// > DateDiff.ElapsedMonths: 4
Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
// > DateDiff.ElapsedDays: 12
Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
// > DateDiff.ElapsedHours: 12
Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
// > DateDiff.ElapsedMinutes: 41
Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
// > DateDiff.ElapsedSeconds: 29
// description
Console.WriteLine( "DateDiff.GetDescription(1): {0}", dateDiff.GetDescription( 1 ) );
// > DateDiff.GetDescription(1): 1 Year
Console.WriteLine( "DateDiff.GetDescription(2): {0}", dateDiff.GetDescription( 2 ) );
// > DateDiff.GetDescription(2): 1 Year 4 Months
Console.WriteLine( "DateDiff.GetDescription(3): {0}", dateDiff.GetDescription( 3 ) );
// > DateDiff.GetDescription(3): 1 Year 4 Months 12 Days
Console.WriteLine( "DateDiff.GetDescription(4): {0}", dateDiff.GetDescription( 4 ) );
// > DateDiff.GetDescription(4): 1 Year 4 Months 12 Days 12 Hours
Console.WriteLine( "DateDiff.GetDescription(5): {0}", dateDiff.GetDescription( 5 ) );
// > DateDiff.GetDescription(5): 1 Year 4 Months 12 Days 12 Hours 41 Mins
Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
// > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample
DateDiff also calculates the difference of Quarters.

Leap years and uneven months actually make this a non-trivial problem. I'm sure someone can come up with a more efficient way, but here's one option - approximate on the small side first and adjust up (untested):
public static void GetDifference(DateTime date1, DateTime date2, out int Years,
out int Months, out int Weeks, out int Days)
{
//assumes date2 is the bigger date for simplicity
//years
TimeSpan diff = date2 - date1;
Years = diff.Days / 366;
DateTime workingDate = date1.AddYears(Years);
while(workingDate.AddYears(1) <= date2)
{
workingDate = workingDate.AddYears(1);
Years++;
}
//months
diff = date2 - workingDate;
Months = diff.Days / 31;
workingDate = workingDate.AddMonths(Months);
while(workingDate.AddMonths(1) <= date2)
{
workingDate = workingDate.AddMonths(1);
Months++;
}
//weeks and days
diff = date2 - workingDate;
Weeks = diff.Days / 7; //weeks always have 7 days
Days = diff.Days % 7;
}

What about using the System.Data.Linq namespace and its SqlMethods.DateDiffMonth method?
For example, say:
DateTime starDT = {01-Jul-2009 12:00:00 AM}
DateTime endDT = {01-Nov-2009 12:00:00 AM}
Then:
int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(startDT, endDT);
==> 4
There are other DateDiff static methods in the SqlMethods class.

Subtract two DateTime instances to give you a TimeSpan which has a Days property. (E.g. in PowerShell):
PS > ([datetime]::today - [datetime]"2009-04-07")
Days : 89
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 0
Ticks : 76896000000000
TotalDays : 89
TotalHours : 2136
TotalMinutes : 128160
TotalSeconds : 7689600
TotalMilliseconds : 7689600000
Converting days into years or weeks is relatively easy (days in a year could be 365, 365.25, ... depending on context). Months is much harder, because without a base date you don't know which month lengths apply.
Assuming you want to start with your base date, you can incrementally substract while counting first years (checking for leap years), then month lengths (indexing from startDate.Month), then weeks (remaining days divided by 7) and then days (remainder).
There are a lot of edge cases to consider, e.g. 2005-03-01 is one year from 2004-03-01, and from 2004-02-29 depending on what you mean by "Year".

Well, #Jon Skeet, if we're not worried about getting any more granular than days (and still rolling days into larger units rather than having a total day count), as per the OP, it's really not that difficult in C#. What makes date math so difficult is that the number of units in each composite unit often changes. Imagine if every 3rd gallon of gas was only 3 quarts, but each 12th was 7, except on Fridays, when...
Luckily, dates are just a long ride through the greatest integer function. These crazy exceptions are maddening, unless you've gone all the way through the wackily-comprised unit, when it's not a big deal any more. If you're born on 12/25/1900, you're still EXACTLY 100 on 12/25/2000, regardless of the leap years or seconds or daylight savings periods you've been through. As soon as you've slogged through the percentages that make up the last composite unit, you're back to unity. You've added one, and get to start over.
Which is just to say that if you're doing years to months to days, the only strangely comprised unit is the month (of days). If you need to borrow from the month value to handle a place where you're subtracting more days than you've got, you just need to know the number of days in the previous month. No other outliers matter.
And C# gives that to you in System.DateTime.DaysInMonth(intYear, intMonth).
(If your Now month is smaller than your Then month, there's no issue. Every year has 12 months.)
And the same deal if we go more granular... you just need to know how many (small units) are in the last (composite unit). Once you're past, you get another integer value more of (composite unit). Then subtract how many small units you missed starting where you did Then and add back how many of those you went past the composite unit break-off with your Now.
So here's what I've got from my first cut at subtracting two dates. It might work. Hopefully useful.
(EDIT: Changed NewMonth > OldMonth check to NewMonth >= OldMonth, as we don't need to borrow one if the Months are the same (ditto for days). That is, Nov 11 2011 minus Nov 9 2010 was giving -1 year, 12 months, 2 days (ie, 2 days, but the royal we borrowed when royalty didn't need to.)
(EDIT: Had to check for Month = Month when we needed to borrow days to subtract a dteThen.Day from dteNow.Day & dteNow.Day < dteThen.Day, as we had to subtract a year to get 11 months and the extra days. Okay, so there are a few outliers. ;^D I think I'm close now.)
private void Form1_Load(object sender, EventArgs e) {
DateTime dteThen = DateTime.Parse("3/31/2010");
DateTime dteNow = DateTime.Now;
int intDiffInYears = 0;
int intDiffInMonths = 0;
int intDiffInDays = 0;
if (dteNow.Month >= dteThen.Month)
{
if (dteNow.Day >= dteThen.Day)
{ // this is a best case, easy subtraction situation
intDiffInYears = dteNow.Year - dteThen.Year;
intDiffInMonths = dteNow.Month - dteThen.Month;
intDiffInDays = dteNow.Day - dteThen.Day;
}
else
{ // else we need to substract one from the month diff (borrow the one)
// and days get wacky.
// Watch for the outlier of Month = Month with DayNow < DayThen, as then we've
// got to subtract one from the year diff to borrow a month and have enough
// days to subtract Then from Now.
if (dteNow.Month == dteThen.Month)
{
intDiffInYears = dteNow.Year - dteThen.Year - 1;
intDiffInMonths = 11; // we borrowed a year and broke ONLY
// the LAST month into subtractable days
// Stay with me -- because we borrowed days from the year, not the month,
// this is much different than what appears to be a similar calculation below.
// We know we're a full intDiffInYears years apart PLUS eleven months.
// Now we need to know how many days occurred before dteThen was done with
// dteThen.Month. Then we add the number of days we've "earned" in the current
// month.
//
// So 12/25/2009 to 12/1/2011 gives us
// 11-9 = 2 years, minus one to borrow days = 1 year difference.
// 1 year 11 months - 12 months = 11 months difference
// (days from 12/25 to the End Of Month) + (Begin of Month to 12/1) =
// (31-25) + (0+1) =
// 6 + 1 =
// 7 days diff
//
// 12/25/2009 to 12/1/2011 is 1 year, 11 months, 7 days apart. QED.
int intDaysInSharedMonth = System.DateTime.DaysInMonth(dteThen.Year, dteThen.Month);
intDiffInDays = intDaysInSharedMonth - dteThen.Day + dteNow.Day;
}
else
{
intDiffInYears = dteNow.Year - dteThen.Year;
intDiffInMonths = dteNow.Month - dteThen.Month - 1;
// So now figure out how many more days we'd need to get from dteThen's
// intDiffInMonth-th month to get to the current month/day in dteNow.
// That is, if we're comparing 2/8/2011 to 11/7/2011, we've got (10/8-2/8) = 8
// full months between the two dates. But then we've got to go from 10/8 to
// 11/07. So that's the previous month's (October) number of days (31) minus
// the number of days into the month dteThen went (8), giving the number of days
// needed to get us to the end of the month previous to dteNow (23). Now we
// add back the number of days that we've gone into dteNow's current month (7)
// to get the total number of days we've gone since we ran the greatest integer
// function on the month difference (23 to the end of the month + 7 into the
// next month == 30 total days. You gotta make it through October before you
// get another month, G, and it's got 31 days).
int intDaysInPrevMonth = System.DateTime.DaysInMonth(dteNow.Year, (dteNow.Month - 1));
intDiffInDays = intDaysInPrevMonth - dteThen.Day + dteNow.Day;
}
}
}
else
{
// else dteThen.Month > dteNow.Month, and we've got to amend our year subtraction
// because we haven't earned our entire year yet, and don't want an obo error.
intDiffInYears = dteNow.Year - dteThen.Year - 1;
// So if the dates were THEN: 6/15/1999 and NOW: 2/20/2010...
// Diff in years is 2010-1999 = 11, but since we're not to 6/15 yet, it's only 10.
// Diff in months is (Months in year == 12) - (Months lost between 1/1/1999 and 6/15/1999
// when dteThen's clock wasn't yet rolling == 6) = 6 months, then you add the months we
// have made it into this year already. The clock's been rolling through 2/20, so two months.
// Note that if the 20 in 2/20 hadn't been bigger than the 15 in 6/15, we're back to the
// intDaysInPrevMonth trick from earlier. We'll do that below, too.
intDiffInMonths = 12 - dteThen.Month + dteNow.Month;
if (dteNow.Day >= dteThen.Day)
{
intDiffInDays = dteNow.Day - dteThen.Day;
}
else
{
intDiffInMonths--; // subtract the month from which we're borrowing days.
// Maybe we shoulda factored this out previous to the if (dteNow.Month > dteThen.Month)
// call, but I think this is more readable code.
int intDaysInPrevMonth = System.DateTime.DaysInMonth(dteNow.Year, (dteNow.Month - 1));
intDiffInDays = intDaysInPrevMonth - dteThen.Day + dteNow.Day;
}
}
this.addToBox("Years: " + intDiffInYears + " Months: " + intDiffInMonths + " Days: " + intDiffInDays); // adds results to a rich text box.
}

If you subtract two instances of DateTime, that will return an instance of TimeSpan, which will represent the difference between the two dates.

DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);
int diffMonth = Math.Abs((dt2.Year - dt1.Year)*12 + dt1.Month - dt2.Month)

I came across this post while looking to solve a similar problem. I was trying to find the age of an animal in units of Years, Months, Weeks, and Days. Those values are then displayed in SpinEdits where the user can manually change the values to find/estimate a birth date. When my form was passed a birth date from a month with less than 31 days, the value calculated was 1 day off. I based my solution off of Ic's answer above.
Main calculation method that is called after my form loads.
birthDateDisplay.Text = birthDate.ToString("MM/dd/yyyy");
DateTime currentDate = DateTime.Now;
Int32 numOfDays = 0;
Int32 numOfWeeks = 0;
Int32 numOfMonths = 0;
Int32 numOfYears = 0;
// changed code to follow this model http://stackoverflow.com/posts/1083990/revisions
//years
TimeSpan diff = currentDate - birthDate;
numOfYears = diff.Days / 366;
DateTime workingDate = birthDate.AddYears(numOfYears);
while (workingDate.AddYears(1) <= currentDate)
{
workingDate = workingDate.AddYears(1);
numOfYears++;
}
//months
diff = currentDate - workingDate;
numOfMonths = diff.Days / 31;
workingDate = workingDate.AddMonths(numOfMonths);
while (workingDate.AddMonths(1) <= currentDate)
{
workingDate = workingDate.AddMonths(1);
numOfMonths++;
}
//weeks and days
diff = currentDate - workingDate;
numOfWeeks = diff.Days / 7; //weeks always have 7 days
// if bday month is same as current month and bday day is after current day, the date is off by 1 day
if(DateTime.Now.Month == birthDate.Month && DateTime.Now.Day < birthDate.Day)
numOfDays = diff.Days % 7 + 1;
else
numOfDays = diff.Days % 7;
// If the there are fewer than 31 days in the birth month, the date calculated is 1 off
// Dont need to add a day for the first day of the month
int daysInMonth = 0;
if ((daysInMonth = DateTime.DaysInMonth(birthDate.Year, birthDate.Month)) != 31 && birthDate.Day != 1)
{
startDateforCalc = DateTime.Now.Date.AddDays(31 - daysInMonth);
// Need to add 1 more day if it is a leap year and Feb 29th is the date
if (DateTime.IsLeapYear(birthDate.Year) && birthDate.Day == 29)
startDateforCalc = startDateforCalc.AddDays(1);
}
yearsSpinEdit.Value = numOfYears;
monthsSpinEdit.Value = numOfMonths;
weeksSpinEdit.Value = numOfWeeks;
daysSpinEdit.Value = numOfDays;
And then, in my spinEdit_EditValueChanged event handler, I calculate the new birth date starting from my startDateforCalc based on the values in the spin edits. (SpinEdits are constrained to only allow >=0)
birthDate = startDateforCalc.Date.AddYears(-((Int32)yearsSpinEdit.Value)).AddMonths(-((Int32)monthsSpinEdit.Value)).AddDays(-(7 * ((Int32)weeksSpinEdit.Value) + ((Int32)daysSpinEdit.Value)));
birthDateDisplay.Text = birthDate.ToString("MM/dd/yyyy");
I know its not the prettiest solution, but it seems to be working for me for all month lengths and years.

TimeSpan period = endDate.AddDays(1) - startDate;
DateTime date = new DateTime(period.Ticks);
int totalYears = date.Year - 1;
int totalMonths = ((date.Year - 1) * 12) + date.Month - 1;
int totalWeeks = (int)period.TotalDays / 7;
date.Year - 1 because the year 0 doesn't exist.
date.Month - 1, the month 0 doesn't exist

Days: (endDate - startDate).Days
Weeks: (endDate - startDate).Days / 7
Years: Months / 12
Months: A TimeSpan only provides Days, so use the following code to get the number of whole months between a specified start and end date. For example, the number of whole months between 01/10/2000 and 02/10/2000 is 1. The the number of whole months between 01/10/2000 and 02/09/2000 is 0.
public int getMonths(DateTime startDate, DateTime endDate)
{
int months = 0;
if (endDate.Month <= startDate.Month)
{
if (endDate.Day < startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year - 1))
+ (12 - startDate.Month + endDate.Month - 1);
}
else if (endDate.Month < startDate.Month)
{
months = (12 * (endDate.Year - startDate.Year - 1))
+ (12 - startDate.Month + endDate.Month);
}
else // (endDate.Month == startDate.Month) && (endDate.Day >= startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year));
}
}
else if (endDate.Day < startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year))
+ (endDate.Month - startDate.Month) - 1;
}
else // (endDate.Month > startDate.Month) && (endDate.Day >= startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year))
+ (endDate.Month - startDate.Month);
}
return months;
}

If you have to find the difference between originalDate and today’s date, Here is a reliable algorithm without so many condition checks.
Declare a intermediateDate variable and initialize to the originalDate
Find difference between years.(yearDiff)
Add yearDiff to intermediateDate and check whether the value is greater than today’s date.
If newly obtained intermediateDate > today’s date adjust the yearDiff and intermediateDate by one.
Continue above steps for month and Days.
I have used System.Data.Linq functions to do find the year, month and day differences. Please find c# code below
DateTime todaysDate = DateTime.Now;
DateTime interimDate = originalDate;
///Find Year diff
int yearDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffYear(interimDate, todaysDate);
interimDate = interimDate.AddYears(yearDiff);
if (interimDate > todaysDate)
{
yearDiff -= 1;
interimDate = interimDate.AddYears(-1);
}
///Find Month diff
int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(interimDate, todaysDate);
interimDate = interimDate.AddMonths(monthDiff);
if (interimDate > todaysDate)
{
monthDiff -= 1;
interimDate = interimDate.AddMonths(-1);
}
///Find Day diff
int daysDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffDay(interimDate, todaysDate);

private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
int gyear = dateTimePicker1.Value.Year;
int gmonth = dateTimePicker1.Value.Month;
int gday = dateTimePicker1.Value.Day;
int syear = DateTime.Now.Year;
int smonth = DateTime.Now.Month;
int sday = DateTime.Now.Day;
int difday = DateTime.DaysInMonth(syear, gmonth);
agedisplay = (syear - gyear);
lmonth = (smonth - gmonth);
lday = (sday - gday);
if (smonth < gmonth)
{
agedisplay = agedisplay - 1;
}
if (smonth == gmonth)
{
if (sday < (gday))
{
agedisplay = agedisplay - 1;
}
}
if (smonth < gmonth)
{
lmonth = (-(-smonth)+(-gmonth)+12);
}
if (lday < 0)
{
lday = difday - (-lday);
lmonth = lmonth - 1;
}
if (smonth == gmonth && sday < gday&&gyear!=syear)
{
lmonth = 11;
}
ageDisplay.Text = Convert.ToString(agedisplay) + " Years, " + lmonth + " Months, " + lday + " Days.";
}

Use Noda Time:
var ld1 = new LocalDate(2012, 1, 1);
var ld2 = new LocalDate(2013, 12, 25);
var period = Period.Between(ld1, ld2);
Debug.WriteLine(period); // "P1Y11M24D" (ISO8601 format)
Debug.WriteLine(period.Years); // 1
Debug.WriteLine(period.Months); // 11
Debug.WriteLine(period.Days); // 24

Use the Subtract method of the DateTime object which returns a TimeSpan...
DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);
TimeSpan ts = dt1.Subtract(dt2);
Double days = ts.TotalDays;
Double hours = ts.TotalHours;
Double years = ts.TotalDays / 365;

I was trying to find a clear answer for Years, Months and Days, and I didn't find anything clear, If you are still looking check this method:
public static string GetDifference(DateTime d1, DateTime d2)
{
int[] monthDay = new int[12] { 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
DateTime fromDate;
DateTime toDate;
int year;
int month;
int day;
int increment = 0;
if (d1 > d2)
{
fromDate = d2;
toDate = d1;
}
else
{
fromDate = d1;
toDate = d2;
}
// Calculating Days
if (fromDate.Day > toDate.Day)
{
increment = monthDay[fromDate.Month - 1];
}
if (increment == -1)
{
if (DateTime.IsLeapYear(fromDate.Year))
{
increment = 29;
}
else
{
increment = 28;
}
}
if (increment != 0)
{
day = (toDate.Day + increment) - fromDate.Day;
increment = 1;
}
else
{
day = toDate.Day - fromDate.Day;
}
// Month Calculation
if ((fromDate.Month + increment) > toDate.Month)
{
month = (toDate.Month + 12) - (fromDate.Month + increment);
increment = 1;
}
else
{
month = (toDate.Month) - (fromDate.Month + increment);
increment = 0;
}
// Year Calculation
year = toDate.Year - (fromDate.Year + increment);
return year + " years " + month + " months " + day + " days";
}

I have below solution which works correctly for me(After doing some Test cases).
Extra Test cases are acceptable.
public class DateDiffernce
{
private int m_nDays = -1;
private int m_nWeek;
private int m_nMonth = -1;
private int m_nYear;
public int Days
{
get
{
return m_nDays;
}
}
public int Weeks
{
get
{
return m_nWeek;
}
}
public int Months
{
get
{
return m_nMonth;
}
}
public int Years
{
get
{
return m_nYear;
}
}
public void GetDifferenceBetwwenTwoDate(DateTime objDateTimeFromDate, DateTime objDateTimeToDate)
{
if (objDateTimeFromDate.Date > objDateTimeToDate.Date)
{
DateTime objDateTimeTemp = objDateTimeFromDate;
objDateTimeFromDate = objDateTimeToDate;
objDateTimeToDate = objDateTimeTemp;
}
if (objDateTimeFromDate == objDateTimeToDate)
{
//textBoxDifferenceDays.Text = " Same dates";
//textBoxAllDifference.Text = " Same dates";
return;
}
// If From Date's Day is bigger than borrow days from previous month
// & then subtract.
if (objDateTimeFromDate.Day > objDateTimeToDate.Day)
{
objDateTimeToDate = objDateTimeToDate.AddMonths(-1);
int nMonthDays = DateTime.DaysInMonth(objDateTimeToDate.Year, objDateTimeToDate.Month);
m_nDays = objDateTimeToDate.Day + nMonthDays - objDateTimeFromDate.Day;
}
// If From Date's Month is bigger than borrow 12 Month
// & then subtract.
if (objDateTimeFromDate.Month > objDateTimeToDate.Month)
{
objDateTimeToDate = objDateTimeToDate.AddYears(-1);
m_nMonth = objDateTimeToDate.Month + 12 - objDateTimeFromDate.Month;
}
//Below are best cases - simple subtraction
if (m_nDays == -1)
{
m_nDays = objDateTimeToDate.Day - objDateTimeFromDate.Day;
}
if (m_nMonth == -1)
{
m_nMonth = objDateTimeToDate.Month - objDateTimeFromDate.Month;
}
m_nYear = objDateTimeToDate.Year - objDateTimeFromDate.Year;
m_nWeek = m_nDays / 7;
m_nDays = m_nDays % 7;
}
}

int day=0,month=0,year=0;
DateTime smallDate = Convert.ToDateTime(string.Format("{0}", "01.01.1900"));
DateTime bigDate = Convert.ToDateTime(string.Format("{0}", "05.06.2019"));
TimeSpan timeSpan = new TimeSpan();
//timeSpan is diff between bigDate and smallDate as days
timeSpan = bigDate - smallDate;
//year is totalDays / 365 as int
year = timeSpan.Days / 365;
//smallDate.AddYears(year) is closing the difference for year because we found the year variable
smallDate = smallDate.AddYears(year);
//again subtraction because we don't need the year now
timeSpan = bigDate - smallDate;
//month is totalDays / 30 as int
month = timeSpan.Days / 30;
//smallDate.AddMonths(month) is closing the difference for month because we found the month variable
smallDate = smallDate.AddMonths(month);
if (bigDate > smallDate)
{
timeSpan = bigDate - smallDate;
day = timeSpan.Days;
}
//else it is mean already day is 0

No perfect solution exists for this problem :)!
I'll show that there is in fact no perfectly consistent answer. To simplify, let's disregard years and weeks, just look at months and days in non-leap year.
What is the expected output for:
28/1 to 28/2?
-> Alice: "1 month" (1 month takes you to the same day of next month)
-> Brad: "1 month and 3 days" (3 days until end of Jan and the entire month of Feb)
Both legitimate answers, and this is already problem number one. But it gets worse. Let's reduce the date difference by 1 day and ask again:
28/1 to 27/2?
-> Alice: "30 days"
-> Brad: "Umm... 1 month and 2 days does not seem right!"
Brad has given up on his concept. But Alice is still happy about hers. Next, let's increase the date difference by 1 day from the initial question:
28/1 to 1/3?
-> Alice: "1 month and 1 day" (1 month to 28/2, plus 1 day)
Alice is really confident now.
29/1 to 1/3?
-> Alice: "Umm... still 1 month 1 day?"
30/1 to 1/3?
-> Alice: "Ok, I see... "
Conclusion
Neither Alice's nor Brad's concept stays consistent. You will have to accept one of the following two caveats for a timespan counter:
It jumps from e.g. 1 month to 1 month and 3 days from one day to the next
It stays put at e.g. 1 month and 1 day for up to 4 days in a row
It doesn't always give you exactly the result you would expect
Personally I'd prefer accepting the last one, if it means your counter keeps counting, but I guess it depends on the use case.
Reasonable approximation
Once you accept caveat #3, adding the calculated timespan to DateTime.MinValue and taking the Year/Month/Day from where you land seems a reasonable approximation to me. Below code is adapted from this answer. You could roll in accounting for leap years as well, to have a few more days where the output meets the expectations.
public static string GetYearMonthDayDiff(DateTime start, DateTime end)
{
var span = (end - start).Duration(); // absolute value (allows switched start/end)
var diff = DateTime.MinValue + span;
int years = diff.Year - 1;
int months = diff.Month - 1;
int days = diff.Day - 1; // matter of definition if you start counting at 0 or 1
return $"{years}Y {months}M {days}D";
}
The code above then gives the following answers
28/1 to 27/2? -> 30 days
28/1 to 28/2? -> 1 month
28/1 to 1/3? -> 1 month and 1 day
29/1 to 1/3 -> 1 month
30/1 to 1/3 -> 30 days
Don't you love it, when it all doesn't quite add up :)?

Based on Joaquim's answer, but fixing the calculation when end date month is less than start date month, and adding code to handle end date before start date:
public static class GeneralHelper
{
public static int GetYears(DateTime startDate, DateTime endDate)
{
if (endDate < startDate)
return -GetYears(endDate, startDate);
int years = (endDate.Year - startDate.Year);
if (endDate.Year == startDate.Year)
return years;
if (endDate.Month < startDate.Month)
return years - 1;
if (endDate.Month == startDate.Month && endDate.Day < startDate.Day)
return years - 1;
return years;
}
public static int GetMonths(DateTime startDate, DateTime endDate)
{
if (startDate > endDate)
return -GetMonths(endDate, startDate);
int months = 12 * GetYears(startDate, endDate);
if (endDate.Month > startDate.Month)
months = months + endDate.Month - startDate.Month;
else
months = 12 - startDate.Month + endDate.Month;
if (endDate.Day < startDate.Day)
months = months - 1;
return months;
}
}
[TestClass()]
public class GeneralHelperTest
{
[TestMethod]
public void GetYearsTest()
{
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2000, 12, 31)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 4, 4)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 5, 4)));
Assert.AreEqual(1, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 5, 5)));
Assert.AreEqual(1, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 12, 31)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 12, 31), new DateTime(2000, 5, 5)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2001, 4, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2001, 5, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetYears(new DateTime(2001, 5, 5), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetYears(new DateTime(2001, 12, 31), new DateTime(2000, 5, 5)));
}
[TestMethod]
public void GetMonthsTest()
{
Assert.AreEqual(0, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 4)));
Assert.AreEqual(1, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 5)));
Assert.AreEqual(1, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 6)));
Assert.AreEqual(11, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 5, 4)));
Assert.AreEqual(12, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 5, 5)));
Assert.AreEqual(13, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 6, 6)));
Assert.AreEqual(0, GeneralHelper.GetMonths(new DateTime(2000, 6, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetMonths(new DateTime(2000, 6, 5), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetMonths(new DateTime(2000, 6, 6), new DateTime(2000, 5, 5)));
Assert.AreEqual(-11, GeneralHelper.GetMonths(new DateTime(2001, 5, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(-12, GeneralHelper.GetMonths(new DateTime(2001, 5, 5), new DateTime(2000, 5, 5)));
Assert.AreEqual(-13, GeneralHelper.GetMonths(new DateTime(2001, 6, 6), new DateTime(2000, 5, 5)));
}
}
EDIT No that still doesn't work. It fails this test:
Assert.AreEqual(24, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2003, 5, 5)));
Expected:<24>. Actual:<12>

Console.WriteLine("Enter your Date of Birth to Know your Current age in DD/MM/YY Format");
string str = Console.ReadLine();
DateTime dt1 = DateTime.Parse(str);
DateTime dt2 = DateTime.Parse("10/06/2012");
int result = (dt2 - dt1).Days;
result = result / 365;
Console.WriteLine("Your Current age is {0} years.",result);

DateTime startTime = DateTime.Now;
DateTime endTime = DateTime.Now.AddSeconds( 75 );
TimeSpan span = endTime.Subtract ( startTime );
Console.WriteLine( "Time Difference (seconds): " + span.Seconds );
Console.WriteLine( "Time Difference (minutes): " + span.Minutes );
Console.WriteLine( "Time Difference (hours): " + span.Hours );
Console.WriteLine( "Time Difference (days): " + span.Days );
Output:
Time Difference (seconds): 15
Time Difference (minutes): 1
Time Difference (hours): 0
Time Difference (days): 0

Related

c# Round minute to nearest quarter only if more than 5 minute window [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I want to round the time minute to nearest quarter up and down only if the difference is more than 5 minute.
I have date time in the format "2019/02/19 10:05 AM"
10:05 = 10:00
10:06 = 10:15
10:20 = 10:15
10:21 = 10:30
and so on.
Round down to 0,15,30, 45 if there are less than 5 minutes in the difference, or round up to 0,15,30,45 when there are more than 5 minutes
Let me know how I can achieve this.
Thanks!
Searching found this method which will round up to the nearest quarter of an hour
private DateTime MyRoundTime(DateTime date)
{
TimeSpan roundMins = TimeSpan.FromMinutes(15);
return new DateTime(((date.Ticks + (roundMins.Ticks - 1)/2) / roundMins.Ticks) * roundMins.Ticks);
}
this will round up after 7.5 minutes after the quarter hour. What you want is to round up after the 5th minute, so simply add 2.5 minutes to the original time.
private DateTime MyRoundTime(DateTime date)
{
TimeSpan roundMins = TimeSpan.FromMinutes(15);
date += TimeSpan.FromMinutes(2.5);
return new DateTime(((date.Ticks + (roundMins.Ticks - 1)/2) / roundMins.Ticks) * roundMins.Ticks);
}
You can use modular arithmetic:
static DateTime RoundTime(DateTime dt, int roundInterval, int roundPoint)
{
TimeSpan ts = dt.TimeOfDay;
int modInterval = ts.Minutes % roundInterval;
if (modInterval < roundPoint || (modInterval == roundPoint && ts.Seconds == 0 && ts.Milliseconds == 0))
{
return dt.Subtract(new TimeSpan(0, modInterval, ts.Seconds));
} else
{
return dt.Add(new TimeSpan(0, roundInterval - modInterval, -ts.Seconds));
}
}
It takes in a DateTime, and an interval and returns the rounded version. Call like
static void Main(string[] args)
{
Console.WriteLine(RoundTime(new DateTime(2019, 2, 19, 11, 35, 30), 15, 5));
Console.WriteLine(RoundTime(DateTime.Now, 15, 5));
Console.ReadLine();
}
You can then format the returned DateTime as you wish ("AM"/"PM" etc)

Adding two periods on nodatime

Where listObjAge is a list with multiple periods;
Period objTotalPeriod = listObjAge[0].period;
for (int i = 1; i < listObjAge.Count; i++) {
objTotalPeriod += listObjAge[i].period;
}
In-short:
What i am getting:
listObjAge[0].period + listObjAge[1].period = ????.
2 yr 1 mnth 28 days + 0 yr 8 mnth 30 days = 2 yr 9 mnth 58 days
// this result is not wrong but is there any way to correctly add days for the above code.
What i am expecting:
2 yr 1 mnth 28 days + 0 yr 8 mnth 30 days = 2 yr 10 mnth 28 days
As you can see i want to add results of two period. Is there any way we can achieve it using nodatime.
Solved:
I know its not correct theoretically. But it worked for me.
int intDays = 0;
for (int i = 0; i < listObjAge.Count; i++) {
intDays += listObjAge[i].period.Days; // adding all the days for every period
}
strYear = (intDays / 365).ToString();
strMonth = ((intDays % 365) / 30).ToString();
strDays = ((intDays % 365) % 30).ToString();
You should look at the user guide for Noda Time that describes arithmetic http://nodatime.org/2.0.x/userguide/arithmetic - look under the section "Adding a Period" for more information.
It's easiest to think about where this can be confusing with an example. Suppose we add "one month minus three days" to January 30th 2011:
Period period = Period.FromMonths(1) - Period.FromDays(3);
LocalDate date = new LocalDate(2011, 1, 30);
date = date + period;
If you give this puzzle to a real person, they may well come up with an answer of "February 27th" by waiting until the last moment to check the validity. Noda Time will give an answer of February 25th, as the above code is effectively evaluated as:
Period period = Period.FromMonths(1) - Period.FromDays(3);
LocalDate date = new LocalDate(2011, 1, 30);
date = date + Period.FromMonths(1); // February 28th (truncated)
date = date - Period.FromDays(3); // February 25th
The benefit of this approach is simplicity and predictability: when you know the rules, it's very easy to work out what Noda Time will do. The downside is that if you don't know the rules, it looks like it's broken.
With your code
According to the documentation, the result you are getting is the expected behavior based on the "rules". Simply, your addition operation on two two periods will evaluate to:
Years (2 + 0) = 2
Months(1 + 8) = 9
Days (28 + 30) = 58
Your comment:
this result is not wrong but is there any way to correctly add days for the above code.
What do you mean as "correct"? Are you saying that 28 + 30 = 58 is incorrect?
Alternatives
int days = 28 + 30; // carry over your days and +1 month or whatever logic you had in mind
int months = 1 + 8;
Period p1 = new PeriodBuilder { Days = days, Months = months }.Build();
It sounds like you're looking for something to normalize the months and days (and weeks?). The existing Normalize method deals with everything from "days downwards" (e.g. hours) so you can use that to start with:
public static Period NormalizeIncludingMonths(this Period period, int daysPerMonth)
{
period = period.Normalize();
int extraMonths = days / daysPerMonth;
int months = period.Months + extraMonths;
int extraYears = months / 12;
// Simplest way of changing just a few parts...
var builder = period.ToBuilder();
builder.Years += extraYears;
builder.Months = months % 12;
builder.Days = days % daysPerMonth;
return builder.Build();
}
So in your case, it sounds like you might want:
objTotalPeriod = objTotalPeriod.NormalizeIncludingMonths(31);
Note that arithmetic using this may well produce "odd" results, just as part of the nature of calendrical arithmetic.
Use this
public static Period add(Period b, Period c)
{
int years = b.year + c.year;
int months = b.month + c.month;
int days= b.days + c.days;
return new PeriodBuilder { Days = days, Months = months , Years = years}.Build();
}

Update a Date to a specific month

In my MVC application, my users said that they do written tests 2 times a year.. in March and September. However, if a user fails a test then they retest in 90 days (doesn't have to be March or September).
For example, if a user takes their very first test on 3/2/2016, and that user passes, then that user doesn't need to take the other test until anyday in September. But if that user fails, then that user needs to retake the test on the 31st of May. So let me try and illustrate this with a little bit of code.
DateTime usersTestDate = new DateTime(2016, 3, 2);
// user fails
usersTestDate = usersTestDate.AddDays(90);
// usersTestDate is now 5/31/2016
// now the user retakes the test and passes
// usersTestDate should now be in September of 2016.
How do I get that to happen, since usersTestDate could essentially be any date in the book if the user fails.. Basically, if the user passes a retake of the exam in any months besides March or September.. how do I get their new date to be in either March or September?
I have created a dotnetfiddle
Any help is appreciated.
UPDATE
If they fail again after already failing once, then they keep retaking every 90 days. If they keep failing their March test past September then they skip September and just go to March again if they pass
if (userTestDate.Month < 3)
userTestDate = new DateTime(userTestDate.Year, 3, 1);
else if (userTestDate.Month < 9)
userTestDate = new DateTime(userTestDate.Year, 9, 1);
else
userTestDate = new DateTime(userTestDate.Year + 1, 3, 1);
You don't specify which day of the month the March & September dates should be, so I have arbitrarily chosen the first.
I think this captures what you're after. I set the next test date to the last day in Sept/March, although you could easily change that based on your requirements.
public DateTime GetNextTestDate(DateTime testDate, bool passed)
{
if (passed)
{
if (testDate.Month >= 3 && testDate.Month < 9)
return new DateTime(testDate.Year, 9, 30);
else
return new DateTime(testDate.Year + (testDate.Month >= 9 && testDate.Month <= 12 ? 1 : 0), 3, 31); // (add a year if we're 9-12)
}
else
return testDate.AddDays(90);
}
To simplify the logic consider the following.
When someone passes, we do not need to care about the last test date; we can just schedule them for the next available test, based off the current date. The code below says you can only book for that test the month before
If they fail, then add 90 days to the last test date.
public DateTime NextTestDate(DateTime testDate, bool passed)
{
if (passed)
{
var now = DateTime.Now;
if (DateTime.Now.Month < 3)
return new DateTime(now.Year, 3, 2);
if (DateTime.Now.Month < 9)
return new DateTime(now.Year, 9, 2);
return new DateTime(now.Year + 1, 3, 2);
}
return testDate.AddDays(90);
}

c# date comparison returning a DateTime object

Given a DateTime object (last purchase) can I compare against DateTime.Now to return a new DateTime object that contains, years, months, days, hours and seconds?
I can compare dates and return the number of years however, I need all details down to the second.
Any help or examples would be appreciated
You don't want to have a new DateTime object. What you want to have is a TimeSpan. And that's what you get if you subtract DateTime instances:
TimeSpan difference = DateTime.Now - yourDate
No, you can't, because a DateTime represents an absolute point in time, not a span of time. The correct object to use for the difference is TimeSpan. It has properties for all the relevant time units. You can get one by just subtracting two DateTimes:
TimeSpan ts = DateTime.Now - purchaseDate;
You can use the TimeSpan to compare two dates. Have a look at
DateTime date1 = new DateTime(2010, 1, 1, 8, 0, 15);
DateTime date2 = new DateTime(2010, 8, 18, 13, 30, 30);
// Calculate the interval between the two dates.
TimeSpan interval = date2 - date1;
Years and months have to be calculated through custom coding as month is a variable unit of measure but since TimeSpan tell you the days you can calculate months and years yourself.
Have a look at this post.
Have a look at this forum.
Hope this help.
TimeSpan is the correct way to handle this, however, if you don't want to do the conversion of months/years yourself, you can always use some recursive method to subtract years until you get to the correct year, months until you get to the correct month, days until you get to the correct day, etc. It would suck and be ugly, though.
Also, you'd need a custom object that would hold the data for you.
A rough cut would be something like
DateTime original = [Whatever]
DateTime compare = [Whatever]
DateTimeDiff difference = GetDifference(original, compare)
GetDifference(DateTime original, DateTime compare)
{
DateTimeDiff difference = new DateTimeDifference
while(!original.Equals(compare) && original.Year >= compare.Year)
{
original.AddYears(-1);
difference.Years++;
}
<snip... //more code to do the same thing for months, days, hours, etc.)
}
Like I said, it would suck and be ugly, but you could do it.
This library includes the class DateDiff with support of Year and Months:
// ----------------------------------------------------------------------
public void DateDiffSample()
{
DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
Console.WriteLine( "Date1: {0}", date1 );
// > Date1: 08.11.2009 07:13:59
DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
Console.WriteLine( "Date2: {0}", date2 );
// > Date2: 20.03.2011 19:55:28
DateDiff dateDiff = new DateDiff( date1, date2 );
// differences
Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
// > DateDiff.Years: 1
Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
// > DateDiff.Quarters: 5
Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
// > DateDiff.Months: 16
Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
// > DateDiff.Weeks: 70
Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
// > DateDiff.Days: 497
Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
// > DateDiff.Weekdays: 71
Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
// > DateDiff.Hours: 11940
Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
// > DateDiff.Minutes: 716441
Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
// > DateDiff.Seconds: 42986489
} // DateDiffSample
If you want years and months you have to write your own code. It's actually tricky to write this code with an output which is 100% correct due to the different leapyear issues.
Here are a couple of tricky special cases (Note 2012 is a leap year, 2011 isn't):
Between February 28th 2010 and February 28th 2011 - it is 1y, 0m, 0d
Between February 28th 2011 and February 28th 2012 - it is 1y, 11m, 28d
Between February 28th 2011 and February 29th 2012 - it is 1y, 0m, 0d
Between February 29th 2012 and February 28th 2013 - it is 1y, 0m, 0d
Between February 10th 2011 and March 9th 2011 - it is 0y, 0m, 27d
Between February 10th 2012 and March 9th 2012 - it is 0y, 0m, 28d
I think you need to implement something like AllenG's algorithm with a few modifications
You need to start with the earliest datetime (datetime1). Make sure that it is earliest.
Loop until datetime1.AddYears(1) compares as greater than or equal to your latest datetime (datetime2). Make sure to compare the entire datetimes and not just the year property of starttime and endttime.
Increment a year counter on each iteration.
actually add a year to your datetime1 copy.
Start with the datetime1 copy (to which you have added years in step 2)
Loop until datetime1.AddMonths(1) compares as greater than or equal to your latest datetime (datetime2). Again: Make sure you compare the entire datetimes and not just the month component.
Increment a month counter on each iteration.
actually add a month to your datetime1 copy.
etc. etc
This algorithm ensures that you are calculating your years, months, days, hours, minutes and seconds differences using the right excerpt of the underlying calendar.
I think you need to combine the TimeSpan and some custom calculations. This, because you would like the years and months, which have a variable length. In addition you also want the number of hours, minutes and seconds.
I think this approach would serve your needs:
var date1 = new DateTime(2010, 1, 1, 8, 0, 15);
var date2 = new DateTime(2007, 8, 18, 13, 30, 30);
var differenceYears = date1.Year - date2.Year;
var tempDate = date2.AddYears(differenceYears);
var differenceMonths = date1.Month - tempDate.Month;
if (differenceMonths < 0)
{
differenceMonths += 12;
differenceYears--;
}
tempDate = date2.AddYears(differenceYears).AddMonths(differenceMonths);
var additionalTimeSpan = date1 - tempDate;
var differenceDays = additionalTimeSpan.Days;
var differenceHours = additionalTimeSpan.Hours;
var differenceMinutes = additionalTimeSpan.Minutes;
var differenceSeconds = additionalTimeSpan.Seconds;

Function to determine first day of a month, last day of a month, etc.

I'm creating a scheduler and need to be able to do the following in C#:
Find the 1st Tuesday of June 2012
Find the last Friday of March 2008
Find every Saturday in January 2013
Find the 3rd Friday in July 2009
Find every Saturday over the next 3 months
Find every day in March 2018
The results should come back as DateTime or List<DateTime>.
Here are methods to find the first/last specified day of week in a given month:
public DateTime GetFirstDayOfWeekInMonth(int year, int month, DayOfWeek dayOfWeek)
{
DateTime dt = new DateTime(year, month, 1);
int first = (int)dt.DayOfWeek;
int wanted = (int)dayOfWeek;
if (wanted < first)
wanted += 7;
return dt.AddDays(wanted - first);
}
public DateTime GetLastDayOfWeekInMonth(int year, int month, DayOfWeek dayOfWeek)
{
int daysInMonth = CultureInfo.CurrentCulture.Calendar.GetDaysInMonth(year, month);
DateTime dt = new DateTime(year, month, daysInMonth);
int last = (int)dt.DayOfWeek;
int wanted = (int)dayOfWeek;
if (wanted > last)
last += 7;
return dt.AddDays(wanted - last);
}
From those, you can easily find the answers to the other questions... just add 7 days to find the next occurence of the day you're looking for
EDIT: thinking more about it, it would be pretty handy to have that in the form of extension methods, such as :
Console.WriteLine("Next monday : {0}", DateTime.Today.Next(DayOfWeek.Monday));
Console.WriteLine("Last saturday : {0}", DateTime.Today.Previous(DayOfWeek.Saturday));
Here is the implementation :
public static class DateExtensions
{
public static DateTime Next(this DateTime from, DayOfWeek dayOfWeek)
{
int start = (int)from.DayOfWeek;
int wanted = (int)dayOfWeek;
if (wanted < start)
wanted += 7;
return from.AddDays(wanted - start);
}
public static DateTime Previous(this DateTime from, DayOfWeek dayOfWeek)
{
int end = (int)from.DayOfWeek;
int wanted = (int)dayOfWeek;
if (wanted > end)
end += 7;
return from.AddDays(wanted - end);
}
}
It's probably more flexible than the first methods I suggested... With that, you can easily do things like that :
// Print all Sundays between 2009/01/01 and 2009/03/31
DateTime from = new DateTime(2009, 1, 1);
DateTime to = new DateTime(2009, 3, 31);
DateTime sunday = from.Next(DayOfWeek.Sunday);
while(sunday <= to)
{
Console.WriteLine(sunday);
sunday = sunday.AddDays(7);
}
Inspired by this question it seemed like a fun thing to make something that allows you to treat dates as sequences and query them with LINQ. I've made a simple project that can be downloaded here from GitHub. Don't know if it was necessary to add it to source control as I doubt I'll be working on it, but had to upload it somewhere and uploading it to RapidShare seemed a little dodgy :)
Your first questions solved with my "Linq-to-DateTime":
/*
* 1. Find the 1st Tuesday of June 2012
2. Find the last Friday of March 2008
3. Find every Saturday in January 2013
4. Find the 3rd Friday in July 2009
5. Find every Saturday over the next 3 months
6. Find every day in March 2018
*/
var firstTuesday = (from d in DateSequence.FromYear(2012).June()
where d.DayOfWeek == DayOfWeek.Tuesday
select d).First();
var lastFriday = (from d in DateSequence.FromYear(2008).March()
where d.DayOfWeek == DayOfWeek.Friday
select d).Last();
var saturdays = (from d in DateSequence.FromYear(2013).January()
where d.DayOfWeek == DayOfWeek.Saturday
select d);
var thirdFriday = (from d in DateSequence.FromYear(2009).July()
where d.DayOfWeek == DayOfWeek.Friday
select d).Skip(2).First();
var nextSaturdays = (from d in DateSequence.FromDates(DateTime.Today, DateTime.Today.AddMonths(3))
where d.DayOfWeek == DayOfWeek.Saturday
select d);
var allDays = (from d in DateSequence.FromYear(2018).March()
select d);
Not shown in this example is that you can choose the granularity of your queries. Meaning that if you do this:
var days = (from d in DateSequence.FromYears(2001, 2090).AsYears()
where d.Year % 2 == 0
select d.Year).ToList();
You will iterate through the dates with an increment of a year. The default is AsDays() if you don't specify anything.
The project is pretty barebone, no commentary or unit tests, but I hope this can still help you. If not, then it was fun writing anyway :)
Here is an example on how to do the first day of a month.
public enum Month
{
January = 1,
Febuary = 2,
March = 3,
April = 4,
May = 5,
June = 6,
July = 7,
August = 8,
September = 9,
October = 10,
November = 11,
December = 12
}
public static Nullable<DateTime> FindTheFirstDayOfAMonth(DayOfWeek dayOfWeek, Month month, int year)
{
// Do checking of parameters here, i.e. year being in future not past
// Create a DateTime object the first day of that month
DateTime currentDate = new DateTime(year, (int)month, 1);
while (currentDate.Month == (int)month)
{
if (currentDate.DayOfWeek == dayOfWeek)
{
return currentDate;
}
currentDate = currentDate.AddDays(1);
}
return null;
}
You call it like
Nullable<DateTime> date = Program.FindTheFirstDayOfAMonth(DayOfWeek.Monday, Month.September, 2009);
So you get the idea. You will have to do various functions do get what you want to achieve.
YO YO YO - You guys are making this too hard:
int DaysinMonth = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month);
When I wrote a scheduler I pretty much copied SQL Servers sysschedules table
http://msdn.microsoft.com/en-us/library/ms178644.aspx
Using things like Frequency Type, Frequency Interval, etc... really made it easy to compute the values in code, but I am guessing there is a sproc to do it for you. I am searching for that now.
Yes, the .NET Framework will support that without any problems ;)
But seriously - you should be able to write code that does that fairly easily. If not, ask a question when you get stuck, and we'll help out. But you won't need any external assemblies - just go with a standard C# class =)

Categories

Resources