Errornous calculation of year/month/day between two dates - c#

What I try to do is calculate the number of years, months and days between two dates.
Unfortunately, there is no method of the .NET Framework which can do this.
What I did is basically the following:
http://www.codeproject.com/Articles/28837/Calculating-Duration-Between-Two-Dates-in-Years-Mo
with a few adaptations from the comments of said website:
var monthDay = new[] { 31, 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
DayCalculation:
if (fromDate.Day > toDate.Day)
{
increment = monthDay[toDate.Month - 1];
}
if (increment == -1)
{
increment = DateTime.IsLeapYear(toDate.Year) ? 29 : 28;
}
So I have the following effect:
Date1: 1979-01-30
Date2: 2013-03-01
And the output is: 34 Years, 1 Month, -1 Day
The expected output is: 34 Years, 1 Month, 1 Day
This effect happens every time, the Date2 is a date in march.
Do you know what is wrong with this calculation?
Or do you know a better solution how to achieve the desired result?
Thanks in advance
PS: I know that you can calculate the amount of days between two dates, but what I need is the amount of finished years, the amount of finished months, and the amount of finished days

Unfortunately, there is no method of the .NET Framework which can do this.
True, but there is Noda Time instead :) (It's my port of Joda Time to .NET, although there are quite a few differences between the two projects.)
LocalDate start = new LocalDate(1979, 1, 30);
LocalDate end = new LocalDate(2013, 3, 1);
Period period = Period.Between(start, end);
Console.WriteLine("{0} years, {1} months, {2} days",
period.Years, period.Months, period.Days);
Output:
34 years, 1 months, 1 days

Here is a way to calculate the difference without using an external library. Two dates are required. I assume that the first date is no later than the second date. Otherwise you will have to swap them for the calculation to be correct.
var first = new DateTime(1979, 1, 30);
var second = new DateTime(2013, 3, 1);
Here is how to compute the difference. You do not need a table to get the number of days in the month. That information is provided by the DateTime.DaysInMonth function.
var years = second.Year - first.Year;
var months = second.Month - first.Month;
if (months < 0) {
months += 12;
years -= 1;
}
var days = second.Day - first.Day;
if (days < 0) {
var daysInFirstMonth = DateTime.DaysInMonth(first.Year, first.Month);
days += daysInFirstMonth;
months -= 1;
}
Printing the computed values
Console.WriteLine("{0} year(s), {1} month(s), {2} day(s)", years, months, days);
will result in
34 year(s), 1 month(s), 2 day(s)
This produces the same results as the code that you have linked to. However, you expect to get 1 day instead of 2 days. I guess it depends on how you define "days between". If you prefer to count only January 31 as the day between you can subtract 1 from days. Then there will be 0 days between two adjacent days and -1 days "between the same date".

Related

Create 75-Mins timeblock for multiple DateTimes of a day

I am working on a stock market software. Where I am having a candle every 5 minutes. So whenever a time-frame of say 30 minutes is selected, what we do is -
long val = (long)(D * 24 * 60) / 30; //D is datetime of candle converted in OA date in double.
//The above code never create problem as (24*60)%30 == 0.
The above line returns same value for every half an hour chunk i. e. candle of 10:35, 10:40.....11:00. With that we can easily find out chunks of half an hour, whenever val is changed.
Now, We have a challange to implement the chunk of 75-Mins in the same way. Our market start from 9:15 and ends at 3:30. Suppose date for which 75-Mins needs to be calculated is 22-9-2018. For that I will need to have exactly 5 candle of below time -
22-9-2018 10:30 (9:15 to 10:30 = 75 mins)
22-9-2018 11:45
22-9-2018 1:00
22-9-2018 2:15
22-9-2018 3:30
I need to have same kind of code as metioned above which will calculate same value for these five chunks.
Problem I found is, If we start 75 from 12:00, then the chunk in market time will be at 8:45 to 10:00 while we require from 9:15 to 10:30 first chunk.
Also, (24*60)%75 = 15, So 15 Mins difference everyday disturbs the next day calculation too.
UPDATE -
To clear the question, For a chunk from 10:35 to 11:45, I will have candles like 10:35, 10:40, 10:45..... 11:45. For all these datetimes, I need a same numeric return value. As soon as the candle of 11:50 comes, the returned numeric value will get changed and my new 75 Min chunk will start. It will give same value till 1:00.
You can use a loop or a linq query like this:
var startTime = new DateTime(2018, 09, 22, 9, 15, 0);
var times = Enumerable.Range(1, 5).Select(x => startTime.AddMinutes(x * 75)).ToList();
Example
Here is also another example about how to split a date range. In the following example, i included the start time also as part of the result:
IEnumerable<DateTime> Split(DateTime start, DateTime end, int minutes)
{
if (minutes <= 0)
throw new ArgumentException(
$"'{nameof(minutes)}' should be greater than 0.",
nameof(minutes));
var result = start;
while (result <= end)
{
yield return result;
result = result.AddMinutes(minutes);
}
}
And here is the usage:
var startTime = new DateTime(2018, 09, 22, 9, 15, 0);
var endTime = startTime.AddHours(7);
var times = Split(startTime, endTime, 75).ToList();

How to calculate amount of hours between two points in c#/Code review

Hello everyone I have a small programming problem which is probably a lot easier than i think. So I need to set the time to install Timespan opbject below to be 24 + time left to the next 4 pm. The below is C# pseudo code, it was written in notepad because at work I don't have an IDE, i also don't have much experience in programming using dates. i think my alghorithm will work but i guess there is a milion easier ways to do it. Please have a look:
//I need to make a timespan object which has 24 hours from current time + time left to the next 4pm
//The context is time to install, which user should see
Timespan TimeToInstall = new Timespan(23,59,59)
//Now I am taking the current time
Current = DateTime.Now
//Now I add the 24 hours to the current in order to create the next day date
Current.Add(TimeToInstall)
//Now creating the 4 PM on the next day
DateTime pm4 = new DateTime(Current.year,Current.month,Current.Day,16,0,0)
//Now checking if current is above or below 4 pm
if(Current.TimeOfDay < pm4){
TimeToInstall = TimeToInstall + (pm4 - Current)
}else if(Current.TimeOfDay > pm4){
pm4.AddDays(1)
TimeToInstall = TimeToInstall + (pm4 - Current)
}else {
//24 hours has passed and it is 4 pm so nothing to do here
}
TimeSpan can be negative. So just substract the TimeSpan for 4PM with current TimeOfDay, if you get negative value, add 24 hours.
var timeLeft = new TimeSpan(16, 0, 0) - DateTime.Now.TimeOfDay;
if (timeLeft.Ticks<0)
{
timeLeft = timeLeft.Add(new TimeSpan(24,0,0))
}
Based on your code:
DateTime now = DateTime.Now;
DateTime today4pmDateTime= new DateTime(now.Year, now.Month, now.Day, 16, 0, 0);
//Will hold the next 4pm DateTime.
DateTime next4pmDateTimeOccurrence = now.Hour >= 16 ? today4pmDateTime : today4pmDateTime.AddDays(1);
//From here you can do all the calculations you need
TimeSpan timeUntilNext4pm = next4pmDateTimeOccurrence - now;
The answer is really simple, I should have seened this earlier. The solution to these kind of problems is basically modular arithmetic. The client requirment was the popup to show 24+ time to next 4 pm (Don't ask i don't know) so if:
program runs at 13:00 then the clock should display 24 +3 = 27
when 16:00 it should be 24+24 which is 48
when 22:00 it shoould be 24 + 18 which is 42
Now I noticed that:
13 + 27 = 40
16 + 24 = 40
22 + 18 = 40
40 Modulo 24 = 16
So basically if I subtract the current time from 40 then I will be left with the difference:
40 - 13 = 27
40 - 16 = 24
40 - 22 = 18
So what I did is this:
TimeSpan TimeToInstall;
TimeSpan TimeGiven = new TimeSpan(23, 59, 59);
DateTime Now = DateTime.Now;
long TimeTo4 = (new TimeSpan(40, 0, 0).Ticks - Now.TimeOfDay.Ticks) + TimeGiven.Ticks;
TimeToInstall = TimeSpan.FromTicks(TimeTo4);
EDIT
The above was a trap
Corrected:
DateTime Now = DateTime.Now;
if (Now.Hour < 16)
{
long TimeTo4 = (new TimeSpan(40, 0, 0).Ticks - Now.TimeOfDay.Ticks);
TimeToInstall = TimeSpan.FromTicks(TimeTo4);
}
else
{
long TimeTo4 = (new TimeSpan(40, 0, 0).Ticks - Now.TimeOfDay.Ticks) + TimeGiven.Ticks;
TimeToInstall = TimeSpan.FromTicks(TimeTo4);
}

How to determine fortnight number from date

I have a niggling problem that I am having trouble conceptualising a solution.
I have a payroll system that has the following rules:
The Pay period relating to each pay period is 14 days long starting
on Monday and ending on Sunday
Payday is each fortnight on Thursday following the last day of the pay period.
Pay periods are numbered 1 through to 26 (or 27 in some years) with ONE being the first payday in the financial year and 26 (or 27) being the last in the financial year.
Our financial year runs July 1 to June 30
A known pay fortnight was started on 25/06/2012 and ended on 8/07/2012. The pay day for this period was 12/07/2012. So I do have a few start points.
From any date, I can calculate what its pay date is because I have a known start point. I can also calculate the financial year it relates to easily enough. There are many solutions to those problems on SO.
I now need to allow my users to enter a date and have the application show the pay period it relates to.
What I am struggling with is how can I determine which pay period (1 to 26/27) it is in the financial year? Any ideas?
Now your user enters a date, and you can calculate its pay date, right? Let "payDate" be this pay date, and what you want is "which fortnight of the financial year does payDate fall in, numbered starting with one", right?
Then, you first need to determine the beginning of the financial year:
DateTime yearBegin;
if (payDate.Month >= 7)
yearBegin = new DateTime(payDate.Year, 7, 1);
else
yearBegin = new DateTime(payDate.Year - 1, 7, 1);
With this you can calculate how many days are there between payDate and yearBegin:
int delta = (payDate - yearBegin).Days;
Finally you get the answer:
int period = (delta / 14) + 1;
I understood the first pay day from any given year starts at a different day to always match full periods of 14 days. If that is the case, you could try this code:
static DateTime InitialDate = new DateTime(2012, 06, 25);
private static int PayPeriod(DateTime inputDate)
{
int periodStartDay = (inputDate - InitialDate).Days % 14 + 1;
int periodStartMonth = 07;
int periodStartYear = inputDate.Month >= 7 ? inputDate.Year : inputDate.Year - 1;
DateTime periodStartDate = new DateTime(periodStartYear, periodStartMonth, periodStartDay);
int payPeriod = (inputDate - periodStartDate).Days / 14 + 1;
return payPeriod;
}

TimeSpan for different years subtracted from a bigger TimeSpan

The language I am using is C#.
I have the folowing dillema.
DateTime A, DateTime B. If A < B then I have to calculate the number of days per year in that timespan and multiply it by a coeficient that corresponds to that year.
My problem is the fact that it can span multiple years.
For example:
Nr of Days in TimeSpan for 2009 * coef for 2009 + Nr of Days in TimeSpan for 2010 * coef for 2010 + etc
You can't do this with a simple TimeSpan, basically. It doesn't know anything about when the span covers - it's just a number of ticks, really.
It sounds to me like there are two cases you need to consider:
A and B are in the same year. This is easy - just subtract one from the other and get the number of days from that
A and B are in different years. There are now either two or three cases:
The number of days after A in A's year. You can work this out by constructing January 1st in the following year, then subtracting A
The number of days in each year completely between A and B (so if A is in 2008 and B is in 2011, this would be 2009 and 2010)
The number of days in B's year. You can work this out by constructing December 31st in the previous year, then subtracting B. (Or possibly January 1st in B's year, depending on whether you want to count the day B is on or not.)
You can use DateTime.IsLeapYear to determine whether any particular year has 365 or 366 days in it. (I assume you're only using a Gregorian calendar, btw. All of this changes if not!)
Here is a little snippet of code that might help
var start = new DateTime(2009, 10, 12);
var end = new DateTime(2014, 12, 25);
var s = from j in Enumerable.Range(start.Year, end.Year + 1 - start.Year)
let _start = start.Year == j ? start : new DateTime(j, 1, 1)
let _end = end.Year == j ? end : new DateTime(j, 12, 31)
select new {
year = j,
days = Convert.ToInt32((_end - _start).TotalDays) + 1
};
If I understood your problem correctly, solving it using Inclusion Exclusion principle would be the best.
Say, if your start date is somewhere in 2008 and the end date is in 2010:
NDAYS(start, end) * coeff_2008
- NDAYS(2009, end) * coeff_2008 + NDAYS(2009, end) * coeff_2009
- NDAYS(2010, end) * coeff_2009 + NDAYS(2010, end) * coeff_2010
Where Ndays computes the number of dates in the interval (TotalDays plus one day).
There is no need to handle leap years specially or compute december 31st.
The details you can work out in a for-loop going over jan first of each year in the span.

Subtracting TimeSpan from date

I want to subtract a time-span from a date-time object using a 30-day month and ignoring leap years etc.
Date is 1983/5/1 13:0:0 (y/m/d-h:m:s)
Time span is 2/4/28-2:51:0 (y/m/d-h:m:s)
I can use DateTime and TimeSpan objects to do this, after converting years and months of the time-span to days (assuming a 30 day month and a ~364 day year).
new DateTime(1981,5,1,13,0,0).Subtract(new TimeSpan(878,13,51,0));
With this i get the result:
{12/4/1978 11:09:00 PM}
Above answer obviously doesn't ignore the factors i want ignored and gives me an accurate answer. But in this case that's not what i want so i wrote the below code.
public static CustomDateTime operator -(CustomDateTime DT1,CustomDateTime DT2)
{
CustomDateTime retVal = new CustomDateTime();
try
{
const int daysPerYear = 364.25;
const int monthsPerYear = 12;
const int daysPerMonth = 30;
const int hoursPerDay = 24;
const int minutesPerHour = 60;
retVal.Minute = DT1.Minute - DT2.Minute;
if (retVal.Minute < 0)
{
retVal.Minute += minutesPerHour;
DT1.Hour -= 1;
}
retVal.Hour = DT1.Hour - DT2.Hour;
if (retVal.Hour < 0)
{
retVal.Hour += hoursPerDay;
DT1.Day -= 1;
}
retVal.Day = DT1.Day - DT2.Day;
if (retVal.Day < 0)
{
retVal.Day += daysPerMonth;
DT1.Month -= 1;
}
retVal.Month = DT1.Month - DT2.Month;
if (retVal.Month < 0)
{
retVal.Month += monthsPerYear;
DT1.Year -= 1;
}
retVal.Year = DT1.Year - DT2.Year;
}
catch (Exception ex) { }
return retVal;
}
Then i get:
1981/0/3-10:9:0
This is pretty close to what i'm after except i shouldn't get 0 for month and year should be 1980. Any kind of help is appreciated.
Just to make things clear again; in this context I have to use a 30-day month and ignore leap-years, different numbers of months, etc. Its a weird thing to do, i know. So I'm pretty much after a 'wrong answer' as opposed to the exact answer given by the managed classes.
If you're estimating a month at 30 days, of course your math will be off. When you subtract 878 days from 5/1/1981, .Net is giving you the exact difference, not an estimate, and this difference accounts for leap years, if there are any. The error is not in the Subtract(...) method - it is in your own "manual" calculation.
DateTime dt = new DateTime(1981, 5, 1, 13, 0, 0);
TimeSpan t = new TimeSpan(878, 13, 51, 0);
dt.Ticks
624931668000000000
t.Ticks
759090600000000
dt.Ticks - t.Ticks
624172577400000000
new DateTime(dt2)
{12/4/1978 11:09:00 PM}
Date: {12/4/1978 12:00:00 AM}
Day: 4
DayOfWeek: Monday
DayOfYear: 338
Hour: 23
Kind: Unspecified
Millisecond: 0
Minute: 9
Month: 12
Second: 0
Ticks: 624172577400000000
TimeOfDay: {23:09:00}
Year: 1978
These are the total ticks since the epoch. Do this math, then convert back into a datetime.
Also: correct your math. 878 days is 2 years and 148 days. 5/1/1981 is the 121st day of the year, so subtract 120 to get Jan 1, 1979. This leaves 28 days. Start counting backwards from the end of 1978, and you get very close to the .Net answer. Your own answer isn't anywhere close.
EDIT based on feedback
// zh-Hans is a chinese culture
CultureInfo ci = CultureInfo.GetCultureInfo("zh-Hans");
DateTime dt = new DateTime(1981, 5, 1, 13, 0, 0, ci.Calendar);
TimeSpan t = new TimeSpan(878, 13, 51, 0);
Please note that you are still subtracting 878 days. The length of a month would be irrelevant in that case based on the Julian calendar. You will probably need to find the correct culture code for your particular calendar, then try this. However, with this calendar, I still arrive at the same answer above.
Beyond doing this, I am unsure how else to do the math. If you can provide a link to how you are doing it by hand, I can help code it for you.
EDIT 2
I understand now. Try this:
DateTime dt = new DateTime(1981, 5, 1, 13, 0, 0, ci.Calendar);
int years = 878 / 365;
int remainingDays = 878 % 365;
int months = remainingDays / 30;
remainingDays = remainingDays % 30;
TimeSpan t = new TimeSpan(years * 365 + months * 30 + remainingDays);
DateTime newdate = dt.Subtract(t);
You cannot assume a 30-day month. You are specifying that you want to subtract 878 days. The managed classes (I'm assuming you mean managed when you say native) are designed to factor in leap-years, different numbers of months, etc.
Using the managed classes will not give you a 0 for a month.

Categories