I have starting date and number of months. I need to create specific number of monthly periods for example:
var startingDate = new DateTime(2010,1,15);
var months = 3;
for (int i = 0; i < months; i++)
{
Console.WriteLine("{0} from {1} to {2}", i + 1, startingDate.AddMonths(i),
startingDate.AddMonths(i + 1).AddDays(-1));
}
OUTPUT:
1 from 2010-1-15 to 2010-2-14
2 from 2010-2-15 to 2010-3-14
3 from 2010-3-15 to 2010-4-14
In this case code is simple and it works.
However when startDate is DateTime(2010,1,31) result is:
OUTPUT:
1 from 2010-1-31 to 2010-2-27
2 from 2010-2-28 to 2010-3-30
3 from 2010-3-31 to 2010-4-29
Are these periods correct?
The periods do look funky but they are correct.
If your periods are starting on the last day of the month they will end on the second to last day of the next month.
You get to choose if they are right or wrong. Here is what I mean :
If you start your period on the 15th of January, are you starting 14 days after the first day of the month, or are you starting 16 days before the last?
It can get even more tricky. If the 15th is a Tuesday, is your period defined as starting the 3rd Tuesday of a given month?
There is a lot of literature about this in the financial community, since the Day Count Conventions, the Business Days, Rolling Conventions, etc. can make a lot of difference in the pricing of a financial product, and in the cash flows associated with it.
Related
Can anyone explain to me why this unit test is failing?
I scoured through MSDN expecting to find an explanation, for example I was expecting to find something like, "the starting day is not inclusive" etc. but I found no such statement. Therefore I am confused as to why this seems to be off by one day.
The following will result in Sept 29th. I am expecting Sept 30th.
[Test]
public void AddDaysBug_OffByOne()
{
DateTime end = new DateTime(2018,10,3);
DateTime fourDaysEarlier = end.AddDays(-4);
// this fails. 29!=30
Assert.AreEqual(fourDaysEarlier.Day,30, "four days prior to October 3 is Sept 30");
}
Lets take these days one at a time for illustration...
3rd - 1 = 2nd
2nd - 1 = 1st
1st - 1 = 30th
30th - 1 = 29th
There is no zero day in months as with "normal" numbers.
Setp: 28, 29, 30
Oct: 1, 2, 3
So: (3 Oct - 4 day) is equal to 29
Sept 29th seems to be correct answer.
It's easy to see if you subtracting one day at a time.
-1 day, 10/02/2018
-2 day, 10/01/2018
-3 day, 09/30/2018
-4 day, 09/29/2018
Is there any better way to add one DateTime object to another one, than this:
DateTime first = new DateTime(2000, 1, 1);
DateTime second = new DateTime(11, 2, 5, 10, 10, 11);
DateTime result = first.AddYears(second.Year);
DateTime result = first.AddMonths(second.Month);
...
and so on...
In this example I'd like to get DateTime(2011, 3, 6, 10, 10, 11)
EDIT
After a intensive brainstorm it seems to there's no different way, but to facilitate it can be boxed inside additional class and operator+ just like in JonSkeet's answer
It doesn't make sense to add two DateTime values together. If you want to represent "11 years, 2 months, 5 days, 10 hours, 10 minutes and 11 seconds" then you should represent that. That's not the same as 0011-02-05T10:10:11. In particular, you'd never be able to add "2 months and 30 days" for example. Likewise you'd never be able to add just a single year, because you can't have 0 for month and day values within a date.
Now there's no BCL type to represent the idea of "11 years [...]" but you could create your own one reasonably easily. As an alternative, you could use my Noda Time project which has Period for precisely this purpose:
var localDateTime = new LocalDate(2000, 1, 10).AtMidnight();
var period = new PeriodBuilder {
Years = 11, Months = 2, Days = 5,
Hours = 10, Minutes = 10, Seconds = 11
}.Build();
var result = localDateTime + period;
Contrary to some other answers provided here, you cannot use TimeSpan for this purpose. TimeSpan doesn't have any concept of months and years, because they vary in length, whereas a TimeSpan represents a fixed number of ticks. (If your largest unit is days, then you're fine to use TimeSpan, but given your example, I assume you need months and years.)
If you don't want to use Noda Time, I'd recommend you fake up a Period-like class yourself. It's easy enough to do - for example:
// Untested and quickly hacked up. Lots more API you'd probably
// want, string conversions, properties etc.
public sealed class Period
{
private readonly int years, months, days, hours, minutes, seconds;
public Period(int years, int months, int days,
int hours, int minutes, int seconds)
{
this.years = years;
this.months = months;
this.days = days;
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
}
public static DateTime operator+(DateTime lhs, Period rhs)
{
// Note: order of operations is important here.
// Consider January 1st + (1 month and 30 days)...
// what do you want the result to be?
return lhs.AddYears(rhs.years)
.AddMonths(rhs.months)
.AddDays(rhs.days)
.AddHours(rhs.hours)
.AddMinutes(rhs.minutes)
.AddSeconds(rhs.seconds);
}
}
Usage:
DateTime first = new DateTime(2000, 1, 1);
Period second = new Period(11, 2, 5, 10, 10, 11);
DateTime result = first + second;
You need to be aware of how DateTime.Add will handle impossible situations - for example adding a month to January 31st will give you February 28th/29th depending on whether or not it's a leap year.
The simple approach I've listed here, going through intermediate values, has its downsides, because that truncation can happen twice (adding years and then adding months) when it needn't - for example, "February 29th + 1 year + 1 month" might logically be "March 29th" but it will actually end up as "March 28th" as the truncation to February 28th will happen before the month is added.
Trying to work out a "right" way of doing calendrical arithmetic is fiendishly difficult, particularly as in some cases people may disagree about what the "right" answer is. In the above code I've opted for simplicity and predictability - depending on your real requirements, you may need something more complex.
DateTime first = new DateTime(2000, 1, 1);
DateTime second = new DateTime(11, 2, 5, 10, 10, 11);
DateTime result = new DateTime(first.Ticks + second.Ticks);
You have a DateTime which represents a point in time. And you want to add a number of years/months/days/hours/minutes/seconds to it.
A change in DataTime is not a point, it is a vector (a difference between points). It is really easy to mistake one for the other, as they often have similar structure. However, that kind of type error leads to lots of pain.
Avoiding it doesn't fix your pain, but it makes it manageable.
Adding two DateTime together is adding two points together. Sort of like adding the location of Los Angeles to New York.
Now, adding the "vector" of LA to NY to London makes sense -- because the travel vector is a vector, not a point. And point+vector is just a point.
So this means you need to create a time vector type. A simple time span is an option, but probably not appropriate: because you care about months, years and days, not nanoseconds or absolute time durations.
I'll dub the name of the vector a CalendarVector, as it represents movement on a Calendar, not in time itself.
An easy first pass is to create a tuple of each sub type of time -- years, months, days, etc -- then add them in some arbitrary order to your original DateTime with an overloaded operator+.
You should support:
DateTime = DateTime + CalendarVector
CalendarVector = CalendarVector + CalendarVector
CalendarVector = CalendarVector - CalendarVector
CalendarVector = int * CalendarVector
CalendarVector = - CalendarVector
DateTime = DateTime - CalendarVector
CalendarVector = DateTime - DateTime
ideally. The CalendarVector + DateTime overload is optional, but probably not needed.
However, this only gets you half way.
The big remaining problem is that CalendarVector addition does not commute. Adding 1 month to a DateTime, then adding 1 day, is different than adding 1 day then adding 1 month.
And this is fundamental.
There is the problem of "what does it mean to be 1 month after January 31st", which can be answered, but any reasonable answer to that question doesn't solve the commuting problem.
Your planned constructor -- where you feed it the number of years, months, days, hours, minutes seconds -- is thus ambiguous in what it means.
So a robust solution should not have that constructor.
A solution is to create Years, Months, Days, Hours, Minutes and Seconds types that you explicitly add together. The order they are added together is the order they are applied to the DateTime you add it to. Commuting and "simplification" is avoided until the final application on a DateTime -- so +1 year, +2 days, -1 month, -1 year, -2 days, +1 month is not the zero transformation.
There is a related problem with DateTime-DateTime -- it should return a CalendarVector v such that lhs = rhs + v, but there are multiple such vectors. The same problem can occur with spherical coordinates -- do you mean the short way around the Earth, or the long way? It doesn't matter in some contexts -- but then you halve the result to find the mid-way point. Plus, you get discontinuities as you approach "far side of the world".
So my advice would be to maintain a list of transformations on a DateTime object. 1 year is a transformation that consists of adding 1 to the year field, and then repairing the other fields so they are consistent. These transformations support negation. Addition is applying them one at a time, from left to right. Negation may also reverse the order of application, and adjacent transformations "of the same kind" may combine (so +1 month -1 month becomes the identity transformation, instead of a clamping operation based off next months' end of month), or not (so x = x+1 month,then x = x-1 month on the next line is the same as x = x + 1 month - 1 month).
Yet another approach is to insist that the user provide a policy for what to do in these exceptional circumstances (which happen ... all the time), because this problem is thorny enough that a library that "solves" the problem can at best highlight the problems and force the client programmer to think about them and make decisions.
Simplest is:
firstDateTime.AddTicks(secondDateTime.Ticks);
I have tried searching for a solution which gives the correct week number for the date value.
link1, link2,link3
Followed the methods in the above links, but for the date 30/12/2014, I get the week number as 53. but it falls as 1st week of 2015 year.
I tried the below methods to get the week number of the year for the specific date.
private int GetWeekNumberOfTheYear() {
var currentCulture = CultureInfo.CurrentCulture;
// option 1
var weekNo = currentCulture.Calendar.GetWeekOfYear(DateTime.Now,currentCulture.DateTimeFormat.CalendarWeekRule, currentCulture.DateTimeFormat.FirstDayOfWeek);
// option 2
var weekNo = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
return weekNo; }
Is the method above is correct to return 53 as week number or it should be 1 ?
Is there any mistake in the above code. Suggestions please.
EDIT :
Found many searches specified, Dec 29th 2014 to 4th Jan 2015 as 1st week of year 2015.
So my confusion is the present week must be taken as 53rd Week or 1st Week.
http://week-number.net/calendar-with-week-numbers-2014.html
http://www.epochconverter.com/date-and-time/weeknumbers-by-year.php
If you're looking for the ISO-8601 week-of-week-year, you could use my Noda Time project:
var date = new LocalDate(2014, 12, 30);
var week = date.WeekOfWeekYear; // 1
var weekYear = date.WeekYear; // 2015
You can get a LocalDate from a DateTime via a LocalDateTime, but ideally you'd use the Noda Time times as widely as possible through your project. (That's the way I'd hope you'd get the maximum benefit, anyway.)
I'm stuck trying to figure this one out..
We currently have a date criteria on our reports, that are limited by days, configurable of course, currently set to 90 days.. message says, it is limited by 90 days, however my boss wants to increase it to 13 months, unfortunately if I did that, I'd need to do it by days and it would say, 395 days..
Not a very friendly message..
Trying to figure out a way to satisfy this, my other only option is to add another settings that is limited by months as well as days. but then i still need to convert the months back to days which wont be perfect since not every month has same days..
Ideas?
You need to decide if you're going to use 13 months as the time interval, or some number of days that approximates to 13 months. If you use 13 months, then the number of days (or the end date for your report) is going to vary depending on the start date.
I would suggest making your report configurable for either months or days (storing not just the number, but the units in configuration). You can then display on the report whatever has been specified in the configuration (with the units from configuration, too) and calculate the end date for the query by adding the configured number of configured units to the start date.
If you try to do everything in days, when you're now working in months, you'll just make life difficult for yourself.
It's much easier to add 13 months to the start date to get the end date, than it is to try and (inaccurately) work out how many months in a given number of days.
Use the TimeSpan object to perform the calculations you need for your date criteria.
I would do something like this, given the number of days:
int myDays; // 390 or whatever
DateTime d1 = DateTime.Now;
DateTime d2 = d1.AddDays(myDays);
int monthsDiff = d2.Month - d1.Month + 12 * (d2.Year - d1.Year);
DateTime d3 = d1.AddMonths(monthsDiff);
TimeSpan tf = d2 - d3;
string msg = monthsDiff.ToString() + " months, " + tf.Days + " days";
TimeSpan give you duration between two DateTime objects. It can give it consistently in Days, Hours or Mins; Number of months would be different based upon actual start & end dates as different months have different number of actual days.
Having said that, you can always write a Utility method that gives you YourTimeSpan object that gives you number of Months etc based upon your calendar and StartDate / EndDates.
In your case you can make it even simpler by storing it separately in configuration, for example - ReportDuration_Years, ReportDuration_Months, ReportDuration_Days. This would allow you to create meaningful lable on your report as well as allow to identify StartDate and EndDate properly.
//Call this by passing values from configuration
private string GetNiceLookingLable(int? years, int? months, int? days){
var yearMessage = (years.HasValue)?String.Format("{0} Years", years):String.Empty;
var monthMessage = (months.HasValue)?String.Format("{0} Months", months):String.Empty;
var daysMessage = (days.HasValue)?String.Format("{0} Days", days):String.Empty;
// You probably want to concatenate them properly
return String.Format("{0} {1} {2}",yearMessage, monthMessage, daysMessage);
}
-
//Call this to get starting date
private DateTime getStartingDate(int? years, int? months,int? days){
var retDate = DateTime.Today;
if(years.HasValue){
retDate = retDate.AddYears(-1*years.Value);
}
if(months.HasValue){
retDate = retDate.AddMonths(-1*months.Value);
}
if(days.HasValue){
retDate = retDate.AddDays(-1*days.Value);
}
return retDate;
}
I wrote some code for my new billing system. The purpose is to bill the customer on the same day each month. (not the 1st or last day of the month)
static bool NeedToBill(DateTime planLastBilled, DateTime cycleDate)
{
// is today the same date as the cycleDate AND is was the planLastBilled not the same day as today?
if (DateTime.UtcNow.Day.Equals(cycleDate.Day) && !DateTime.UtcNow.Day.Equals(planLastBilled))
return true;
else
return false;
}
The 2 pitfalls are:
If his cycleDate.Day is the 31 and the current month only has 29 days
cycleDate is Feb 29 2012 - he will only get billed on leap years
Is there a common best practice here?
so it seems like there's a bunch things to check
has this account already been billed this month?
does the cycle day exists in the current month
is the cycle day greater than or equal to the current date (this is ideal if
the transaction failed the day before)
Thanks!
Only allow the choice of a billing day between 1 - 28. In my experience this is how most credit card / loan companies deal with it when given a choice.
What does the same day each month mean?
If I am a customer, I want to be billed on the 16th each month. No problem. If I want to be billed on the 31st on each month the obvious issue is not all months have 31 days as you've pointed out in your question.
Why not check the current month for the number of days. If it has less than 31 days, make the last day of the month the bill date.
Is there more to the problem?
I'd say make him choose between 1-28, or any day but charge on the last day on the month if the current month has less days than the chosen day of month.
Ok, I believe I have been totally over-thinking this.
This is simple and covers everything:
bool NeedToBill = ((DateTime.UTCNow – LastBillDate) >= 30 Days)
It will not necessarily bill on the exact same day, however it's close enough.
This also adds flexibility if the transaction was denied for a day, or if the scheduled task was not ran for 1 day the next time it runs it will pick it up.