You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP中如何排除非工作时间、周末及节假日计算SLA与任务耗时?

Great question! Calculating SLA in PHP while excluding weekends, holidays, and off-hours is a common but tricky task—let’s break this down into manageable steps so you can implement it reliably.

Core Approach

The key is to split the task timeline into chunks and only count time that falls within:

  • A weekday (Monday-Friday)
  • The defined work window (9:00 AM to 5:00 PM)
  • Non-holiday dates

We’ll build helper functions to validate each condition, then combine them to calculate total valid SLA time.

Step 1: Define Base Configuration

First, set up your static rules as reusable variables (you can move these to a config file later for easier maintenance):

// Work hours: 9 AM to 5 PM
$workStartHour = 9;
$workEndHour = 17;

// Weekend days (PHP's date('w') returns 0 for Sunday, 6 for Saturday)
$weekendDays = [0, 6];

// Custom holiday list (format: YYYY-MM-DD)
$holidays = [
    '2024-01-01',   // New Year's Day
    '2024-02-10',   // Lunar New Year
    // Add more holidays as needed
];
Step 2: Helper Function - Check for Valid Workday

This function verifies if a given date is neither a weekend nor a holiday:

function isWorkDay(DateTime $date): bool {
    global $weekendDays, $holidays;
    
    // Check if it's a weekend
    $dayOfWeek = (int)$date->format('w');
    if (in_array($dayOfWeek, $weekendDays)) {
        return false;
    }
    
    // Check if it's a pre-defined holiday
    $dateString = $date->format('Y-m-d');
    if (in_array($dateString, $holidays)) {
        return false;
    }
    
    return true;
}
Step 3: Calculate Daily Valid Work Duration

For a single day, compute how much time falls within the work window (if it’s a valid workday):

function getDailyValidSeconds(DateTime $periodStart, DateTime $periodEnd): int {
    global $workStartHour, $workEndHour;
    
    // Skip non-workdays entirely
    if (!isWorkDay($periodStart)) {
        return 0;
    }
    
    // Create timestamps for the work window boundaries on this day
    $dailyWorkStart = (clone $periodStart)->setTime($workStartHour, 0, 0);
    $dailyWorkEnd = (clone $periodStart)->setTime($workEndHour, 0, 0);
    
    // Find the overlap between the task's time on this day and the work window
    $actualStart = max($periodStart, $dailyWorkStart);
    $actualEnd = min($periodEnd, $dailyWorkEnd);
    
    // No overlap means no valid time for this day
    if ($actualEnd <= $actualStart) {
        return 0;
    }
    
    // Return total valid seconds for the day
    return $actualEnd->getTimestamp() - $actualStart->getTimestamp();
}
Step 4: Total SLA Duration Calculation

This is the main function that iterates through each day of the task timeline and sums up valid time:

function calculateTotalSlaSeconds(DateTime $taskStart, DateTime $taskEnd): int {
    global $workStartHour, $workEndHour;
    
    $totalValidSeconds = 0;
    $currentDay = (clone $taskStart)->setTime(0, 0, 0);
    $endDay = (clone $taskEnd)->setTime(0, 0, 0);
    
    // Calculate valid time for the first partial day
    $totalValidSeconds += getDailyValidSeconds($taskStart, (clone $taskStart)->setTime(23, 59, 59));
    
    // If task starts and ends on the same day, we're done here
    if ($currentDay == $endDay) {
        return $totalValidSeconds;
    }
    
    // Calculate valid time for full middle days
    $currentDay->modify('+1 day');
    while ($currentDay < $endDay) {
        if (isWorkDay($currentDay)) {
            // Full workday seconds: (17-9)*3600 = 28800
            $totalValidSeconds += ($workEndHour - $workStartHour) * 3600;
        }
        $currentDay->modify('+1 day');
    }
    
    // Calculate valid time for the last partial day
    $totalValidSeconds += getDailyValidSeconds((clone $taskEnd)->setTime(0, 0, 0), $taskEnd);
    
    return $totalValidSeconds;
}
Step 5: Usage Example

Test the function with a real-world scenario:

// Task starts on Friday 4:30 PM, ends on Monday 10:00 AM
$taskStart = new DateTime('2024-05-10 16:30:00', new DateTimeZone('Asia/Shanghai'));
$taskEnd = new DateTime('2024-05-13 10:00:00', new DateTimeZone('Asia/Shanghai'));

$totalSeconds = calculateTotalSlaSeconds($taskStart, $taskEnd);

// Convert seconds to human-readable format
$hours = floor($totalSeconds / 3600);
$minutes = floor(($totalSeconds % 3600) / 60);

echo "Total valid SLA time: {$hours} hours {$minutes} minutes";
// Output: Total valid SLA time: 1 hours 30 minutes
Pro Tips for Improvement
  • Dynamic Holidays: Store holidays in a database instead of hardcoding, so you can update them without changing code.
  • OOP Refactor: Wrap all this logic into a SlaCalculator class to avoid global variables and make it reusable across your app.
  • Timezone Consistency: Always specify a timezone when creating DateTime objects to avoid unexpected bugs.

内容的提问来源于stack exchange,提问作者Abhi_J

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:54:35