问题
I have a enum type called PaymentFrequency
whose values indicate how many payments per year are being made...
So I have
public enum PaymentFrequency
{
None = 0,
Annually = 1,
SemiAnnually = 2,
EveryFourthMonth = 3,
Quarterly = 4,
BiMonthly = 6,
Monthly = 12,
EveryFourthWeek = 13,
SemiMonthly = 24,
BiWeekly = 26,
Weekly = 52
}
Based on NumberOfPayments
, PaymentFrequency
, and FirstPaymentDate
(of type DateTimeOffset) I want to calculate LastPaymentDate
. But I am having issue figuring out how many time units (days, months) to add in case of SemiMonthly...
switch (paymentFrequency)
{
// add years...
case PaymentFrequency.Annually:
LastPaymentDate = FirstPaymentDate.AddYears(NumberOfPayments - 1);
break;
// add months...
case PaymentFrequency.SemiAnnually:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 6); // 6 months
break;
case PaymentFrequency.EveryFourthMonth:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 4); // 4 months
break;
case PaymentFrequency.Quarterly:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 3); // 3 months
break;
case PaymentFrequency.BiMonthly:
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 2); // 2 months
break;
case PaymentFrequency.Monthly:
LastPaymentDate = FirstPaymentDate.AddMonths(NumberOfPayments - 1);
break;
// add days...
case PaymentFrequency.EveryFourthWeek:
LastPaymentDate = FirstPaymentDate.AddDays((NumberOfPayments - 1) * 4 * 7); // 4 weeks (1 week = 7 days)
break;
case PaymentFrequency.SemiMonthly:
// NOTE: how many days in semi month? AddMonths (0.5) does not work :)
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) * 0.5); // 2 weeks (1 week = 7 days)
break;
case PaymentFrequency.BiWeekly:
LastPaymentDate = FirstPaymentDate.AddDays((NumberOfPayments - 1) * 2 * 7); // 2 weeks (1 week = 7 days)
break;
case PaymentFrequency.Weekly:
LastPaymentDate = FirstPaymentDate.AddDays((NumberOfPayments - 1) * 7); // 1 week (1 week = 7 days)
break;
case PaymentFrequency.None:
default:
throw new ArgumentException("Payment frequency is not initialized to valid value!", "paymentFrequency");
}
So, how many days/months should I use when using SemiMonthly? Is this even possible without knowing exact # of days for each month in between? Or is this really simple, and I have just run out of caffeine and I am not seeing forest for the trees :)
回答1:
For Semi-Monthly, if your first payment was always the 1st payment of the month as well (that is, anytime from the 1st to the 13th, starting after 13th is problematic as discussed in the comments), you could do as follows:
// assuming first payment will be 1st of month, add month for every 2 payments
// num payments / 2 (int division, remainder is chucked)
// then add 15 days if this is even payment of the month
LastPaymentDate = FirstPaymentDate.AddMonths((NumberOfPayments - 1) / 2)
.AddDays((NumberOfPayments % 2) == 0 ? 15 : 0);
So for the 1st payment, this will add 0 months and 0 days so be 1st payment date. For 2nd payment, this will add 0 months (int dividision, remainder is chucked) and 15 days for 16th of month. For 3rd payment, this will add 1 month (1 / 3) and 0 days for 1st of next month, etc.
This is assuming that the FirstPaymentDate will be on the 1st of some given month. You can probably see where to go from here if you want to allow the 16th to be a starting date, etc.
Make sense?
So to illustrate, if we had:
DateTime LastPaymentDate, FirstPaymentDate = new DateTime(2011, 12, 5);
for(int numOfPayments=1; numOfPayments<=24; numOfPayments++)
{
LastPaymentDate = FirstPaymentDate.AddMonths((numOfPayments - 1) / 2)
.AddDays((numOfPayments % 2) == 0 ? 15 : 0);
Console.WriteLine(LastPaymentDate);
}
This loop would give us:
12/5/2011 12:00:00 AM
12/20/2011 12:00:00 AM
1/5/2012 12:00:00 AM
// etc...
10/20/2012 12:00:00 AM
11/5/2012 12:00:00 AM
11/20/2012 12:00:00 AM
回答2:
Because months have varying lengths, you can't just add a pre-defined number. You have to know which month you are dealing with, and go from there.
If you know that the 1st and the 16th of a month are due dates, then the last payment is December 16th (assuming you are calculating for a calendar year).
回答3:
The basic pairs for semi monthly payments are:
- 1 and 16 (the 1st and 16th day of a month)
- 15 and (2|3)? (the 15th and the last day of the month)
Peek and choose
回答4:
I've recently had the same issue, but I needed to allow any date input. It's a bit of a mess and needs to be refactored, but this is what I came up with so far. February had some problems that I had to hack.
Date returnDate;
if (numberOfPayments % 2 == 0)
{
returnDate = date.AddMonths(numberOfPayments / 2);
if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))//Last day of the month adjustment
{
returnDate = new Date(returnDate.Year, returnDate.Month, DateTime.DaysInMonth(returnDate.Year, returnDate.Month));
}
}
else
{
returnDate = date.Day <= 15 ? date.AddDays(15).AddMonths((numberOfPayments - 1) / 2) : date.AddDays(-15).AddMonths((numberOfPayments + 1) / 2);
if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))//Last day of the month adjustment
{
returnDate = new Date(returnDate.Year, returnDate.Month, 15);
}
else if (date.Month == 2 && date.Day == 14)
{
returnDate = returnDate.AddMonths(-1);
returnDate = new Date(returnDate.Year, returnDate.Month, returnDate.Month == 2 ? 28 : 29);
}
else if (date.Month == 2 && date.Day == 15)
{
returnDate = returnDate.AddMonths(-1);
returnDate = new Date(returnDate.Year, returnDateMonth, DateTime.DaysInMonth(returnDate.Year, returnDate.Month));
}
}
return returnDate;
来源:https://stackoverflow.com/questions/8481517/how-many-days-to-add-for-semi-monthly