I need to calculate the number of days between two dates (DateTime) but with a twist. I want to know how many days fall into each of the months that the two days span. Is there an easy way two do it?
Example:
I have start date 30/03/2011 and end date 05/04/2011 then the result should be something like:
var result = new Dictionary<DateTime, int>
{
{ new DateTime(2011, 3, 1), 2 },
{ new DateTime(2011, 4, 1), 5 }
};
You could try something like this:
using System;
using System.Collections.Generic;
static class Program {
// return dictionary tuple<year,month> -> number of days
static Dictionary<Tuple<int, int>, int> GetNumberOfDays(DateTime start, DateTime end) {
// assumes end > start
Dictionary<Tuple<int, int>, int> ret = new Dictionary<Tuple<int, int>, int>();
DateTime date = end;
while (date > start) {
if (date.Year == start.Year && date.Month == start.Month) {
ret.Add(
Tuple.Create<int, int>(date.Year, date.Month),
(date - start).Days + 1);
break;
} else {
ret.Add(
Tuple.Create<int, int>(date.Year, date.Month),
date.Day);
date = new DateTime(date.Year, date.Month, 1).AddDays(-1);
}
}
return ret;
}
static void Main(params string[] args) {
var days = GetNumberOfDays(new DateTime(2011, 3, 1), new DateTime(2011, 4, 1));
foreach (var m in days.Keys) {
Console.WriteLine("{0}/{1} : {2} days", m.Item1, m.Item2, days[m]);
}
}
}
You can use the class Month of the Time Period Library for .NET:
// ----------------------------------------------------------------------
public Dictionary<DateTime,int> CountMonthDays( DateTime start, DateTime end )
{
Dictionary<DateTime,int> monthDays = new Dictionary<DateTime, int>();
Month startMonth = new Month( start );
Month endMonth = new Month( end );
if ( startMonth.Equals( endMonth ) )
{
monthDays.Add( startMonth.Start, end.Subtract( start ).Days );
return monthDays;
}
Month month = startMonth;
while ( month.Start < endMonth.End )
{
if ( month.Equals( startMonth ) )
{
monthDays.Add( month.Start, month.DaysInMonth - start.Day + 1 );
}
else if ( month.Equals( endMonth ) )
{
monthDays.Add( month.Start, end.Day );
}
else
{
monthDays.Add( month.Start, month.DaysInMonth );
}
month = month.GetNextMonth();
}
return monthDays;
} // CountMonthDays
Usage:
// ----------------------------------------------------------------------
public void CountDaysByMonthSample()
{
DateTime start = new DateTime( 2011, 3, 30 );
DateTime end = new DateTime( 2011, 4, 5 );
Dictionary<DateTime, int> monthDays = CountMonthDays( start, end );
foreach ( KeyValuePair<DateTime, int> monthDay in monthDays )
{
Console.WriteLine( "month {0:d}, days {1}", monthDay.Key, monthDay.Value );
}
// > month 01.03.2011, days 2
// > month 01.04.2011, days 5
} // CountDaysByMonthSample
Simple yes, fast no:
DateTime StartDate = new DateTime(2011, 3, 30);
DateTime EndDate = new DateTime(2011, 4, 5);
int[] DaysPerMonth = new int[12];
while (EndDate > StartDate)
{
DaysPerMonth[StartDate.Month]++;
StartDate = StartDate.AddDays(1);
}
Here's my solution. I did a quick check and it seems to work... let me know if there are any problems:
public Dictionary<DateTime, int> GetMontsBetween(DateTime startDate, DateTime EndDate)
{
Dictionary<DateTime, int> rtnValues = new Dictionary<DateTime, int>();
DateTime startMonth = new DateTime(startDate.Year, startDate.Month, 1);
DateTime endMonth = new DateTime(EndDate.Year, EndDate.Month, 1);
//some checking
if (startDate >= EndDate)
{
rtnValues.Add(startMonth, 0); // Or return null;
}
else if (startDate.Month == EndDate.Month && startDate.Year == EndDate.Year)
{
rtnValues.Add(startMonth, EndDate.Day - startDate.Day);
}
else
{
//Add first month remaining days
rtnValues.Add(startMonth, DateTime.DaysInMonth(startDate.Year, startDate.Month) - startDate.Day);
//Add All months days inbetween
for (DateTime st = startMonth.AddMonths(1); st < endMonth; st = st.AddMonths(1))
{
rtnValues.Add(new DateTime(st.Year, st.Month, 1), DateTime.DaysInMonth(st.Year, st.Month) );
}
//Add last month days
rtnValues.Add(new DateTime(EndDate.Year, EndDate.Month, 1), EndDate.Day);
}
return rtnValues;
}
Little example of how we can accurately get the total months and days between 2 dates using the built-in DateTime.DaysInMonth method which gives us the number of days in each month so we can get 100% accuracy.
DateTime date1 = DateTime.Now.AddDays(60);
DateTime date2 = DateTime.Now;
TimeSpan ts = date1 - date2;
int totalDays = int.Parse(ts.TotalDays.ToString("0"));
int totalMonths = Math.Abs((date1.Month - date2.Month) + 12 * (date1.Year - date2.Year));
int months = 0;
int days = 0;
int totalDaysInMonths = 0;
for (int i = totalMonths; i > 0; i--)
{
int month = date2.Month + i;
int year = date1.Year;
if (month > 12)
{
year++;
int newMonth = month - 12;
month = newMonth;
}
totalDaysInMonths = totalDaysInMonths + DateTime.DaysInMonth(year, month);
}
if (totalDays > totalDaysInMonths)
{
months = totalMonths - 1;
days = totalDays - totalDaysInMonths;
}
else if (totalDays < totalDaysInMonths)
{
months = totalMonths - 1;
int tempTotalDaysInMonths = 0;
for (int i = months; i > 0; i--)
{
int month = date2.Month + i;
int year = date1.Year;
if (month > 12)
{
year++;
int newMonth = month - 12;
month = newMonth;
}
tempTotalDaysInMonths = tempTotalDaysInMonths + DateTime.DaysInMonth(year, month);
}
days = totalDays - tempTotalDaysInMonths;
}
else
{
months = totalMonths;
}
return string.Format("{0} months and {1} days", months, days);
A very quick and dirty run at it using linqpad:
DateTime start = DateTime.Parse("03/30/2011");
DateTime end = new DateTime(2011,04,05,23,59,59);
var startNextMonthFirstDay = new DateTime(start.Year, start.Month+1, 1);
var diffForStartMonth = (startNextMonthFirstDay - start);
var totalDiff = (end-start);
var diff = Math.Round(totalDiff.TotalDays);
var diffForEndMonth = diff - diffForStartMonth.Days;
Dictionary<DateTime, int> result = new Dictionary<DateTime, int>();
result.Add(new DateTime(start.Year, start.Month, 1), diffForStartMonth.Days);
result.Add(new DateTime(end.Year, end.Month, 1), (int)diffForEndMonth);
//Dictionary<DateTime,int>{{new DateTime(2011,3,1),2},{new DateTime(2011,4,1),5}}
result.Dump();
DateTime dt1 = new DateTime(2011, 12, 12);
DateTime dt2 = new DateTime(2011, 06, 12);
TimeSpan ts = dt1.Subtract(dt2);
String s = ts.Days.ToString();
MessageBox.Show(s);
Related
e.g. 70,105 - calculate any date of birth that meets the age range of the parameters
CalculateDob(int youngestAge, int oldestAge)
{
Random r = new Random();
int age = 0;
age = r.Next(70, 105);
var year = DateTime.Now.Year - age;
var month = r.Next(1, 12);
var day = r.Next(1, 28);
return new DateTime(year, month, day);
}
My current soluton almost works, but fails in some edge cases i.e. returns 69 in some circumstances due what i presume a month issue.
Any suggestions?
Reason you are getting 69 years of age sometimes is because if CalculateDob returns month which is after current month (DateTime.Now); then Dob still wouldn't reach 70 years. Also, you should bring random constructor out from the method, and make it static so that you don't keep seeding rand generator during every call.
public static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
var birthDate = CalculateDob(70, 105);
var now = DateTime.Now;
int age = now.Year - birthDate.Year;
if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day))
{
age--;
}
Console.WriteLine(age);
}
}
//Construct this as static so that we don't keep seeding the rand
private static readonly Random _random = new Random();
static DateTime CalculateDob(int youngestAge, int oldestAge)
{
int age = 0;
age = _random.Next(70, 105);
var today = DateTime.Now;
var year = today.Year - age;
var month = _random.Next(1, 12);
//Age might less than youngest age,
//if born at/after current month, edge condition
if (month >= today.Month)
{
month = today.Month - 1;
if (month < 1)
{
year--;
month = 12;
}
}
var day = _random.Next(1, DateTime.DaysInMonth(year, month));
return new DateTime(year, month, day);
}
Instead of specifying
"string[] week_Days = {"Monday", "Tuesday"...};"
How to dynamically set days as array?
I tried solving it, but couldn't get starting day as "Sunday" -
DateTime days = DateTime.Now;
string[] weekDays = new string[7];
for (int i = 0; i < weekDays.Length; i++)
{
weekDays[i] = string.Format("{0:dddd}", days.AddDays(i));
Console.WriteLine(weekDays[i]);
days = DateTime.Now;
}
Output -
Wednesday
Thursday
Friday
Saturday
Sunday
Monday
Tuesday
using System;
using System.Globalization;
static void Main()
{
string[] weekDays = new CultureInfo("en-us").DateTimeFormat.DayNames;
for (int i = 1; i <= 7; i++)
Console.WriteLine(weekDays[i % 7]);
}
You could use the DayOfWeek enum:
string[] weekdays = Enum.GetNames(typeof(DayOfWeek));
EDIT: If you need to change the start day of the week:
private static string[] GetWeekdays(DayOfWeek firstDayOfWeek)
{
string[] weekdays = new string[7];
DateTime dateTime = DateTime.Now;
while (dateTime.DayOfWeek != firstDayOfWeek)
{
// Find the next date with start day of week
dateTime = dateTime.AddDays(1);
}
for (int i = 0; i < 7; i++)
{
// Get day of week of current day, add 1 day, iterate 7 times.
weekdays[i] = dateTime.DayOfWeek.ToString();
dateTime = dateTime.AddDays(1);
}
return weekdays;
}
You can use this LINQ query. The only challenge is to get the correct order:
DayOfWeek firstWeekDay = DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek;
string[] weekDays = Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>()
.OrderBy(dow => dow < firstWeekDay ? 7-(int)dow : dow-firstWeekDay)
.Select(dow => dow.ToString())
.ToArray();
Maybe someone has a more elegant way than this OrderBy.
string[] week_Days = new string[7];
DayOfWeek day = DayOfWeek.Sunday;
for (int i = 0; i < week_Days.Length; i++)
{
week_Days[i] = day.ToString();
Console.WriteLine(week_Days[i]);
day++;
}
Instead of assigning DateTime property, Use DayOfWeek. it solves the issue with ease.
Your code fails because DateTime.Now isn't always on a sunday:
DateTime days = DateTime.Now;
Better use the existing DayOfWeek enum:
string[] weekdays = Enum.GetNames(typeof(DayOfWeek));
This could be done in this way too. Until you get "Monday", add days to the DateTime.Now.
int j = 0;
string[] weekDays = new string[7];
DateTime days = DateTime.Now;
while (weekDays[0] != "Sunday")
{
days = days.AddDays(j++);
weekDays[0] = string.Format("{0:dddd}", days);
if (weekDays[0] != "Sunday")
days = DateTime.Now;
}
for (int i = 0; i < weekDays.Length; i++)
{
weekDays[i] = string.Format("{0:dddd}", days.AddDays(i));
Console.WriteLine(weekDays[i]);
}
I have a dataset that I need to remove all records that are outside of the current financial year.
I have a function that returns a boolean that suggests if the record will be deleted or not, for some reason it is telling me to delete records that are inside the current financial year... Maybe a logic issue?
The Function:
public static Boolean GetCurrentFinancialYear(DateTime CompareDate)
{
TimeSpan diff = DateTime.Now - CompareDate;
double Years = diff.TotalDays / 365.25;
double Months = diff.TotalDays / 12;
Boolean DeleteRecord = false;
var CurrentYear = DateTime.Today.Year;
var CurrentMonth = DateTime.Today.Month;
if(CompareDate.Month == 10 && CompareDate.Year == 2017)
{
var something = "nothing";
}
if (Months >= 13)
{
/// This is over a year old, safe bet its last finacial year or earlier
///
DeleteRecord = true;
}
else
{
var CurrentEndDate = new DateTime(DateTime.Now.Year, 7, 1);
var CurrentStartDate = new DateTime(DateTime.Now.Year - 1, 6, 30);
TimeSpan Enddiff = CurrentEndDate - CompareDate;
TimeSpan Startdiff = CurrentStartDate - CompareDate;
double EndMonths = Enddiff.TotalDays / 12;
double StartMonths = Startdiff.TotalDays / 12;
if (EndMonths < 12 && StartMonths > 0)
{
DeleteRecord = false;
}
else
{
DeleteRecord = true;
}
}
return DeleteRecord;
}
Found an extremely simple and elegant solution
public static Boolean GetCurrentFinancialYear(DateTime CompareDate)
{
Boolean DeleteRecord = false;
var CurrentEndDate = new DateTime(DateTime.Now.Year, 7, 1);
var CurrentStartDate = new DateTime(DateTime.Now.Year - 1, 6, 30);
if (CompareDate >= CurrentStartDate && CompareDate < CurrentEndDate)
{
DeleteRecord = false;
}
else
{
DeleteRecord = true;
}
return DeleteRecord;
}
I made a <List>DateTime class which named GetDataRange so that users can get days between two specific days.
Then I want to show days in the columns of data grid view. I use a foreach loop, This loop will cause a problem. The problem is it won't show all days in the columns because I want to show all days in other calendar. see the codes :
for (int i = 0; i < dtEnd.Subtract(dtStart).Days; i++)
{
TimeSpan counter = new TimeSpan(i, 0, 0, 0);
string s1 = "10/9/2012";
string s2 = "11/10/2012";
d1 = Convert.ToDateTime(s1);
d2 = Convert.ToDateTime(s2);
foreach (DateTime item in GetDateRange(d1, d2))
{
s = item.ToShortDateString();
}
PersianCalendar p = new PersianCalendar();
DateTime date = Convert.ToDateTime(s);
int day = p.GetDayOfMonth(date);
int month = p.GetMonth(date);
int year = p.GetYear(date);
string dt = string.Format("{0}/{1}/{2}", year, month, day);
dataGridView1.Columns.Add(string.Format("col{0}", i), string.Format("{0} {1}", (dtStart + counter).DayOfWeek.ToString(), dt));
}
It will show only one day because of that foreach loop. DayOfWeek is working perfectly but the problem is showing the days in foreach loop it will only show one day.
UPDATE
string dt only show one day it won't show other days.
You can't do that. because every time in the loop you are overriding the value. can you show the dates in a new row.
Try this :
for (int i = 0; i < dtEnd.Subtract(dtStart).Days; i++)
{
TimeSpan counter = new TimeSpan(i, 0, 0, 0);
dataGridView1.Columns.Add(string.Format("col{0}", i), string.Format("{0}", (dtStart + counter).DayOfWeek.ToString()));
}
Then add new row for that :
d1 = dtStart;
d2 = dtEnd;
foreach (DateTime item in GetDateRange(d1, d2))
{
int day = p.GetDayOfMonth(item);
int month = p.GetMonth(item);
int year = p.GetYear(item);
string dt1 = string.Format("{0}/{1}/{2}", year, month, day);
listBox1.Items.Add(dt1);
}
dataGridView1.Rows.Add();
for (int i = 0; i < dataGridView1.Columns.Count; i++)
{
try
{
dataGridView1.Rows[0].Cells[i].Value = listBox1.Items[i];
}
catch (Exception)
{
}
}
In a month i want to know mondays to sataurdays for the current month eg: in oct month 2011 there are
3-oct-2011 to 8-oct-2011,
10-OCt-11 to 15-Oct-11,
17-Oct-11 to 22-oct-2011,
24-Oct-2011 to 29-Oct-2011
all these sequence of days All these days like 3-oct-2011,4-oct-2011 ....29-oct-11 etc need to get in the array format or in the datatable.
var today = DateTime.Today;
var daysInMonth = DateTime.DaysInMonth(today.Year, today.Month);
var dates = Enumerable.Range(1, daysInMonth)
.Select(n => new DateTime(today.Year, today.Month, n))
.Where(date => date.DayOfWeek != DayOfWeek.Sunday)
.ToArray();
This will look at the number of days in the current month, create a DateTime object for each, then only return those dates which are not a Sunday as an array.
var today = DateTime.Today;
var daysInMonth = DateTime.DaysInMonth(today.Year, today.Month);
var dates = Enumerable.Range(1, daysInMonth)
.Select(n => new DateTime(today.Year, today.Month, n))
.Where(date => date.DayOfWeek != DayOfWeek.Sunday)
.SkipWhile(date => date.DayOfWeek != DayOfWeek.Monday)
.TakeWhile(date => date.DayOfWeek != DayOfWeek.Monday || (date.DayOfWeek == DayOfWeek.Monday && daysInMonth - date.Day > 7))
.ToArray();
This will do the same, except get rid of any Monday -> Saturday ranges which are not in the current month. (Week started in the previous month, or ends in the next).
Edit:
Here is a .NET 2 solution which will do the same thing as my previously posted LINQ solution.
DateTime today = DateTime.Today;
int daysInMonth = DateTime.DaysInMonth(today.Year, today.Month);
List<DateTime> dates = new List<DateTime>();
bool foundFirst = false;
for (int n = 1; n <= daysInMonth; n++)
{
var date = new DateTime(today.Year, today.Month, n);
// Skip untill we find the first Monday of the month.
if (date.DayOfWeek != DayOfWeek.Monday && !foundFirst)
continue;
foundFirst = true;
// Add all days except Sundays.
if (date.DayOfWeek != DayOfWeek.Sunday)
dates.Add(date);
int remainingDays = daysInMonth - n;
// Verify that there are enough days left in this month to add all days upto the next Saturday.
if (date.DayOfWeek == DayOfWeek.Saturday && remainingDays < 7)
break;
}
DateTime[] dateArray = dates.ToArray();
most easy:
int month = DateTime.Now.Month;
int year = DateTime.Now.Year;
int days= DateTime.DaysInMonth(year, month);
int totalSaturdays = 0;
for(int i=1;i<=days;i++)
{
var day = new DateTime(year, month, i);
if(day.DayOfWeek==DayOfWeek.Saturday)
{
totalSaturdays++;
}
}
Console.WriteLine(("Total Saturdays ="+totalSaturdays.ToString()));
Console.ReadLine();
Efficient solution;
var x = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month);
int i = 1;
while (i <= x)
{
if (new DateTime(DateTime.Now.Year, DateTime.Now.Month, i).DayOfWeek == DayOfWeek.Saturday)
{
Console.WriteLine(new DateTime(DateTime.Now.Year, DateTime.Now.Month, i));
i += 6;
}
i++;
}