# How Business Days Are Calculated for Review Due Dates in the GitHub-Asana Request Review Action

> Understand how business days are calculated for GitHub-Asana review due dates. Learn about the NextBusinessDay function, weekend skips, and holiday exclusions in this technical breakdown.

- Repository: [Keita Kitamura/github-asana-request-review-action](https://github.com/keitap/github-asana-request-review-action)
- Tags: internals
- Published: 2026-03-05

---

**The action determines a review sub-task's due date by adding a configurable number of business days to the current timestamp, skipping weekends and user-defined holidays using the `NextBusinessDay` function implemented in [`date.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/date.go).**

The `keitap/github-asana-request-review-action` automatically creates Asana sub-tasks when GitHub pull requests require review. A critical feature is its ability to set realistic due dates based on actual working days rather than calendar days. This article examines how business days are calculated for review due dates within the action's source code, detailing the algorithm that powers this scheduling logic.

## Algorithm Overview

The core calculation resides in the `NextBusinessDay` function defined in [[`date.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/date.go)](https://github.com/keitap/github-asana-request-review-action/blob/main/date.go). The algorithm accepts three inputs and returns a `time.Time` representing the target business day.

### Input Parameters

- **`n`** (`int`): The number of business days to add, configured via the `due_date` setting in your workflow YAML.
- **`base`** (`time.Time`): The starting timestamp, typically `time.Now()`.
- **`holidays`** (`map[string]bool`): A map of `YYYY-MM-DD` strings representing non-working days (e.g., public holidays).

### Holiday Detection Logic

The helper function `isHoliday` determines whether a date should be skipped. It returns `true` when either condition is met:

- The date exists in the `holidays` map, **or**
- The date falls on `time.Saturday` or `time.Sunday`.

### Step-by-Step Execution

The algorithm executes in two distinct phases:

**1. Normalize to the First Business Day**

The function first ensures the calculation starts on an actual business day. If the `base` date is a weekend or listed holiday, the algorithm advances day-by-day until finding a valid business day:

```go
for {
    if isHoliday(base, holidays) {
        base = base.AddDate(0, 0, 1)
        i = 1
    } else {
        break
    }
}

```

Here, `i` is initialized to 1 to indicate the following day when skipping occurs.

**2. Count Business Days**

After normalization, the function increments a counter only on valid business days, advancing the calendar day-by-day until reaching the requested count:

```go
for {
    if !isHoliday(base, holidays) {
        i++
    }
    if n < i {
        return base
    }
    base = base.AddDate(0, 0, 1)
}

```

The loop exits when the counter `i` exceeds the requested number `n`, returning the current `base` as the due date.

### Zero-Day Offset Handling

When `n` equals 0, the function returns the most recent business day on or before the original `base` date. Due to the initial normalization loop, a weekend or holiday start date gets bumped forward to the next business day, and since `i` (set to 1) immediately exceeds `n` (0), that date is returned instantly.

## Core Implementation in date.go

The `NextBusinessDay` function in [`date.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/date.go) implements the complete business day calculation logic. This file also contains the `isHoliday` helper that checks both the custom holidays map and the day of week. The function uses `time.AddDate(0, 0, 1)` for day advancement, ensuring proper handling of month and year boundaries without manual calendar math.

## Integration with Asana Task Creation

The calculated date is applied when creating review sub-tasks in [[`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go)](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go). The handler retrieves configuration values from the `Config` struct (defined in [[`config.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/config.go)](https://github.com/keitap/github-asana-request-review-action/blob/main/config.go)) and passes them to the business day function:

```go
// In handler.go
due := asana.Date(NextBusinessDay(h.conf.DueDate, time.Now(), h.conf.Holidays))

```

- `h.conf.DueDate` provides the integer offset (default 1)
- `h.conf.Holidays` contains the map of excluded dates loaded from the action's configuration

## Configuration Options

According to the repository's [[`README.md`](https://github.com/keitap/github-asana-request-review-action/blob/main/README.md)](https://github.com/keitap/github-asana-request-review-action/blob/main/README.md), users control the calculation through two configuration fields:

- **`due_date`**: Integer specifying how many business days to add to the current date. Defaults to 1 if unspecified.
- **`holidays`**: Array of `YYYY-MM-DD` strings representing additional non-working days beyond weekends.

Example configuration:

```yaml
- uses: keitap/github-asana-request-review-action@main
  with:
    due_date: 3
    holidays: |
      2024-12-25
      2024-12-26
      2025-01-01

```

## Practical Usage Example

You can leverage the `NextBusinessDay` function directly in custom Go applications importing this action:

```go
package main

import (
	"fmt"
	"time"
	
	"github.com/keitap/github-asana-request-review-action"
)

func main() {
	holidays := map[string]bool{
		"2024-12-25": true,
		"2024-01-01": true,
	}
	
	// Calculate 3 business days from now
	due := githubasana.NextBusinessDay(3, time.Now(), holidays)
	fmt.Println("Review due:", due.Format("2006-01-02"))
}

```

This returns a timestamp representing the third business day after today, excluding weekends and the specified Christmas and New Year's dates.

## Summary

- The **business day calculation** resides in the `NextBusinessDay` function inside [`date.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/date.go), which adds configurable working days to the current date while excluding weekends and custom holidays.
- The **algorithm** first normalizes the start date to the next valid business day, then iterates through the calendar counting only valid workdays until reaching the requested offset.
- **Holiday detection** combines a user-provided `YYYY-MM-DD` map with standard weekend checks (`time.Saturday` and `time.Sunday`).
- When the offset is **zero**, the function returns the most recent business day on or before the start date (after normalizing from weekends/holidays).
- The handler in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go) integrates this calculation when setting Asana sub-task due dates, using configuration from [`config.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/config.go) for the offset and holiday list.

## Frequently Asked Questions

### How does the action handle weekends when calculating review due dates?

The `isHoliday` helper function in [`date.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/date.go) automatically identifies Saturdays and Sundays using Go's `time.Saturday` and `time.Sunday` constants. These days are skipped during the counting loop, ensuring the final due date always falls on a weekday unless explicitly defined as a holiday.

### Can I configure specific public holidays to be excluded from the calculation?

Yes. The action accepts a `holidays` configuration parameter containing a list of `YYYY-MM-DD` formatted dates. These are stored in a `map[string]bool` and checked by the `isHoliday` function alongside weekend detection, allowing you to exclude any non-working days specific to your organization or country.

### What happens if I set the due_date to 0 in the configuration?

When `n` equals 0, the `NextBusinessDay` function returns the most recent business day on or before the current date. If today is a weekend or holiday, the normalization loop first advances to the next business day, then immediately returns that date because the internal counter (initialized to 1) exceeds the target of 0.

### Where in the codebase is the due date actually assigned to the Asana task?

The due date assignment occurs in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go), specifically where the handler constructs the Asana sub-task. It calls `NextBusinessDay` with the configured offset and holiday map, converts the result using `asana.Date()`, and attaches this timestamp to the newly created review request task.