Skip to content

📅 Задачи, свързани с дати и време в C++

Работата с дати и време е често срещана задача в програмирането. Езикът C++ предлага стандартни библиотеки и функции за работа с времеви данни.

🔧 Основни функции за работа с дати и време

C++ използва библиотеката <ctime> за обработка на дати и време. Тази библиотека предоставя функции и структури за представяне, манипулиране и извеждане на времеви стойности.

📚 Основни структури и типове

  • time_t
    Представлява времето като брой секунди от 1 януари 1970 г. (Epoch Time).

  • tm
    Структура, която съдържа подробна информация за дата и време:

  • tm_sec: секунди (0-59)
  • tm_min: минути (0-59)
  • tm_hour: часове (0-23)
  • tm_mday: ден от месеца (1-31)
  • tm_mon: месец (0-11, където 0 е януари)
  • tm_year: година (годината минус 1900)
  • tm_wday: ден от седмицата (0-6, където 0 е неделя)
  • tm_yday: ден от годината (0-365)

🕒 Извличане на текущата дата и време

Код за текущото време:

#include <iostream>
#include <ctime>
using namespace std;

int main() {
    time_t now = time(0); // Вземане на текущото време
    tm *localTime = localtime(&now); // Преобразуване в локално време

    cout << "Текуща дата и време: "
         << localTime->tm_mday << "/"
         << (localTime->tm_mon + 1) << "/"
         << (1900 + localTime->tm_year) << " "
         << localTime->tm_hour << ":"
         << localTime->tm_min << ":"
         << localTime->tm_sec << endl;

    return 0;
}

⏳ Преобразуване на време в различни формати

C++ позволява форматиране на времето с помощта на функцията strftime.

Пример:

#include <iostream>
#include <ctime>
using namespace std;

int main() {
    time_t now = time(0);
    tm *localTime = localtime(&now);

    char buffer[80];
    strftime(buffer, sizeof(buffer), "%d-%m-%Y %H:%M:%S", localTime);

    cout << "Форматирано време: " << buffer << endl;

    return 0;
}

📆 Разлика между дати

Можете да изчислите разликата между две дати с помощта на difftime.

Пример:

#include <iostream>
#include <ctime>
using namespace std;

int main() {
    time_t now = time(0);
    time_t future = now + (7 * 24 * 60 * 60); // След 7 дни

    double diff = difftime(future, now);
    cout << "Разлика в секунди: " << diff << endl;

    return 0;
}

🔄 Работа със системно време и таймери

Можете да използвате функцията clock() за измерване на изминало процесорно време.

Пример за таймер:

#include <iostream>
#include <ctime>
using namespace std;

int main() {
    clock_t start = clock();

    // Симулираме изчисление
    for (int i = 0; i < 1e7; ++i);

    clock_t end = clock();
    double elapsed = double(end - start) / CLOCKS_PER_SEC;

    cout << "Изминало време: " << elapsed << " секунди." << endl;

    return 0;
}

🗓️ Бележки за работа с дати и време

  • UTC и локално време
  • gmtime() връща времето в UTC.
  • localtime() връща локалното време.

  • Форматиране на времето

  • Използвайте шаблони като %d, %m, %Y, %H, %M, %S в strftime.

  • Грешки при работа с време

  • Уверете се, че обработвате правилно стойностите за месеци (0-11) и години (минус 1900).

📊 Типични алгоритмични задачи с дати

1. Проверка дали година е високосна

Високосна година е: - Делима на 4 и не е делима на 100, или - Делима на 400

bool isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

Примери: - 2000 е високосна (делима на 400) - 1900 не е високосна (делима на 100, но не на 400) - 2024 е високосна (делима на 4, но не на 100)

2. Брой дни в месец

int daysInMonth(int month, int year) {
    if (month == 2) {
        return isLeapYear(year) ? 29 : 28;
    }
    // Април, юни, септември, ноември имат 30 дни
    if (month == 4 || month == 6 || month == 9 || month == 11) {
        return 30;
    }
    return 31; // Останалите имат 31
}

3. Ден от седмицата (Формула на Зелер)

Формулата на Зелер позволява да се изчисли деня от седмицата за произволна дата:

int dayOfWeek(int day, int month, int year) {
    if (month < 3) {
        month += 12;
        year--;
    }
    int q = day;
    int m = month;
    int k = year % 100;
    int j = year / 100;

    int h = (q + ((13 * (m + 1)) / 5) + k + (k / 4) + (j / 4) - 2 * j) % 7;

    // h = 0: Събота, 1: Неделя, ..., 6: Петък
    return h;
}

4. Разлика в дни между две дати

За да намерим броя дни между две дати, можем да използваме формулата на Юлианския ден:

int julianDay(int day, int month, int year) {
    int a = (14 - month) / 12;
    int y = year + 4800 - a;
    int m = month + 12 * a - 3;

    return day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
}

int daysBetween(int d1, int m1, int y1, int d2, int m2, int y2) {
    return abs(julianDay(d2, m2, y2) - julianDay(d1, m1, y1));
}

5. Добавяне на дни към дата

void addDays(int &day, int &month, int &year, int daysToAdd) {
    while (daysToAdd > 0) {
        int daysInCurrentMonth = daysInMonth(month, year);
        if (day + daysToAdd <= daysInCurrentMonth) {
            day += daysToAdd;
            break;
        }
        daysToAdd -= (daysInCurrentMonth - day + 1);
        day = 1;
        month++;
        if (month > 12) {
            month = 1;
            year++;
        }
    }
}

🎯 Практически задачи

Задача 1: Изчисляване на възраст

struct Date {
    int day, month, year;
};

int calculateAge(Date birth, Date current) {
    int age = current.year - birth.year;

    // Ако текущият месец е преди месеца на раждане
    // или е същият месец но деня е преди деня на раждане
    if (current.month < birth.month || 
        (current.month == birth.month && current.day < birth.day)) {
        age--;
    }

    return age;
}

Задача 2: Намиране на петък 13-ти

void findFriday13(int year) {
    for (int month = 1; month <= 12; month++) {
        if (dayOfWeek(13, month, year) == 6) { // 6 = Петък в Зелер
            cout << "Петък 13-ти: " << month << "/" << year << endl;
        }
    }
}

Задача 3: Брой работни дни между две дати

int workingDays(int d1, int m1, int y1, int d2, int m2, int y2) {
    int count = 0;
    int day = d1, month = m1, year = y1;

    while (day != d2 || month != m2 || year != y2) {
        int dow = dayOfWeek(day, month, year);
        // Понеделник (1) до Петък (5) са работни дни
        if (dow >= 1 && dow <= 5) {
            count++;
        }

        // Преминаваме към следващия ден
        day++;
        if (day > daysInMonth(month, year)) {
            day = 1;
            month++;
            if (month > 12) {
                month = 1;
                year++;
            }
        }
    }

    return count;
}

🔬 Модерен C++ (C++20 chrono)

C++20 въведе по-мощна библиотека <chrono> за работа с дати и време:

#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;

int main() {
    // Текуща дата
    auto now = system_clock::now();
    auto today = floor<days>(now);

    year_month_day ymd{today};

    cout << "Година: " << (int)ymd.year() << endl;
    cout << "Месец: " << (unsigned)ymd.month() << endl;
    cout << "Ден: " << (unsigned)ymd.day() << endl;

    // Операции с дати
    auto tomorrow = ymd + days{1};
    auto nextWeek = ymd + weeks{1};

    return 0;
}

⚠️ Важни забележки

  1. Часови зони: При работа с глобални системи винаги използвайте UTC вместо локално време
  2. Високосни секунди: При високо прецизни изчисления, имайте предвид че високосните секунди могат да повлияят на резултатите
  3. Overflow: При изчисления с дати далеч в бъдещето или миналото, внимавайте за препълване на time_t
  4. Валидация: Винаги проверявайте дали въведената дата е валидна (напр. 31 февруари не съществува)

🧩 Олимпиадни задачи

  1. Намиране на n-тия ден от годината: Дадена е дата (ден, месец, година), намерете кой ден от годината е това (1-365/366)
  2. Следващ палиндромен ден: Намерете следващата дата, която е палиндром във формат DDMMYYYY
  3. Брой уикенди в интервал: Дадени са две дати, намерете броя на съботите и неделите между тях
  4. Най-близкият понеделник: Дадена е произволна дата, намерете следващия понеделник

🏁 Заключение

Работата с дати и време в C++ изисква разбиране както на библиотеките, така и на математическите алгоритми. Високосните години, различният брой дни в месеците и деновете от седмицата правят задачите предизвикателни, но с правилните функции и формули, можете да решавате сложни проблеми ефективно. Упражнявайте се с различни задачи, за да развиете интуиция при работа с времеви данни! 🚀