2

我正在开发一个付款计划程序,它n根据任意开始日期和一组频率(每天、每周、每月等)中的一个来计划付款,并且正在寻找一种通用算法来这样做。

我尝试了一种蛮力的方法来做到这一点,通过调整频率并根据需要添加一定数量的天、周、月。这适用于大多数目的。

它失败的地方是当任意开始日期在一个月的 28 日之后,并且频率介于每月和每年之间,特别是对于像“每个月的第一天”和“每个月的最后一个”这样的频率。因为并非所有月份都出现第 29、30 和 31 天,所以添加一个月date('2013-10-31')->addMonth(1)会产生不可预知的结果。就像添加月份一样date('2014-01-31')->addDays(30),再次,由于二月不必要地短。

这个问题是否有一个通用的解决方案,没有我需要在任何给定月份移动任何给定频率所需的极其复杂的案例?

PHP 的奖励积分,但如果需要我可以翻译。

4

2 回答 2

2

由于月份长度不同而引起的“加一个月”等烦恼确实令人恼火。

如果您的 PHP >= 5.2,解决方案是DateTime 类

虽然使用这个类来获得完全控制很简单,但它并非完全微不足道。

这是添加一个月的正确代码的一个版本。

// Variables defining the start date
// Example only - this could be any valid date
$year = '2013';
$month = '01';
$day = '31';

// set to the desired starting date and time
$the_date = new DateTime($year . '-' . $month . '-' . $day);

// Jump to the first day of this month
$the_date->modify("first day of this month");

// add 14 days, so we'll land on the 15th
$the_date->add(new DateInterval("P14D"));

// add 1 month - guaranteed to work!
$the_date->add(new DateInterval("P1M"));

// calculate how many days to add to 15 to get back to the **day** we started with...
// (as an integer, regardless of whether it is a valid day of the current month)
$number_days_to_add_back = intval($day) - 15;

// determine the last day of the month stored in $the_date
$test_last_date = clone $the_date;
$test_last_date->modify("last day of this month");
$day_last = $test_last_date->format('j'); // This provides the day, 01-31

// Test if adding $number_days_to_add_back runs past
// the end of the month; if so, adjust it so it won't run past
// the last day of the month
if (15 + $number_days_to_add_back > intval($day_last)) {
    $number_days_to_add_back = intval($day_last) - 15;
}

// Now make the final adjustment
$the_date->modify("" . $number_days_to_add_back . " day");

// Test it - a month has been added
$test = date_format($the_date, 'Y-m-d');
于 2013-11-01T02:36:18.287 回答
0

首先,您需要定义希望它如何工作。这是一个业务逻辑问题,更不用说技术问题了。“一个月”有多长?您的意思是“一个月”作为大约 30 天的时间跨度(那么到底有多长?)还是“+ 一个月”的意思是“下个月的同一天”?一旦您定义了“第 31 + 1 个月”应该如何工作,只需正确实施它即可。

我的建议是“+1 月”的意思是“将月数加一,保持天数不变,除非该天在该月不存在,在这种情况下使用该月的最后一天”。可以使用这样的东西来实现:

$date      = mktime(0, 0, 0, 1, 31);  // midnight Jan 31st
$nextMonth = mktime(0, 0, 0, date('n', $date) + 1, 1, date('Y', $date));  // 1st of next month
$newDate   = mktime(0, 0, 0,
                    date('n', $nextMonth),
                    min(date('t', $nextMonth), date('j', $date)),
                    date('Y', $nextMonth));

它不漂亮,但日期/时间计算很少,特别是如果操作的定义含糊不清。

于 2013-11-01T08:28:26.593 回答