When building a calendar or date-picker utility in terminal-based C++, determining the exact day of the week (Monday through Sunday) for any given date is a fundamental requirement. If you are struggling with complex date arithmetic or wrestling with ctime, you are not alone.

In this guide, we will explore the best ways to determine weekdays in C++: the modern, effortless C++20 <chrono> approach, followed by the classic std::mktime solution for older standards.

The Problem with Calculating Date Differences

A common pitfall is fetching the current date via localtime() and attempting to calculate relative differences between the current year/month and the user's input. This approach quickly becomes brittle due to leap years, daylight saving time (DST), and varying month lengths.

Instead, the simplest approach is to populate a date structure directly with the target year, month, and day, then ask the standard library to resolve the day of the week.

Method 1: The Modern C++20 Way (Recommended)

C++20 introduced extensive calendar extensions to the <chrono> library. It is type-safe, eliminates manual conversions (like year - 1900), and handles leap years automatically.

#include <iostream>
#include <chrono>
#include <format> // Or use std::cout with older stream support

int main() {
    int year_input = 2026;
    int month_input = 10; // October

    std::chrono::year y{year_input};
    std::chrono::month m{static_cast<unsigned>(month_input)};
    std::chrono::year_month ym{y, m};

    // Get the last day of the month accurately (handles leap years)
    std::chrono::year_month_day_last ymdl{y, std::chrono::month_day_last{m}};
    unsigned int total_days = static_cast<unsigned>(ymdl.day());

    std::cout << "Output: " << year_input << "-" << month_input << "\n";

    for (unsigned int d = 1; d <= total_days; ++d) {
        std::chrono::year_month_day ymd{y, m, std::chrono::day{d}};
        std::chrono::weekday wd{std::chrono::sys_days{ymd}};

        // Using std::format (C++20)
        std::cout << d << " " << std::format("{:%A}", wd) << "\n";
    }

    return 0;
}

Why this works:

  • std::chrono::sys_days automatically casts a date into a count of days since the Unix epoch.
  • Casting that back to std::chrono::weekday computes the correct weekday instantly.
  • std::format("{:%A}", wd) prints the full weekday name (e.g., "Thursday"). Use "{%a}" for abbreviated names ("Thu").

Method 2: Using Classic C++ (<ctime> and mktime)

If your compiler doesn't support C++20 yet, you can fix your original code using std::mktime. When you populate a std::tm structure and call mktime, the library automatically calculates and populates the tm_wday field (where 0 = Sunday, 1 = Monday, etc.).

#include <iostream>
#include <ctime>
#include <vector>
#include <string>

const std::vector<std::string> WEEKDAYS = {
    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};

// Helper to determine the number of days in a month
int getDaysInMonth(int year, int month) {
    if (month == 2) {
        bool isLeap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
        return isLeap ? 29 : 28;
    }
    if (month == 4 || month == 6 || month == 9 || month == 11) {
        return 30;
    }
    return 31;
}

int main() {
    int year = 2026;
    int month = 10; // 1-based (1 = Jan, 10 = Oct)

    int days = getDaysInMonth(year, month);

    for (int day = 1; day <= days; ++day) {
        std::tm date = {};
        date.tm_year = year - 1900; // Years since 1900
        date.tm_mon  = month - 1;   // Months since January (0-11)
        date.tm_mday = day;         // Day of the month (1-31)
        date.tm_isdst = -1;         // Let system determine DST

        // mktime normalizes the struct and computes tm_wday
        if (std::mktime(&date) != -1) {
            std::cout << day << " " << WEEKDAYS[date.tm_wday] << "\n";
        }
    }

    return 0;
}

Important Details for std::tm:

  • tm_year: Counted as years since 1900 (e.g., 2026 - 1900 = 126).
  • tm_mon: Zero-indexed (0 for January, 9 for October).
  • tm_isdst: Always set to -1 before calling mktime() so the C runtime determines whether Daylight Saving Time is in effect.

Summary

  • Avoid computing manual date deltas against time(nullptr).
  • If using C++20 or newer, prefer std::chrono date types for a clean, type-safe API.
  • If using C++11 through C++17, populate a zero-initialized std::tm struct and invoke std::mktime() to obtain tm_wday.