Calculating ticks from given year (Mathematical Approach) - c#

I've been trying to convert a given date to ticks using the following formula and since I've just begun writing this function I do not yet consider the month and day values :
public static long DateToTicks(int year, int month, int day) {
var leapYears = (year - 1) / 4;
var result = ((year - 1) * 365 * OneDay.Ticks) + (leapYears * OneDay.Ticks);
return result;
}
The OneDay.Ticks is a constant here whose value is 3600 * 24 * 1000 * 10000.
The problem I have faced is that, when I calculate ticks from day zero (i.e. 0001-01-01, 0002-01-01) it works fine until I reach year 101 where I get an additional day! I compare my result against the value returned by the DateTime struct in .Net Core. For example:
var myResult = DateToTicks(100, 01, 01);
var dateTimeResult = new DateTime(100,01,01).Ticks;
Until this date, myResult == dateTimeResult is always true, but when I enter the next century I see that the result returned by DateTime struct is one day behind, and as I enter another century this value doubles.
However, I know that in 101 years, there are 25 leap years which means that I have to multiply 75 years by 365 and the rest by 366 and then add them together, and I cannot understand why my result is different from dot net's DateTime result.
What's the problem with my approach? Given that month and day would be not important (always set to 01).

since you dont seem to want to look at the c# code. I looked there for you - took me 1 minute
private static long DateToTicks(int year, int month, int day) {
if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) {
int[] days = IsLeapYear(year)? DaysToMonth366: DaysToMonth365;
if (day >= 1 && day <= days[month] - days[month - 1]) {
int y = year - 1;
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
return n * TicksPerDay;
}
}
throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadYearMonthDay"));
}
and
public static bool IsLeapYear(int year) {
if (year < 1 || year > 9999) {
throw new ArgumentOutOfRangeException("year", Environment.GetResourceString("ArgumentOutOfRange_Year"));
}
Contract.EndContractBlock();
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
here is the direct link
https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,891f8af5025ab2f3

Change your logic for leap year calculation to this and it works well:
var leapYears = ((year - 1) / 4) - (year / 400 > 0 ? ((year) / 100) - (((year) / 400)) : ((year - 1) / 100)) + (year % 100 == 0 && year % 400 != 0 ? 1 : 0) + (year == 100 || year == 200 || year == 300 ? -1 : 0);

A simple divide by 4 is not the correct way to determine a leap year.
Here is a way to test for a leap year:
The year can be evenly divided by 4;
If the year can be evenly divided by 100, it is NOT a leap year, unless;
The year is also evenly divisible by 400. Then it is a leap year.
So, 1900 is not a leap year, but 2000 is a leap year.

Related

Check to see if year is leap year

<script Language="c#" runat="server">
void Page_Load()
{
int currentYear = DateTime.Now.Year();
if (currentYear % 400 == 0) {
Message2.Text = ("This is a leap year");
}
else {
Message2.Text = ("This is not a leap year");
}
}
Currently I am getting a RunTime error. My goal is to test whether or not the current year, using DateTime.Now.Year() is a leap year or not. I think the issue is that I am not properly converting year to int? Please advise.
You can just use DateTime.IsLeapYear():
if (DateTime.IsLeapYear(year))
{
//do stuff
}
For those that come here for the rules:
According to wikipedia, these extra days occur in each year
which is an integer multiple of 4.
years that are evenly divisible by 100 are not leap years unless evenly divisible by 400.
So this results in this function:
// PRE: no year < 1 or > 9999
// POST: true if year is a leap year, or false if not.
public static bool IsLeapYear(int year)
{
if (year < 1 || year > 9999)
{
throw new ArgumentOutOfRangeException("year", Environment.GetResourceString("ArgumentOutOfRange_Year"));
}
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
And now I'm wondering what should happen if I use minus-years for BC - actually, the question is which calendar does even apply, especially prior to 1753 (for Britain) ? ;)
I use C# and this code for leap years.
cheers
if ((jahr % 4 == 0 && jahr % 400 == 0) || (jahr % 4 == 0 && !(jahr % 100 == 0)))
{
Console.WriteLine(jahr + " ist ein Schaltjahr");
}
else
Console.WriteLine(jahr + " ist kein Schaltjahr");
static int GeveDays()
{
int days;
if ((DateTime.Now).Year / 4 != 1 || (DateTime.Now).Year / 400 != 1) {
Console.WriteLine("it is a common year");
days = 365;
return days;
}
else if ((DateTime.Now).Year / 100 != 1) {
Console.WriteLine("it is a leap year");
days = 366;
return days;
}
else {
Console.WriteLine("it is a leap year");
days = 366;
return days;
}
}
int x= int.Parse(Console.ReadLine());
if((x%400==0)||(x%100==0)||(x%4==0))
{
Console.WriteLine(" \n\n\n The year is a leap year ...!");
}
else
{
Console.WriteLine("\n\n\n The year is not a leap year");
}

Date algorithm - Add days without considering leap years

The system I'm working on is built and configured in such a way, where users cannot choose leap years when setting up a recurring payment. This results in all the date-math behind the scenes having to ignore leap years. (I didn't choose this, but this is how it was written)
I have to write a method that takes in a DateTime value, and adds days to the date, ignoring leap years, which essentially means ignoring Feb 29th and pretending it doesn't exist.
For example, If I'm adding 365 days to 1/1/2016, that should result in 1/1/2017, not 12/31/2016.
I'm using .NET, so I can make use of DateTime.IsLeapYear, and other helper methods.
This is a work in progress, and here is what I have so far. I started taking a simpler route, and I'm now realizing that it's going to require a more complex algorithm.
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
// Nothing to do
if (daysToAdd == 0)
{
return date;
}
// NOTE: This is an invalid approach; using DateTime.AddDays will take leap years into account
DateTime dateWithAddedDays = date.AddDays(daysToAdd);
const int FEB_28_DAY_OF_YEAR = 59;
int daysToSubtractForLeapYearConsideration = 0;
// The year is a leap year, which is under the feb 28 day threshold, and we're adding enough days to push it over the feb 28 day threshold
// This will result in .NET taking into account the feb 29th (the leap year day), but we have to subtract that leap year day since the system doesn't take feb 29th into account
if (DateTime.IsLeapYear(date.Year) && date.DayOfYear < FEB_28_DAY_OF_YEAR && (date.DayOfYear + daysToAdd > FEB_28_DAY_OF_YEAR))
{
daysToSubtractForLeapYearConsideration++;
}
// The resulting date (after the days are added or subtracted) is a leap year, whose day is past the feburary 28 day threshold, and it's not the same year as the date (i.e. it spans across "n" years)
if (DateTime.IsLeapYear(dateWithAddedDays.Year) && dateWithAddedDays.DayOfYear > FEB_28_DAY_OF_YEAR && dateWithAddedDays.Year != date.Year)
{
daysToSubtractForLeapYearConsideration++;
}
// We determined if the original date should be leap year considered, as well as the resulting date/year with the days added. Now see if there are any years in between
// that we should consider
bool isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor = Math.Abs(date.Year - dateWithAddedDays.Year) > 0;
if (isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor)
{
for (int leapYearEvalIndex = Math.Min(date.Year, dateWithAddedDays.Year); leapYearEvalIndex <= Math.Max(date.Year, dateWithAddedDays.Year); leapYearEvalIndex++)
{
bool isYearPartOfTheYearsThatWeveAlreadyChecked = leapYearEvalIndex == date.Year || leapYearEvalIndex == dateWithAddedDays.Year;
if (!isYearPartOfTheYearsThatWeveAlreadyChecked && DateTime.IsLeapYear(leapYearEvalIndex))
{
daysToSubtractForLeapYearConsideration++;
}
}
}
DateTime dateResult = date.AddDays(daysToAdd - daysToSubtractForLeapYearConsideration);
// The system does not allow 2/29 days, hence all this crazy date math
if (dateResult.Month == 2 && dateResult.Day == 29)
{
dateResult = dateResult.AddDays(1);
}
return dateResult;
}
The logic has to take into account negative numbers as well (i.e. subtracting), which the above code fails on.
The above code by no means works, but I wanted to demonstrate that I'm trying to tackle the problem, and not simply asking without having tried anything.
Edit
I've come up with an algorithm pretty close to David's approach. (I wrote it, and then came back to StackOverflow to check responses).
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
// Nothing to do
if (daysToAdd == 0)
{
return date;
}
DateTime dateResult = date;
// Are we adding or subtracting
bool areWeAddingDays = daysToAdd > 0;
int daysToAccountForInRegardToLeapYearDates = 0,
absDaysToAdd = Math.Abs(daysToAdd);
for (int i = 1; i <= absDaysToAdd; i++)
{
dateResult = dateResult.AddDays(areWeAddingDays ? 1 : -1);
if (dateResult.Month == 2 && dateResult.Day == 29)
{
daysToAccountForInRegardToLeapYearDates++;
}
}
dateResult = dateResult.AddDays(areWeAddingDays ? daysToAccountForInRegardToLeapYearDates : -daysToAccountForInRegardToLeapYearDates);
return dateResult;
}
Here is an extension method that works. Will also work if you're adding or subtracting enough days to span multiple leap years.
public static DateTime AddDaysWithoutLeapYear(this DateTime input, int days)
{
var output = input;
if (days != 0)
{
var increment = days > 0 ? 1 : -1; //this will be used to increment or decrement the date.
var daysAbs = Math.Abs(days); //get the absolute value of days to add
var daysAdded = 0; // save the number of days added here
while (daysAdded < daysAbs)
{
output = output.AddDays(increment);
if (!(output.Month == 2 && output.Day == 29)) //don't increment the days added if it is a leap year day
{
daysAdded++;
}
}
}
return output;
}
Might need some more testing, but without using the DateTime Add... functions or too much looping, a possible custom implementation:
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
int year = date.Year + daysToAdd / 365, month = date.Month - 1, dir = Math.Sign(daysToAdd);
daysToAdd = (daysToAdd % 365) + date.Day;
int[] months = {31,28,31,30,31,30,31,31,30,31,30,31};
while(daysToAdd > months[month] || daysToAdd < 0){
if(dir ==1) daysToAdd -= months[month];
month += dir;
if(month == 12 || month == -1){
year += dir;
month = dir == -1 ? 11 : 0;
}
if(dir ==-1) daysToAdd += months[month]; //for reverse direction, add previous month
}
return new DateTime(year, ++month,daysToAdd);
}

work out days in week across date range

I am trying to work out how many days are in each week across a date range. my week runs sat-sat.
I have a startdate, and a duration (and a week number - as actually I want to return the number of days in a particular week across the range)
edit: I will try to be a bit more clear. I do indeed want the days before the next saturday across a date range .. so yes when its a range that is long enough the second week will always be 7 days. however the last week will be the number of days remaining of the duration if the duration does not stretch until a saturday.. hopefully the answers below will shed some more light.. maybe not though.
week number is the week across the date range. nothing to do with where weeks fall in the year.
worth noting that the arrivalDate can be a saturday so if its a saturday start and 7 duration would need to return 7...
so basically if I have say
arrivalDate = 29/06/2014 (sunday)
weeknumber = 1
duration = 17
I need it to return 6
if the weeknumber is 2 it should return 7
if the weeknumber is 3 it should return 4
I have this so far, which I know is far from ideal - expecting a bit of recursion might work best but my brain isnt quite functioning as yet..
public static int DaysInWeek(DateTime arrivalDate, int weekNumber, int duration)
{
int ret =0;
List<int> arr = new List<int>();
int leftOver = 0;
for (int i = 1; i <= duration; i++)
{
if (arrivalDate.AddDays(i).DayOfWeek == DayOfWeek.Saturday)
{
int x = i;
arr.Add( x - arr.Sum() );
leftOver = duration - arr.Sum();
}
}
if (leftOver < 7 && leftOver > 0) arr.Add( leftOver );
if (arr.Count == 0) ret = duration;
else ret = arr[weekNumber - 1];
return ret;
}
thanks
Here is my solution based on CeejeeB's solution, that handles Saturday as first day and weekNumbers that are outside the timespan defined by duration.
public static int DaysInWeek(DateTime arrivalDate, int weekNumber, int duration)
{
// Handle a Saturday as start day
var daysInFirstWeek = arrivalDate.DayOfWeek == DayOfWeek.Saturday ? 7 : DayOfWeek.Saturday - arrivalDate.DayOfWeek;
// First week
if (weekNumber == 1) return Math.Min(duration, daysInFirstWeek);
// Other week
var start = daysInFirstWeek + ((weekNumber - 2) * 7);
return Math.Max(0, Math.Min(7, duration - start));
}
This should do what you want.
public static int DaysInWeek(DateTime arrivalDate, int weekNumber, int duration)
{
const int daysInAWeek = 7;
//get the value of Saturday (your week start day)
int firstDayOfWeekIndex = (int)DayOfWeek.Saturday;
//get the day of week of the first day
int startDay = arrivalDate.DayOfWeek;
//Find out number of says until next Saturday (days in first week)
int daysInFirstWeek = (startDay + firstDayOfWeekIndex) % daysInAWeek;
//Get the 'Full Weeks', that have all 7 days in the duration
int fullWeeks = (duration - daysInFirstWeek) / daysInAWeek;
//Get any leftover days
int leftover = duration - daysInAWeek * fullWeeks - daysInFirstWeek;
//Get total number of weeks (complete or otherwise)
int totalWeeks = 1 + fullWeeks + (leftover > 0 ? 1 : 0);
//return accordingly
if(weekNumber > totalWeeks)
return 0;
else if(weekNumber == 1)
return daysInFirstWeek;
else if(weekNumber == totalWeeks)
return daysInLastWeek;
else return daysInAWeek;
}
No list, no iteration, no recursion needed.
Hope this helps. Cheers.
My solution:
public static int DaysInWeekNew(DateTime arrivalDate, int weekNumber, int duration)
{
var offset = DayOfWeek.Saturday - arrivalDate.DayOfWeek;
//First Week
if (offset == 0) offset = 7; //if startdays is saturday
if (weekNumber == 1) return offset > duration ? duration : offset;
int numberofmiddleweeks = (duration - offset) / 7; //floor
//middleweeks
if (weekNumber - 1 <= numberofmiddleweeks) return 7;//-1 for the first week
//Last Week
return duration - offset - numberofmiddleweeks * 7;
}
EDIT: updated for startday saturday
This is a shorter version that satisfies your 3 test cases. No recursion needed. Basically the result is going to fall into 1 of 3 outcomes. The results are always going to be either part of the first week, the whole of a middle week or part of the last week.
This code deals with the three senarios.
public static int DaysInWeekNew(DateTime arrivalDate, int weekNumber, int duration)
{
var offset = arrivalDate.DayOfWeek == DayOfWeek.Saturday ? 7 : DayOfWeek.Saturday - arrivalDate.DayOfWeek;
//First Week
if (duration < offset) return duration;
if (weekNumber == 1) return offset;
//Middle Week
var start = offset + ((weekNumber - 2) * 7);
if (start + 7 < duration) return 7;
//Last Week
return duration - start;
}
UPDATED based on comments

How many days to add for "semi-monthly"

I have a enum type called PaymentFrequency whose values indicate how many payments per year are being made...
So I have
public enum PaymentFrequency
{
None = 0,
Annually = 1,
SemiAnnually = 2,
EveryFourthMonth = 3,
Quarterly = 4,
BiMonthly = 6,
Monthly = 12,
EveryFourthWeek = 13,
SemiMonthly = 24,
BiWeekly = 26,
Weekly = 52
}
Based on NumberOfPayments, PaymentFrequency, and FirstPaymentDate (of type DateTimeOffset) I want to calculate LastPaymentDate. But I am having issue figuring out how many time units (days, months) to add in case of SemiMonthly...
switch (paymentFrequency)
{
// add years...
case PaymentFrequency.Annually:
LastPaymentDate = FirstPaymentDate.AddYears(NumberOfPayments - 1);
break;
// add months...
case PaymentFrequency.SemiAnnually:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 6); // 6 months
break;
case PaymentFrequency.EveryFourthMonth:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 4); // 4 months
break;
case PaymentFrequency.Quarterly:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 3); // 3 months
break;
case PaymentFrequency.BiMonthly:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 2); // 2 months
break;
case PaymentFrequency.Monthly:
LastPaymentDate = FirstPaymentDate.AddMonths(NumberOfPayments - 1);
break;
// add days...
case PaymentFrequency.EveryFourthWeek:
LastPaymentDate = FirstPaymentDate.AddDays((NumberOfPayments - 1) * 4 * 7); // 4 weeks (1 week = 7 days)
break;
case PaymentFrequency.SemiMonthly:
// NOTE: how many days in semi month? AddMonths (0.5) does not work :)
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 0.5); // 2 weeks (1 week = 7 days)
break;
case PaymentFrequency.BiWeekly:
LastPaymentDate = FirstPaymentDate.AddDays((NumberOfPayments - 1) * 2 * 7); // 2 weeks (1 week = 7 days)
break;
case PaymentFrequency.Weekly:
LastPaymentDate = FirstPaymentDate.AddDays((NumberOfPayments - 1) * 7); // 1 week (1 week = 7 days)
break;
case PaymentFrequency.None:
default:
throw new ArgumentException("Payment frequency is not initialized to valid value!", "paymentFrequency");
}
So, how many days/months should I use when using SemiMonthly?
Is this even possible without knowing exact # of days for each month in between?
Or is this really simple, and I have just run out of caffeine and I am not seeing forest for the trees :)
For Semi-Monthly, if your first payment was always the 1st payment of the month as well (that is, anytime from the 1st to the 13th, starting after 13th is problematic as discussed in the comments), you could do as follows:
// assuming first payment will be 1st of month, add month for every 2 payments
// num payments / 2 (int division, remainder is chucked)
// then add 15 days if this is even payment of the month
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) / 2)
.AddDays((NumberOfPayments % 2) == 0 ? 15 : 0);
So for the 1st payment, this will add 0 months and 0 days so be 1st payment date. For 2nd payment, this will add 0 months (int dividision, remainder is chucked) and 15 days for 16th of month. For 3rd payment, this will add 1 month (1 / 3) and 0 days for 1st of next month, etc.
This is assuming that the FirstPaymentDate will be on the 1st of some given month. You can probably see where to go from here if you want to allow the 16th to be a starting date, etc.
Make sense?
So to illustrate, if we had:
DateTime LastPaymentDate, FirstPaymentDate = new DateTime(2011, 12, 5);
for(int numOfPayments=1; numOfPayments<=24; numOfPayments++)
{
LastPaymentDate = FirstPaymentDate.AddMonths((numOfPayments - 1) / 2)
.AddDays((numOfPayments % 2) == 0 ? 15 : 0);
Console.WriteLine(LastPaymentDate);
}
This loop would give us:
12/5/2011 12:00:00 AM
12/20/2011 12:00:00 AM
1/5/2012 12:00:00 AM
// etc...
10/20/2012 12:00:00 AM
11/5/2012 12:00:00 AM
11/20/2012 12:00:00 AM
Because months have varying lengths, you can't just add a pre-defined number. You have to know which month you are dealing with, and go from there.
If you know that the 1st and the 16th of a month are due dates, then the last payment is December 16th (assuming you are calculating for a calendar year).
The basic pairs for semi monthly payments are:
1 and 16 (the 1st and 16th day of a month)
15 and (2|3)? (the 15th and the last day of the month)
Peek and choose
I've recently had the same issue, but I needed to allow any date input. It's a bit of a mess and needs to be refactored, but this is what I came up with so far. February had some problems that I had to hack.
Date returnDate;
if (numberOfPayments % 2 == 0)
{
returnDate = date.AddMonths(numberOfPayments / 2);
if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))//Last day of the month adjustment
{
returnDate = new Date(returnDate.Year, returnDate.Month, DateTime.DaysInMonth(returnDate.Year, returnDate.Month));
}
}
else
{
returnDate = date.Day <= 15 ? date.AddDays(15).AddMonths((numberOfPayments - 1) / 2) : date.AddDays(-15).AddMonths((numberOfPayments + 1) / 2);
if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))//Last day of the month adjustment
{
returnDate = new Date(returnDate.Year, returnDate.Month, 15);
}
else if (date.Month == 2 && date.Day == 14)
{
returnDate = returnDate.AddMonths(-1);
returnDate = new Date(returnDate.Year, returnDate.Month, returnDate.Month == 2 ? 28 : 29);
}
else if (date.Month == 2 && date.Day == 15)
{
returnDate = returnDate.AddMonths(-1);
returnDate = new Date(returnDate.Year, returnDateMonth, DateTime.DaysInMonth(returnDate.Year, returnDate.Month));
}
}
return returnDate;

Date difference in years using C# [duplicate]

This question already has answers here:
How do I calculate someone's age based on a DateTime type birthday?
(74 answers)
Closed 6 years ago.
How can I calculate date difference between two dates in years?
For example: (Datetime.Now.Today() - 11/03/2007) in years.
I have written an implementation that properly works with dates exactly one year apart.
However, it does not gracefully handle negative timespans, unlike the other algorithm. It also doesn't use its own date arithmetic, instead relying upon the standard library for that.
So without further ado, here is the code:
DateTime zeroTime = new DateTime(1, 1, 1);
DateTime a = new DateTime(2007, 1, 1);
DateTime b = new DateTime(2008, 1, 1);
TimeSpan span = b - a;
// Because we start at year 1 for the Gregorian
// calendar, we must subtract a year here.
int years = (zeroTime + span).Year - 1;
// 1, where my other algorithm resulted in 0.
Console.WriteLine("Yrs elapsed: " + years);
Use:
int Years(DateTime start, DateTime end)
{
return (end.Year - start.Year - 1) +
(((end.Month > start.Month) ||
((end.Month == start.Month) && (end.Day >= start.Day))) ? 1 : 0);
}
We had to code a check to establish if the difference between two dates, a start and end date was greater than 2 years.
Thanks to the tips above it was done as follows:
DateTime StartDate = Convert.ToDateTime("01/01/2012");
DateTime EndDate = Convert.ToDateTime("01/01/2014");
DateTime TwoYears = StartDate.AddYears(2);
if EndDate > TwoYears .....
If you need it for knowing someone's age for trivial reasons then Timespan is OK but if you need for calculating superannuation, long term deposits or anything else for financial, scientific or legal purposes then I'm afraid Timespan won't be accurate enough because Timespan assumes that every year has the same number of days, same # of hours and same # of seconds).
In reality the length of some years will vary (for different reasons that are outside the scope of this answer). To get around Timespan's limitation then you can mimic what Excel does which is:
public int GetDifferenceInYears(DateTime startDate, DateTime endDate)
{
//Excel documentation says "COMPLETE calendar years in between dates"
int years = endDate.Year - startDate.Year;
if (startDate.Month == endDate.Month &&// if the start month and the end month are the same
endDate.Day < startDate.Day// AND the end day is less than the start day
|| endDate.Month < startDate.Month)// OR if the end month is less than the start month
{
years--;
}
return years;
}
var totalYears =
(DateTime.Today - new DateTime(2007, 03, 11)).TotalDays
/ 365.2425;
Average days from Wikipedia/Leap_year.
int Age = new DateTime((DateTime.Now - BirthDateTime).Ticks).Year;
To calculate the elapsed years (age), the result will be minus one.
var timeSpan = DateTime.Now - birthDateTime;
int age = new DateTime(timeSpan.Ticks).Year - 1;
Here is a neat trick which lets the system deal with leap years automagically. It gives an accurate answer for all date combinations.
DateTime dt1 = new DateTime(1987, 9, 23, 13, 12, 12, 0);
DateTime dt2 = new DateTime(2007, 6, 15, 16, 25, 46, 0);
DateTime tmp = dt1;
int years = -1;
while (tmp < dt2)
{
years++;
tmp = tmp.AddYears(1);
}
Console.WriteLine("{0}", years);
It's unclear how you want to handle fractional years, but perhaps like this:
DateTime now = DateTime.Now;
DateTime origin = new DateTime(2007, 11, 3);
int calendar_years = now.Year - origin.Year;
int whole_years = calendar_years - ((now.AddYears(-calendar_years) >= origin)? 0: 1);
int another_method = calendar_years - ((now.Month - origin.Month) * 32 >= origin.Day - now.Day)? 0: 1);
I implemented an extension method to get the number of years between two dates, rounded by whole months.
/// <summary>
/// Gets the total number of years between two dates, rounded to whole months.
/// Examples:
/// 2011-12-14, 2012-12-15 returns 1.
/// 2011-12-14, 2012-12-14 returns 1.
/// 2011-12-14, 2012-12-13 returns 0,9167.
/// </summary>
/// <param name="start">
/// Stardate of time period
/// </param>
/// <param name="end">
/// Enddate of time period
/// </param>
/// <returns>
/// Total Years between the two days
/// </returns>
public static double DifferenceTotalYears(this DateTime start, DateTime end)
{
// Get difference in total months.
int months = ((end.Year - start.Year) * 12) + (end.Month - start.Month);
// substract 1 month if end month is not completed
if (end.Day < start.Day)
{
months--;
}
double totalyears = months / 12d;
return totalyears;
}
public string GetAgeText(DateTime birthDate)
{
const double ApproxDaysPerMonth = 30.4375;
const double ApproxDaysPerYear = 365.25;
int iDays = (DateTime.Now - birthDate).Days;
int iYear = (int)(iDays / ApproxDaysPerYear);
iDays -= (int)(iYear * ApproxDaysPerYear);
int iMonths = (int)(iDays / ApproxDaysPerMonth);
iDays -= (int)(iMonths * ApproxDaysPerMonth);
return string.Format("{0} år, {1} måneder, {2} dage", iYear, iMonths, iDays);
}
I found this at TimeSpan for years, months and days:
DateTime target_dob = THE_DOB;
DateTime true_age = DateTime.MinValue + ((TimeSpan)(DateTime.Now - target_dob )); // Minimum value as 1/1/1
int yr = true_age.Year - 1;
If you're dealing with months and years you need something that knows how many days each month has and which years are leap years.
Enter the Gregorian Calendar (and other culture-specific Calendar implementations).
While Calendar doesn't provide methods to directly calculate the difference between two points in time, it does have methods such as
DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)
DateTime musteriDogum = new DateTime(dogumYil, dogumAy, dogumGun);
int additionalDays = ((DateTime.Now.Year - dogumYil) / 4); //Count of the years with 366 days
int extraDays = additionalDays + ((DateTime.Now.Year % 4 == 0 || musteriDogum.Year % 4 == 0) ? 1 : 0); //We add 1 if this year or year inserted has 366 days
int yearsOld = ((DateTime.Now - musteriDogum).Days - extraDays ) / 365; // Now we extract these extra days from total days and we can divide to 365
Works perfect:
internal static int GetDifferenceInYears(DateTime startDate)
{
int finalResult = 0;
const int DaysInYear = 365;
DateTime endDate = DateTime.Now;
TimeSpan timeSpan = endDate - startDate;
if (timeSpan.TotalDays > 365)
{
finalResult = (int)Math.Round((timeSpan.TotalDays / DaysInYear), MidpointRounding.ToEven);
}
return finalResult;
}
Simple solution:
public int getYearDiff(DateTime startDate, DateTime endDate){
int y = Year(endDate) - Year(startDate);
int startMonth = Month(startDate);
int endMonth = Month(endDate);
if (endMonth < startMonth)
return y - 1;
if (endMonth > startMonth)
return y;
return (Day(endDate) < Day(startDate) ? y - 1 : y);
}
This is the best code to calculate year and month difference:
DateTime firstDate = DateTime.Parse("1/31/2019");
DateTime secondDate = DateTime.Parse("2/1/2016");
int totalYears = firstDate.Year - secondDate.Year;
int totalMonths = 0;
if (firstDate.Month > secondDate.Month)
totalMonths = firstDate.Month - secondDate.Month;
else if (firstDate.Month < secondDate.Month)
{
totalYears -= 1;
int monthDifference = secondDate.Month - firstDate.Month;
totalMonths = 12 - monthDifference;
}
if ((firstDate.Day - secondDate.Day) == 30)
{
totalMonths += 1;
if (totalMonths % 12 == 0)
{
totalYears += 1;
totalMonths = 0;
}
}
Maybe this will be helpful for answering the question: Count of days in given year,
new DateTime(anyDate.Year, 12, 31).DayOfYear //will include leap years too
Regarding DateTime.DayOfYear Property.
The following is based off Dana's simple code which produces the correct answer in most cases. But it did not take in to account less than a year between dates. So here is the code that I use to produce consistent results:
public static int DateDiffYears(DateTime startDate, DateTime endDate)
{
var yr = endDate.Year - startDate.Year - 1 +
(endDate.Month >= startDate.Month && endDate.Day >= startDate.Day ? 1 : 0);
return yr < 0 ? 0 : yr;
}

Categories