Counting groups of people in age ranges - c#

I have a little method to get the number of members in a certain age range. The range is supposed to be inclusive in both ends, i.e. if I call CountSelection(memberList, 16, 19) (where memberList is a List<Member>), I expect to get the number of members aged 16, 17, 18 and 19 summed together:
private int CountSelection(List<Member> members, int minAge, int maxAge)
{
DateTime from = DateTime.Now.AddYears(minAge * -1);
DateTime to = DateTime.Now.AddYears(maxAge * -1);
return members.Count(m =>
m.DateBorn.Date <= from.Date &&
m.DateBorn.Date >= to.Date);
}
However, my method is not reliable - sometimes it will omit members, I'm guessing when birth dates fall between ranges. In the main method, I'm calling CountSelection() several times, each with different ranges, theoretically covering all ages.
What should the query look like to guarantee that all the members will be counted?

I found out why my method was failing. I was just subtracting whole years from the from and to dates, and that resulted in ranges that looked like this (date format dd.mm.yyyy):
0-5 years - 11.04.2014-11.04.2019
6-12 years - 11.04.2007-11.04.2013
13-19 years - 11.04.2000-11.04.2006
20-26 years - 11.04.1993-11.04.1999
... and so on.
Notice the gap of almost a year between each range.
Solution:
Instead of setting the from-date like this:
DateTime from = DateTime.Now.Date.AddYears(-maxAge);
I of course have to subtract a further 1 year and add 1 day:
DateTime from = DateTime.Now.Date.AddYears(-maxAge + 1).AddDays(1);
Now the ranges look like this:
0-5 years - 12.04.2013-11.04.2019
6-12 years - 12.04.2006-11.04.2013
13-19 years - 12.04.1999-11.04.2006
20-26 years - 12.04.1992-11.04.1999
... and so on.
The final, working method looks like this:
private int CountSelection(List<Member> members, int minAge, int maxAge)
{
DateTime from = compareDate.AddYears(-maxAge+1).AddDays(1);
DateTime to = compareDate.AddYears(-minAge);
return members.Count(m =>
m.DateBorn >= from &&
m.DateBorn <= to);
}

It is easier to work with ranges with exclusive upper bounds:
private int CountSelection(List<Member> members, int minAge, int maxAgeExclusive)
{
DateTime from = compareDate.AddYears(-maxAgeExclusive);
DateTime to = compareDate.AddYears(-minAge);
return members.Count(m => m.DateBorn > from && m.DateBorn <= to);
}
Now your ranges will be mathematically more consistent.
0-6 years
6-13 years
13-20 years
20-27 years
etc
Then you can subtract one from the upper limit at the presentation layer.

Related

Calculate exact no of week for date range

A person is engaged in different works in different time duration which will start from Monday and end on friday as follows.Monday to Friday will be considered as 1 week.Any overlapping weeks be considered as 1 week.
Below are the scenario
"AssignedEngagementdate":[
{"Startdate":"01/03/2022","Enddate":"01/07/2022"},
{"Startdate":"01/10/2022","Enddate":"01/14/2022"},
{"Startdate":"01/10/2022","Enddate":"01/21/2022"},
{"Startdate":"02/14/2022","Enddate":"02/18/2022"}
]
Here I need to find the no of weeks assigned by this person and it should be 4 since one week is from 10th Jan to 14 Feb is overlapping in 2 engagement.
How I can do this in C# using linq. I was trying to fetch min start date and max end date from list and find the difference and converting in no of weeks but it has not given the actual result since date assigned is not consistent.
The trick is to convert each date range into an enumerated week range, or at least a list of distinct dates (such as Sundays) corresponding to each week or partial week. SelectMany() lets you gather them all together, after which a distinct count gives you an answer.
Try something like:
int numberOfWeeks = AssignedEngagementdate
.SelectMany(a => {
DateTime firstSunday = a.Startdate.AddDays(-(int)a.Startdate.DayOfWeek);
DateTime lastSunday = a.Enddate.AddDays(-(int)a.Enddate.DayOfWeek); // May be same as firstSunday
int weeks = (lastSunday - firstSunday).Days / 7 + 1;
// Enumerate one Sunday per week
return Enumerable.Range(0, weeks).Select(i => firstSunday.AddDays(7 * i));
})
.Distinct()
.Count();
You may still need to consider and test cases where assignments start or end on days other than Monday and Friday. The above should handle most such cases.
Without linq, you can try this Datetime extension :
public static int NumberOfWeeksBetween(this DateTime start, DateTime end)
{
TimeSpan span = end.Subtract(start);
if (span.Days <= 7)
{
if (start.DayOfWeek > end.DayOfWeek) return 2;
else return 1;
}
int days = span.Days - 7 + (int)start.DayOfWeek;
int weekCount = 1;
int dayCount = 0;
for (weekCount = 1; dayCount < days; weekCount++) dayCount += 7;
return weekCount;
}

Add decimal number to Date - C#

How to convert a decimal number (e.g. 2.5) to year and month (2 years and 6 months) and add it to a given date? I tried DateTime.TryParse and it didn't work.
If you are using it for years then multiply the float you have by 12. 2.5 becomes 30months. Then use the addmonths function. If I enter 5 then it will add 60 months which is 5 years
Usually you could just add a TimeSpan or use one of the Add methods, like this:
decimal yearsToAdd = (decimal)2.5;
int years = (int)Math.Floor(yearsToAdd);
decimal months = yearsToAdd - years;
int actualMonths = (int) Math.Floor(months * 12); // or Ceiling or Round
DateTime x = DateTime.Now.AddYears(years).AddMonths(actualMonths);
The problem is, that when you decimal doesn't yield an exacat number of months, how would you know how long e.g. half a month is?
28.0 / 2, 29.0 / 2, 30.0 / 2 or 31.0 / 2?
Would you take the length of the month you started with or one of the possible two months you end up with?
If you init date is dt than
dt = dt.AddMonths((int)(2.5*12));
decimal x =(decimal)2.5;
int nbYear = Convert.ToInt16(x);
var y = x - Math.Truncate(x);
int nbMonth =Convert.ToInt16 (y*12);
// MessageBox .Show (string.Format (" {0} years and {1} months ",nbYear ,nbMonth ));
DateTime dat=DateTime .Now ; // or given date
DateTime dat2 = dat.AddYears(nbYear).AddMonths(nbMonth);
If month is your smallest unit then the solution is, as pointed by many, to multiply number by 12. A more accurate alternative would be to use ticks.
decimal years=3.14592M; // No idea where this came from.
long ticks = (long)(356.0M * (decimal)TimeSpan.TicksPerDay * years);
DateTime futureDate=DateTime.Today.AddTicks(ticks);
Note that solution will not compensate for leap years. It is not difficult to extend it - you need to calculate number of leap years in the period and use average instead of 356.0M to calculate ticks per year (i.e. avg. number of days per year * ticks per day).

subtract list first element with second one

Consider that i have list dtArray1 that consist of DateTime items I mean 10.10.2010, 10.10.2015 and so on.
Could u say me how find subract of this element ?
How i can get diffeerence between 2 years?
for (Int32 index = 1; index < dtArray1.Count;index++)
{
if (dtArray1[index] >= dtArray1[index - 1])
{
dtArray1[index].Subtract(dtArray1[index - 1]);
Console.WriteLine(dtArray1[index]);
Console.ReadLine();
}
}
If your dates are of the DateTime type, you can just substract one from the other, like so:
var span = dtArray1[index] - dtArray1[index - 1];
Console.WriteLine(span.Days); // prints "1836"
The result will be of type TimeSpan. TimeSpan does not have a Years property, so you'll have to calculate the number of years yourself, keeping in mind leap years and whatnot. See How do I calculate someone's age in C#?

Periodicity of events 2 [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
My previous question got a lot cons. I will try again, with a code and why it not work.
For example, we have event that will go every 3 month. Event have DateStart, DataEnd and Periodicity.
For example, we have a record:
start = 02/23/2012 22:00:00;
end = 12/31/2012 23:30:00;
periodicity = 3;
Method must return true when current month = February, May, August, November.
Code:
public bool MonthPeriodicityChecker (DateTime start, DateTime end, DateTime dateToCheck, int periodicity)
{
var monthDiff = dateToCheck.Month - startDate.Month;
return (monthDiff-1) % periodicity == 0;
}
This code return true if month in dateToCheck equals April, July, October. It's skip first month.
UPDATE:
Damn, sorry to all. In the beyond, I have loop that sumulated dates. And this loop start with 1 and add this 1 to start. And next date was 24 February. Therefore, the February not prints( there are checks for number(23) of month too) in my program. Sorry and thanks.
If you want this to trigger in February, May, August and November, you shouldn't be subtracting one from the difference. Just use:
return (monthDiff % periodicity) == 0;
When the months are equal (February), 0 % 3 == 0. When you're a multiple of three months out (such as August), 6 % 3 == 0.
And I'm not sure about C# but some languages don't act as you may expect when taking the modulus of a negative number, so I'd be doubly safe with:
public bool MonthPeriodicityChecker (DateTime start, DateTime end,
DateTime dateToCheck, int periodicity)
{
var monthDiff = dateToCheck.Month - startDate.Month + 12; // make positive
return (monthDiff % periodicity) == 0;
}
However, keep in mind that, because you're only using the month of the year in your calculations, this probably won't work as expected if you run more than 12 months and your periodicity is not something that divides into 12 cleanly.
Example with starting December 2010 for every five months.
December 2010 is okay since the difference is zero: 0 % 5 == 0.
May 2011 is okay since the difference between May and December is five months: 5 % 5 == 0.
October 2011 is okay since the difference between October and December is ten months: 10 % 5 == 0.
March 2012 is not okay. The difference between March and December is three months, not fifteen as has happened in reality: 3 % 5 == 3.
The upshot of that is that you'll fire in May, October and December in every year.
You can fix that little problem (if it is a problem for you) by ensuring the years are taken into account, something like:
public bool MonthPeriodicityChecker (DateTime start, DateTime end,
DateTime dateToCheck, int periodicity)
{
// assuming Month returns 1-12 here which is the case for C# I think.
var monthDiff = (dateToCheck.Year * 12 + dateToCheck.Month - 1)
- (startDate.Year * 12 + startDate.Month - 1);
return (monthDiff % periodicity) == 0;
}
That also gets rid of the need for adding 12 to get a positive number since the advancing years will ensure that (a jump from December to January will now give you 1 rather than -11), assuming that dateToCheck.Month will always be greater than or equal to startDate.Month.
If there's the possibility that dateToCheck may be less startDate, you probably want to check that first and return false as the first step in the above function.
You can do this by generating all of the dates in the range, then see if any of them fall in the same month as dateToCheck. First, get the months:
private IEnumerable<DateTime> GetDatesInRange(DateTime start, DateTime end, int periodicity)
{
var current = start;
do
{
yield return current;
current += TimeSpan.FromMonths(periodicity);
}
while(current <= end)
}
Then, see if any are in the same month:
public bool MonthPeriodicityChecker(DateTime start, DateTime end, DateTime dateToCheck, int periodicity)
{
return GetDatesInRange(start, end, periodicity).Any(date => date.Month == dateToCheck.Month);
}
Your month subtraction is not good. What happens if you change from December to January?
How about this approach:
public bool monthPeriodicityChecker(DateTime start, DateTime end, dateToCheck, periodicity)
{
if ((dateToCheck < start) || (dateToCheck > start))
return false;
int monthDiff = 0;
while (startDate.AddMonths(1) < dateToCheck)
{
monthDiff++
// i'm sure there is a speedier way to calculate the month difference, but this should do for the purpose of this example
}
return (monthDiff) % periodicity == 0;
};

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