Date calculations in C# - c#

When given a start date a need to do various calculations on it to produce 3 other dates.
Basically I need to work out what date the user has been billed up to for different frequencies based on the current date.
Bi-Annually (billed twice a year),
Quarterly (billed 4 times a year),
and Two Monthly (billed ever other month).
Take the date 26/04/2008
- BiAnnually: This date would have been last billed on 26/10/2010 and should give the date 26/04/2011.
- Quarterly: This date would have been last billed on 26/01/2011 and should give the date 26/04/2011.
- Two Month: This date would have been last billed on 26/12/2010 and should give the date 26/02/2011.
Assistance is much appreciated.

I think that you can just do like this:
public void FindNextDate(DateTime startDate, int interval);
DateTime today = DateTime.Today;
do {
startDate = startDate.AddMonths(interval);
} while (startDate <= today);
return startDate;
}
Usage:
DateTime startDate = new DateTime(2008, m4, 26);
DateTime bi = FindNextDate(startDate, 6);
DateTime quarterly = FindNextDate(startDate, 3);
DateTime two = FindNextDate(startDate, 2);

I think all you want is something like
DateTime x = YourDateBasis;
y = x.AddMonths(6);
y = x.AddMonths(3);
y = x.AddMonths(2);
Then to edit from comment,
Date Math per the period cycle of the person's account, you would simply need the start and end date and keep adding respective months until you've created all expected months. Almost like that of a loan payment that's due every month for 3 years
DateTime CurrentDate = DateTime.Now;
while( CurrentDate < YourFinalDateInFuture )
{
CurrentDate = CurrentDate.AddMonths( CycleFrequency );
Add Record into your table as needed
Perform other calcs as needed
}

enum BillPeriod
{
TwoMonth = 2,
Quarterly = 3,
SemiAnnually = 6,
BiAnnually = 24
}
public Pair<Datetime, Datetime> BillDates(Datetime currentBillDate, BillPeriod period)
{
Datetime LastBill = currentBillDate.AddMonths(-1 * (int)period);
Datetime NextBill = currentBillDate.AddMonths((int)period);
return new Pair<Datetime,Datetime>(LastBill, NextBill);
}

This is a terrible solution, but it works. Remember, red-light, green-light, refactor. Here, we're at green-light:
namespace ConsoleApplication1 {
class Program {
static void Main(string[] args) {
Console.WriteLine(GetLastBilled(new DateTime(2008, 4, 26), 6));
Console.WriteLine(GetNextBilled(new DateTime(2008, 4, 26), 6));
Console.WriteLine(GetLastBilled(new DateTime(2008, 4, 26), 4));
Console.WriteLine(GetNextBilled(new DateTime(2008, 4, 26), 4));
Console.WriteLine(GetLastBilled(new DateTime(2008, 4, 26), 2));
Console.WriteLine(GetNextBilled(new DateTime(2008, 4, 26), 2));
Console.WriteLine("Complete...");
Console.ReadKey(true);
}
static DateTime GetLastBilled(DateTime initialDate, int billingInterval) {
// strip time and handle staggered month-end and 2/29
var result = initialDate.Date.AddYears(DateTime.Now.Year - initialDate.Year);
while (result > DateTime.Now.Date) {
result = result.AddMonths(billingInterval * -1);
}
return result;
}
static DateTime GetNextBilled(DateTime initialDate, int billingInterval) {
// strip time and handle staggered month-end and 2/29
var result = initialDate.Date.AddYears(DateTime.Now.Year - initialDate.Year);
while (result > DateTime.Now.Date) {
result = result.AddMonths(billingInterval * -1);
}
result = result.AddMonths(billingInterval);
return result;
}
}
}
This is really tricky. For example, you need to take into account that the date you billed could have been 2/29 on a leap year, and not all months have the same number of days. That's why I did the initialDate.Date.AddYears(DateTime.Now.Year - initialDate.Year); call.

Related

Calculating dates expecting double

I am trying to simply subtract two dates. Probably this is a messy way of doing it. It says it cannot convert to double even though.
DateTime daysPlus14days = _dal.getOptinDate(new Guid(_myuser.id.ToString())).AddDays(14);
DateTime currentDate = DateTime.Now;
DateTime timeLeft = (daysPlus14days - currentDate).TotalDays
This just basically goes to db and gets me the date they created there account. Its just to work out how many days left they have 14 days to click a button other wise it will vanish.
public DateTime getOptinDate(Guid id)
{
var q = _dal.portalEntities.tblPortalUsers.Where(a => a.id == id).FirstOrDefault();
return (DateTime)q.optinDateStart;
}
just change this line:
double timeLeft = (daysPlus14days - currentDate).TotalDays;
TotalDays returns double and not DateTime
Refer this link https://msdn.microsoft.com/en-us/library/system.timespan.totaldays(v=vs.110).aspx,
System.DateTime date1 = new System.DateTime(1996, 6, 3, 22, 15, 0);
System.DateTime date2 = new System.DateTime(1996, 12, 6, 13, 2, 0);
System.TimeSpan diff1 = date2.Subtract(date1);
double totaldays = diff1.TotalDays;

Absolute difference of a TimeSpan object with a timing interval in scale of month and year

I am in some trouble within a simple timing manipulation in C#.
The user defines two DateTime objects, as the start and the end of a time interval:
DateTime From = new DateTime(Y, Mo, D, H, Mi, S, Ms);
DateTime To = new DateTime(YY, MMo, DD, HH, MMi, SS, MMs);
Then a delay parameter, is which a TimeSpan object, would be taken into account:
TimeSpan delay = new TimeSpan(day, month, hour, second);
Now the program should return the deviation of the time interval, corresponding to the delay parameter.
Now, there are two problems:
1- Time span has no Year and Month parameters, whereas the difference between From and To might be more than Day... How can I feed Year and Month into the TimeSpan object?!... (I know that there is no defined constructor for this aim)
2- The final difference, which I try to catch by below code snippet just produces garbage:
var diff = (To - From).duration() - delay;
How should I resolve this case?!
I am appreciated if anyone can handle above cases...
This is the sort of thing that my Noda Time project is designed to handle. It has a Period type which does know about months and years, not just a fixed number of ticks. For example:
LocalDateTime start = new LocalDateTime(2014, 1, 1, 8, 30);
LocalDateTime end = new LocalDateTime(2014, 9, 16, 12, 0);
Period delay = new PeriodBuilder {
Months = 8,
Days = 10,
Hours = 2,
Minutes = 20
}
.Build();
Period difference = (Period.Between(start, end) - delay).Normalize();
Here difference would be a period of 5 days, 1 hour, 10 minutes. (The Normalize() call is to normalize all values up to days... otherwise you can have "1 hour - 10 minutes" for example.) The Period API is going to change a bit for Noda Time 2.0, but it will still have the same basic ideas.)
If you you choose to round down and add extension methods :
public static class Extensions
{
private const double DaysInYear = 365.242;
private const double DaysInMonth = 30.4368;
public static int GetDays(this TimeSpan ts)
{
return (int)((ts.TotalDays % DaysInYear % DaysInMonth));
}
public static int GetMonths(this TimeSpan ts)
{
return (int)((ts.TotalDays % DaysInYear) / DaysInMonth);
}
public static int GetYears(this TimeSpan ts)
{
return (int)(ts.TotalDays / DaysInYear);
}
}
It would be easy as:
var oldDate = new DateTime(2002, 7, 15);
var newDate = new DateTime(2014, 9, 16, 12, 3, 0);
// Difference
var ts = newDate - oldDate;
var dm = ts.Minutes; //3
var dh = ts.Hours; //12
var dd = ts.GetDays(); //2
var dM = ts.GetMonths(); //2
var dY = ts.GetYears(); //12
Note that this is an approximation and would apply only if you can make assumptions that
DaysInYear = 365.242
DaysInMonth = 30.4368
are correct.

How do I find week numbers of a given date range in C#

I need to find the week numbers of a given date rage in C#.
Ex: date between 01/01/2014 and 14/01/2014
Week numbers are 1st,2nd and 3rd weeks, likewise.
Thanks.
Not the smartest way, but works!
var d1 = new DateTime(2014, 1, 1);
var d2 = new DateTime(2014, 1, 14);
var currentCulture = CultureInfo.CurrentCulture;
var weeks = new List<int>();
for (var dt = d1; dt < d2; dt =dt.AddDays(1))
{
var weekNo = currentCulture.Calendar.GetWeekOfYear(
dt,
currentCulture.DateTimeFormat.CalendarWeekRule,
currentCulture.DateTimeFormat.FirstDayOfWeek);
if(!weeks.Contains(weekNo))
weeks.Add(weekNo);
}
This should work:
public List<int> Weeks(DateTime start, DateTime end)
{
List<int> weeks=new List<int>();
var Week=(int)Math.Floor((double)start.DayOfYear/7.0); //starting week number
for (DateTime t = start; t < end; t = t.AddDays(7))
{
weeks.Add(Week);
Week++;
}
return weeks;
}
All this does is get the week of the start date, then loops through one week at a time until you get to the end date, incrementing the week and adding it to the list of weeks.
OK, let's start with a simple, unoptimized example. We'll simply examine every date between those dates and check what week of the year it is.
We can do that with a simple loop:
var end = new DateTime(2014, 1, 14);
for (var date = new DateTime(2014, 1, 1); date <= end; date = date.AddDays(1))
{
}
This will simply loop over every day between two dates. Now we need to examine those days to determine their day of week. To do this you need to consider a few things: What is the first day of a week? Sunday? Monday? Are we assuming gregorian calendar?
For our example, let's assume the first day of the week is a Sunday and we are indeed using the Gregorian calendar. Then we will check each date, and keep a list of unique weeks of the year using a HashSet:
var weekNumber = new HashSet<int>();
var end = new DateTime(2014, 1, 14);
var calendar = new GregorianCalendar();
for (var date = new DateTime(2014, 1, 1); date <= end; date = date.AddDays(1))
{
weekNumber.Add(calendar.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday));
}
The weekNumber Hashset now contains the weeks of the year.
Is this optimized? No. It checks far more dates than it needs to, but the implementation is simple and fast enough. Optimizing can be done as a separate task.
Easy. Here's the code that determines the week of the year for a single date. This should get you going:
int WeekOfYear(DateTime date, DayOfWeek dayOfWeekStart) {
//Find the first day of the year that's the start of week day. If it's not 1/1,
//then we have a first partial week.
bool firstPartialWeek = false;
DateTime firstFullWeekStart = new DateTime(date.Year, 1, 1);
while(firstFullWeekStart.DayOfWeekDay != dayOfWeekStart) {
firstFullWeekStart = firstOfWeek.AddDays(1);
firstPartialWeek = true;
}
//Return the week by integer dividing the date difference by seven,
//and adding in the potential first partial week
return (firstPartialWeek ? 1 : 0) + ((date - firstFullWeekStart).TotalDays / 7);
}

Generating weekly dates

I am sure this has been done before so i am looking for an efficient solution instead of own custom solution.
Given 2 dates, I am trying to generate the accurate weekly date (for creating weekly orders).
EDIT: I need to use .NET standard library to do this.
Example below,
Given 28/02/2012 and 6/03/2012.
so, the weekly dates generated are
- Week From(Start Monday): Week To(End Sunday):
- 27/02/2012 - 04/03/2012
- 05/03/2012 - 11/03/2012
Another example (1 month)
Given 01/02/2012 and 29/02/2012
so, the weekly dates generated are
- Week From(Start Monday): Week To(End Sunday):
- 30/01/2012 - 05/02/2012
- 06/02/2012 - 12/02/2012
- 13/02/2012 - 19/02/2012
- 20/02/2012 - 26/02/2012
- 27/02/2012 - 04/03/2012
I am doing this in c#. Has this been done before? Mind sharing the solutions?
Cheers
Here's a solution using Noda Time. Admittedly it requires a <= operator which I'm just implementing right now - but that shouldn't take long :)
using System;
using NodaTime;
class Test
{
static void Main()
{
ShowDates(new LocalDate(2012, 2, 28), new LocalDate(2012, 3, 6));
ShowDates(new LocalDate(2012, 2, 1), new LocalDate(2012, 2, 29));
}
static void ShowDates(LocalDate start, LocalDate end)
{
// Previous is always strict - increment start so that
// it *can* be the first day, then find the previous
// Monday
var current = start.PlusDays(1).Previous(IsoDayOfWeek.Monday);
while (current <= end)
{
Console.WriteLine("{0} - {1}", current,
current.Next(IsoDayOfWeek.Sunday));
current = current.PlusWeeks(1);
}
}
}
Obviously it's possible to do this in normal DateTime as well, but there's no real representation of "just a date" which makes the code less clear - and you'd need to implement Previous yourself.
EDIT: For example, in this case you might use:
using System;
class Test
{
static void Main()
{
ShowDates(new DateTime(2012, 2, 28), new DateTime(2012, 3, 6));
ShowDates(new DateTime(2012, 2, 1), new DateTime(2012, 2, 29));
}
static void ShowDates(DateTime start, DateTime end)
{
// In DateTime, 0=Sunday
var daysToSubtract = ((int) start.DayOfWeek + 6) % 7;
var current = start.AddDays(-daysToSubtract);
while (current <= end)
{
Console.WriteLine("{0} - {1}", current, current.AddDays(6));
current = current.AddDays(7);
}
}
}
Assuming you don't have to figure out that the start date is a monday:
var slots = new List<Tuple<DateTime, DateTime>>();
DateTime start = new DateTime(2012, 2, 28);
DateTime end = new DateTime(2012, 3, 6);
for (DateTime i = start; i < end; i = i.AddDays(7))
{
slots.Add(new Tuple<DateTime, DateTime>(i, i.AddDays(6)));
}
foreach (var slot in slots)
{
Console.WriteLine("{0}\t{1}", slot.Item1.ToString("dd/MM/yyyy"), slot.Item2.ToString("dd/MM/yyyy"));
}
Edit: Assuming you have to figure out what monday and what sunday covers the date range, you can move one day backwards till you hit a monday, and a day forward till you hit a sunday.
class Program
{
static void Main(string[] args)
{
var slots = new List<Tuple<DateTime, DateTime>>();
DateTime start = FirstMonday(new DateTime(2012, 2, 28));
DateTime end = FirstSunday(new DateTime(2012, 3, 6));
for (DateTime i = start; i < end; i = i.AddDays(7))
{
slots.Add(new Tuple<DateTime, DateTime>(i, i.AddDays(6)));
}
foreach (var slot in slots)
{
Console.WriteLine("{0}\t{1}", slot.Item1.ToString("dd/MM/yyyy"), slot.Item2.ToString("dd/MM/yyyy"));
}
Console.ReadLine();
}
static DateTime FirstMonday(DateTime date)
{
while (date.DayOfWeek != DayOfWeek.Monday) date = date.AddDays(-1);
return date;
}
static DateTime FirstSunday(DateTime date)
{
while (date.DayOfWeek != DayOfWeek.Sunday) date = date.AddDays(1);
return date;
}
}
This solution allows you to customize your start and end DayOfWeek:
Solution:
public Dictionary<DateTime, DateTime> GetWeeklyDateTimes(DateTime from, DateTime to, DayOfWeek startDay, DayOfWeek endDay)
{
int startEndSpan = 7 - endDay - startDay;
// Subtract days until it falls on our desired start day
from = from.AddDays(startDay - from.DayOfWeek);
// Add days until it falls on our desired end day
to = to.AddDays(to.DayOfWeek - endDay + 2);
Dictionary<DateTime, DateTime> dateTimes = new Dictionary<DateTime, DateTime>();
while (to.Subtract(from).Days > startEndSpan)
{
dateTimes.Add(from, from.AddDays(startEndSpan));
from = from.AddDays(startEndSpan + 1);
}
return dateTimes;
}
Example Usage:
// DateTime(2012, 2, 1) corresponds to Year 2012, Month February, Day 1
Dictionary<DateTime, DateTime> dateTimes = GetWeeklyDateTimes(new DateTime(2012, 2, 1), new DateTime(2012, 2, 29), DayOfWeek.Monday, DayOfWeek.Sunday);
foreach (KeyValuePair<DateTime, DateTime> entry in dateTimes)
{
Trace.WriteLine(entry.Key.ToString() + " " + entry.Value.ToString());
}

How to check if a DateTime range is within another 3 month DateTime range

Hi I have a Start Date and End Date per record in a db.
I need to check to see where the time period falls in a 2 year period broken into two lots of quarters then display what quarters each record falls into.
Quarter 1 includes June 09, Jul 09, Aug 09
Quarter 2 includes Sept 09, Oct 09, Nov 09
Quarter 3 includes Dec 09, Jan 10, Feb 10
Quarter 4 includes Mar 10, Apr 10, May 10
Quaretr 5 includes Jun 10, Jul 10...
e.g. 01/10/09 - 01/06/10 would fall into quarters 2, 3, 4 & 5
I am very new to .NET so any examples would be much appreciated.
This should work for you also.
class Range
{
public DateTime Begin { get; private set; }
public DateTime End { get; private set; }
public Range(DateTime begin, DateTime end)
{
Begin = begin;
End = end;
}
public bool Contains(Range range)
{
return range.Begin >= Begin && range.End <= End;
}
}
and then to use it
List<Range> ranges = new List<Range>();
ranges.Add(new Range(DateTime.Now, DateTime.Now.AddMonths(3)));
ranges.Add(new Range(DateTime.Now.AddMonths(3), DateTime.Now.AddMonths(6)));
Range test = new Range(DateTime.Now.AddMonths(1), DateTime.Now.AddMonths(2));
var hits = ranges.Where(range => range.Contains(test));
MessageBox.Show(hits.Count().ToString());
You would call IntervalInQuarters as follows:
IntervalInQuarters(new DateTime(2007, 10, 10), new DateTime(2009, 10, 11));
The function returns a list of quarter start dates. Note that the range of quarters searched is defined within the function itself. Please edit as appropriate for your situation. They key point is to make sure the interval/quarter intersection logic is right.
private List<DateTime> IntervalInQuarters(DateTime myStartDate, DateTime myEndDate)
{
DateTime quarterStart = new DateTime(2006, 06, 01);
DateTime nextQuarterStart = new DateTime(2006, 09, 01);
DateTime finalDate = new DateTime(2011, 01, 01);
List<DateTime> foundQuarters = new List<DateTime>();
while (quarterStart < finalDate)
{
// quarter intersects interval if:
// its start/end date is within our interval
// our start/end date is within quarter interval
DateTime quarterEnd = nextQuarterStart.AddDays(-1);
if (DateInInterval(myStartDate, quarterStart, quarterEnd) ||
DateInInterval(myEndDate, quarterStart, quarterEnd) ||
DateInInterval(quarterStart, myStartDate, myEndDate) ||
DateInInterval(quarterEnd, myStartDate, myEndDate))
{
foundQuarters.Add(quarterStart);
}
quarterStart = nextQuarterStart;
nextQuarterStart = nextQuarterStart.AddMonths(3);
}
return foundQuarters;
}
private bool DateInInterval(DateTime myDate, DateTime intStart, DateTime intEnd)
{
return ((intStart <= myDate) && (myDate <= intEnd));
}
private void Form1_Load(object sender, EventArgs e)
{
DateTime[,] ranges = new DateTime[3,2];
//Range 1 - Jan to March
ranges[0, 0] = new DateTime(2010, 1, 1);
ranges[0, 1] = new DateTime(2010, 3, 1);
//Range 2 - April to July
ranges[1, 0] = new DateTime(2010, 4, 1);
ranges[1, 1] = new DateTime(2010, 7, 1);
//Range 3 - March to June
ranges[2, 0] = new DateTime(2010, 3, 1);
ranges[2, 1] = new DateTime(2010, 6, 1);
DateTime checkDate = new DateTime(2010, 4, 1);
string validRanges = string.Empty;
for (int i = 0; i < ranges.GetLength(0); i++)
{
if (DateWithin(ranges[i,0], ranges[i,1], checkDate))
{
validRanges += i.ToString() + " ";
}
}
MessageBox.Show(validRanges);
}
private bool DateWithin(DateTime dateStart, DateTime dateEnd, DateTime checkDate)
{
if (checkDate.CompareTo(dateStart) < 0 || checkDate.CompareTo(dateEnd) > 0)
{
return false;
}
return true;
}
You may have to take a look at: http://msdn.microsoft.com/en-us/library/03ybds8y(v=VS.100).aspx
This may start you up
FindQuarter(DateTime startDate, DateTime endDate) // 01-10-09, 01-06-10
{
startDateQuarter = GetQuarter(startDate.Month); // 2
endDateQuarter = GetQuarter(endDate.Month); // 1
endDateQuarter += (endDate.Year - startDate.Year) * 4; // 5
// fill up startDateQuarter to endDateQuarter into a list
// and return it // 2,3,4,5
}
GetQuarter(int month) // 6
{
int quarter;
// check the month value and accordingly assign one of the basic quarters
// using if-else construct ie, if(month>=6 && month<=8){ quarter = 1 };
return quarter; // 1
}
Instead of GetQuarter() method, you can also use a dictionary to store your month to quarter mappings
Dictionary<int, int> quarter = new Dictionary<int, int>();
quarter.Add(1,1); //of the format Add(month,quarter)
quarter.Add(2,1);
...
Now instead of GetQuarter(someDate.Month); you can use quarter[someDate.Month];
If you want to compare two dates you should find out the first day of the quarter corresponds every of this dates, then you can compare this two dates:
using System;
namespace DataTime {
class Program {
static int GetQuarter (DateTime dt) {
int Month = dt.Month; // from 1 to 12
return Month / 3 + 1;
}
static DateTime GetQuarterFirstDay (DateTime dt) {
int monthsOfTheFirstDayOfQuarter = (GetQuarter (dt) - 1) * 3 + 1;
return new DateTime(dt.Year, monthsOfTheFirstDayOfQuarter, 1);
// it can be changed to
// return new DateTime(dt.Year, (dt.Month/3)*3 + 1, 1);
}
static void Main (string[] args) {
DateTime dt1 = new DateTime (2009, 6, 9),
dt2 = new DateTime (2009, 7, 9),
dt3 = new DateTime (2009, 8, 9),
dt4 = new DateTime (2009, 8, 9);
Console.WriteLine ("dt1={0}", dt1.AddMonths (1));
Console.WriteLine ("dt2={0}", dt2.AddMonths (1));
Console.WriteLine ("dt3={0}", dt3.AddMonths (1));
DateTime startDate = DateTime.Now,
endDate1 = startDate.AddMonths(24).AddDays(1),
endDate2 = startDate.AddMonths(24).AddDays(-1),
endDate3 = startDate.AddMonths(28);
Console.WriteLine ("Now we have={0}", startDate);
Console.WriteLine ("endDate1={0}", endDate1);
Console.WriteLine ("endDate2={0}", endDate2);
Console.WriteLine ("endDate3={0}", endDate3);
Console.WriteLine ("GetQuarterFirstDay(startDate)={0}", GetQuarterFirstDay (startDate));
Console.WriteLine ("GetQuarterFirstDay(endDate1)={0}", GetQuarterFirstDay (endDate1));
Console.WriteLine ("GetQuarterFirstDay(endDate2)={0}", GetQuarterFirstDay (endDate2));
Console.WriteLine ("GetQuarterFirstDay(endDate3)={0}", GetQuarterFirstDay (endDate3));
if (DateTime.Compare (GetQuarterFirstDay (endDate2), GetQuarterFirstDay (startDate).AddMonths (24)) > 0)
Console.WriteLine ("> 2 Yeas");
else
Console.WriteLine ("<= 2 Yeas");
if (DateTime.Compare (GetQuarterFirstDay (endDate3), GetQuarterFirstDay (startDate).AddMonths (24)) > 0)
Console.WriteLine ("> 2 Yeas");
else
Console.WriteLine ("<= 2 Yeas");
}
}
}
produce
dt1=09.07.2009 00:00:00
dt2=09.08.2009 00:00:00
dt3=09.09.2009 00:00:00
Now we have=22.04.2010 11:21:45
endDate1=23.04.2012 11:21:45
endDate2=21.04.2012 11:21:45
endDate3=22.08.2012 11:21:45
GetQuarterFirstDay(startDate)=01.04.2010 00:00:00
GetQuarterFirstDay(endDate1)=01.04.2012 00:00:00
GetQuarterFirstDay(endDate2)=01.04.2012 00:00:00
GetQuarterFirstDay(endDate3)=01.07.2012 00:00:00
<= 2 Yeas
> 2 Yeas
EDITED: I fixed an error from the first version. Now it should works correct.

Categories

Resources