我需要在给定日期找到前三个工作日,省略周末和假日。这本身并不是一项艰巨的任务,但似乎我要这么做的方式会过于复杂,所以我想我会首先征求你的意见。
为了让事情变得更有趣,让我们把它作为一个比赛。我提供300作为赏金,无论谁提出最符合本规范的最简洁,最干净的解决方案:
Y-m-d
格式Y-m-d
格式的日期,从最旧到最新排序。额外:
假期数组的一个例子:
$holidays = array(
'2010-01-01',
'2010-01-06',
'2010-04-02',
'2010-04-04',
'2010-04-05',
'2010-05-01',
'2010-05-13',
'2010-05-23',
'2010-06-26',
'2010-11-06',
'2010-12-06',
'2010-12-25',
'2010-12-26'
);
请注意,在实际情况中,假日不是硬编码的,而是来自get_holidays($year)
函数。如果您愿意,可以在答案中包含/使用它。
由于我正在提供赏金,这意味着我至少需要三天时间才能将答案标记为已接受(2天即可添加赏金,1天直至我可以接受)。
注意
如果您使用固定的日期长度(例如86400秒)从白天跳到另一天,则会遇到夏令时问题。请改用strtotime('-1 day', $timestamp)
。
此问题的一个示例:
最终解决方案
这是我最终使用的最终解决方案,改编自Keith Minkler关于使用strtotime
的{{1}}的想法。检测通过计数的方向,如果为负,则向后搜索,并在正数上转发:
last weekday
答案 0 :(得分:10)
这应该可以解决问题:
// Start Date must be in "Y-m-d" Format
function LastThreeWorkdays($start_date) {
$current_date = strtotime($start_date);
$workdays = array();
$holidays = get_holidays('2010');
while (count($workdays) < 3) {
$current_date = strtotime('-1 day', $current_date);
if (in_array(date('Y-m-d', $current_date), $holidays)) {
// Public Holiday, Ignore.
continue;
}
if (date('N', $current_date) < 6) {
// Weekday. Add to Array.
$workdays[] = date('Y-m-d', $current_date);
}
}
return array_reverse($workdays);
}
我在get_holidays()函数中进行了硬编码,但我相信你会得到这个想法并调整它以适应它。其余的都是工作代码。
答案 1 :(得分:8)
您可以在strtotime中使用“last weekday”或“next thursday”等表达式,例如:
function last_working_days($date, $backwards = true)
{
$holidays = get_holidays(date("Y", strtotime($date)));
$working_days = array();
do
{
$direction = $backwards ? 'last' : 'next';
$date = date("Y-m-d", strtotime("$direction weekday", strtotime($date)));
if (!in_array($date, $holidays))
{
$working_days[] = $date;
}
}
while (count($working_days) < 3);
return $working_days;
}
答案 2 :(得分:3)
传递true
作为第二个参数,以及时向前而不是向后。如果你将来想要的话,我还编辑了这个功能,允许超过三天。
function last_workingdays($date, $forward = false, $numberofdays = 3) {
$time = strtotime($date);
$holidays = get_holidays();
$found = array();
while(count($found) < $numberofdays) {
$time -= 86400 * ($forward?-1:1);
$new = date('Y-m-d', $time);
$weekday = date('w', $time);
if($weekday == 0 || $weekday == 6 || in_array($new, $holidays)) {
continue;
}
$found[] = $new;
}
if(!$forward) {
$found = array_reverse($found);
}
return $found;
}
答案 3 :(得分:3)
这是我使用PHP的DateTime类对它的看法。关于假期,考虑到你可以在一年内开始,在另一年结束。
function get_workdays($date, $num = 3, $next = false)
{
$date = DateTime::createFromFormat('Y-m-d', $date);
$interval = new DateInterval('P1D');
$holidays = array();
$res = array();
while (count($res) < $num) {
$date->{$next ? 'add' : 'sub'}($interval);
$year = (int) $date->format('Y');
$formatted = $date->format('Y-m-d');
if (!isset($holidays[$year]))
$holidays[$year] = get_holidays($year);
if ($date->format('N') <= 5 && !in_array($formatted, $holidays[$year]))
$res[] = $formatted;
}
return $next ? $res : array_reverse($res);
}
答案 4 :(得分:1)
这是我的理由:
function business_days($date) {
$out = array();
$day = 60*60*24;
//three back
$count = 0;
$prev = strtotime($date);
while ($count < 3) {
$prev -= $day;
$info = getdate($prev);
$holidays = get_holidays($info['year']);
if ($info['wday'] == 0 || $info['wday'] == 6 || in_array($date,$holidays))
continue;
else {
$out[] = date('Y-m-d',$prev);
$count++;
}
}
$count = 0;
$next = strtotime($date);
while ($count < 3) {
$next += $day;
$info = getdate($next);
$holidays = get_holidays($info['year']);
if ($info['wday']==0 || $info['wday']==6 || in_array($date,$holidays))
continue;
else {
$out[] = date('Y-m-d',$next);
$count++;
}
}
sort($out);
return $out;
}
答案 5 :(得分:1)
编辑:
将86400更改为-1 day
,但我不完全明白这是否真的是一个问题。
对原始功能进行了一些修改,但它几乎相同。
// -----------------------
// Previous 3 working days # this is almost the same that someone already posted
function getWorkingDays($date){
$workdays = array();
$holidays = getHolidays();
$date = strtotime($date);
while(count($workdays) < 3){
$date = strtotime("-1 day", $date);
if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
$workdays[] = date('Y-m-d',$date);
}
krsort($workdays);
return $workdays;
}
// --------------------------------
// Previous and Next 3 working days
function getWorkingDays2($date){
$workdays['prev'] = $workdays['next'] = array();
$holidays = getHolidays();
$date = strtotime($date);
$start_date = $date;
while(count($workdays['prev']) < 3){
$date = strtotime("-1 day", $date);
if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
$workdays['prev'][] = date('Y-m-d',$date);
}
$date = $start_date;
while(count($workdays['next']) < 3){
$date = strtotime("+1 day", $date);
if(date('N',$date) < 6 && !in_array(date('Y-m-d',$date),$holidays))
$workdays['next'][] = date('Y-m-d',$date);
}
krsort($workdays['prev']);
return $workdays;
}
function getHolidays(){
$holidays = array(
'2010-01-01', '2010-01-06',
'2010-04-02', '2010-04-04', '2010-04-05',
'2010-05-01', '2010-05-13', '2010-05-23',
'2010-06-26',
'2010-11-06',
'2010-12-06', '2010-12-25', '2010-12-26'
);
return $holidays;
}
echo '<pre>';
print_r( getWorkingDays( '2010-04-04' ) );
print_r( getWorkingDays2( '2010-04-04' ) );
echo '</pre>';
<强>输出:强>
Array
(
[2] => 2010-03-30
[1] => 2010-03-31
[0] => 2010-04-01
)
Array
(
[next] => Array
(
[0] => 2010-04-06
[1] => 2010-04-07
[2] => 2010-04-08
)
[prev] => Array
(
[2] => 2010-03-30
[1] => 2010-03-31
[0] => 2010-04-01
)
)
答案 6 :(得分:1)
我正在添加另一个答案,因为它采用了与我之前发布的方法不同的方法:
function getWorkDays($date){
list($year,$month,$day) = explode('-',$date);
$holidays = getHolidays();
$dates = array();
while(count($dates) < 3){
$newDate = date('Y-m-d',mktime(0,0,0,$month,--$day,$year));
if(date('N',strtotime($newDate)) < 6 && !in_array($newDate,$holidays))
$dates[] = $newDate;
}
return array_reverse($dates);
}
print_r(getWorkDays('2010-12-08'));
输出:
Array
(
[0] => 2010-12-02
[1] => 2010-12-03
[2] => 2010-12-07
)
答案 7 :(得分:0)
你的意思就像Excel中的WORKDAY()函数
如果您查看PHPExcel中的WORKDAYS函数,您将找到如何编写此类函数的示例
答案 8 :(得分:0)
试试这个(公平警告 - 我无权测试这个,所以请更正任何语法错误)。
function LastThreeWorkdays($start_date) {
$startdateseed = strtotime($start_date);
$workdays = array();
$holidays = get_holidays('2010');
for ($counter = -1; $counter >= -10; $counter--)
if (date('N', $current_date = strtotime($counter.' day', $startdateseed)) < 6) $workdays[] = date('Y-m-d', $currentdate);
return array_slice(array_reverse(array_diff($workdays, $holidays)), 0, 3);
}
基本上创建一个“大块”日期,然后使用数组差异从中删除假期。仅返回顶部(最后)三个项目。显然,与以前的答案相比,它需要更多的存储空间和计算时间,但代码要短得多。
可以调整“块”大小以进一步优化。理想情况下,这将是连续假期的最大数量加上2加3,但这假设是真实的假日场景(整周假期不可能,等等)。
代码也可以“展开”,以使一些技巧更容易阅读。总的来说,一些PHP功能更好一些 - 虽然可以与其他想法结合使用。
答案 9 :(得分:0)
/**
* @param $currentdate like 'YYYY-MM-DD'
* @param $n number of workdays to return
* @param $direction 'previous' or 'next', default is 'next'
**/
function adjacentWorkingDays($currentdate, $n, $direction='next') {
$sign = ($direction == 'previous') ? '-' : '+';
$workdays = array();
$holidays = get_holidays();
$i = 1;
while (count($workdays) < $n) {
$dateinteger = strtotime("{$currentdate} {$sign}{$i} days");
$date = date('Y-m-d', $dateinteger);
if (!in_array($date, $holidays) && date('N', $dateinteger) < 6) {
$workdays[] = $date;
}
$i++;
}
return $workdays;
}
// you pass a year into get_holidays, make sure folks
// are accounting for the fact that adjacent holidays
// might cross a year boundary
function get_holidays() {
$holidays = array(
'2010-01-01',
'2010-01-06',
'2010-04-02',
'2010-04-04',
'2010-04-05',
'2010-05-01',
'2010-05-13',
'2010-05-23',
'2010-06-26',
'2010-11-06',
'2010-12-06',
'2010-12-25',
'2010-12-26'
);
return $holidays;
}
在这些函数中,我们使用adjacentWorkingDays()
函数:
// next $n working days, in ascending order
function nextWorkingDays($date, $n) {
return adjacentWorkingDays($date, $n, 'next');
}
// previous $n workind days, in ascending order
function previousWorkingDays($date, $n) {
return array_reverse(adjacentWorkingDays($date, $n, 'previous'));
}
这是测试它:
print "<pre>";
print_r(nextWorkingDays('2010-06-24', 3));
print_r(previousWorkingDays('2010-06-24', 3));
print "<pre>";
结果:
Array
(
[0] => 2010-06-25
[1] => 2010-06-28
[2] => 2010-06-29
)
Array
(
[0] => 2010-06-21
[1] => 2010-06-22
[2] => 2010-06-23
)
答案 10 :(得分:0)
这是我的提交;)
/**
* Helper function to handle year overflow
*/
function isHoliday($date) {
static $holidays = array(); // static cache
$year = date('Y', $date);
if(!isset($holidays["$year"])) {
$holidays["$year"] = get_holidays($year);
}
return in_array(date('Y-m-d', $date), $holidays["$year"]);
}
/**
* Returns adjacent working days (by default: the previous three)
*/
function adjacentWorkingDays($start_date, $limit = 3, $direction = 'previous') {
$current_date = strtotime($start_date);
$direction = ($direction === 'next') ? 'next' : 'previous'; // sanity
$workdays = array();
// no need to verify the count before checking the first day.
do {
// using weekday here skips weekends.
$current_date = strtotime("$direction weekday", $current_date);
if (!isHoliday()) {
// not a public holiday.
$workdays[] = date('Y-m-d', $current_date);
}
} while (count($workdays) < $limit)
return array_reverse($workdays);
}
答案 11 :(得分:0)
这是我的看法。如果您在年初输入日期,则此功能(与大多数其他发布的功能不同)不会失败。如果您只在一年内调用get_holidays
函数,则生成的数组可能包含上一年度假的日期。如果我们回到前一年,我的解决方案将再次致电get_holidays
。
function get_working_days($date)
{
$date_timestamp = strtotime($date);
$year = date('Y', $date_timestamp);
$holidays = get_holidays($year);
$days = array();
while (count($days) < 3)
{
$date_timestamp = strtotime('-1 day', $date_timestamp);
$date = date('Y-m-d', $date_timestamp);
if (!in_array($date, $holidays) && date('N', $date_timestamp) < 6)
$days[] = $date;
$year2 = date('Y', $date_timestamp);
if ($year2 != $year)
{
$holidays = array_merge($holidays, get_holidays($year2));
$year = $year2;
}
}
return $days;
}