تمرین و پروژه‌های پایتون

ساخت برنامه محاسبه سن با پایتون؛ محاسبه دقیق سال، ماه و روز با datetime

ساخت برنامه محاسبه سن با پایتون و datetime

🗓 آخرین به‌روزرسانی:

ساخت برنامه محاسبه سن با پایتون یکی از پروژه‌های ساده اما بسیار مناسب برای تمرین کار با تاریخ، تابع، شرط، حلقه و مدیریت ورودی است. در این آموزش فقط اختلاف سال تولد با سال جاری را حساب نمی‌کنیم؛ برنامه‌ای می‌سازیم که تاریخ تولد را دریافت کند، سن را به‌صورت سال، ماه و روز محاسبه کند، تاریخ آینده و ورودی نامعتبر را تشخیص دهد و تعداد روزهای باقی‌مانده تا تولد بعدی را هم نمایش دهد.

این مقاله پنجمین پروژه از فاز اول دسته تمرین و پروژه‌های پایتون است. اگر پروژه‌های قبلی را انجام داده باشید، می‌توانید از تجربه کار با ورودی، تابع و مدیریت خطا در برنامه تبدیل واحد با پایتون و تولیدکننده رمز عبور با پایتون استفاده کنید.

در این پروژه چه چیزی می‌سازیم؟

  • دریافت تاریخ تولد با فرمت مشخص
  • تبدیل رشته به تاریخ واقعی
  • محاسبه سن به‌صورت سال، ماه و روز
  • محاسبه تعداد روزهای باقی‌مانده تا تولد بعدی
  • مدیریت تاریخ نامعتبر
  • جلوگیری از پذیرش تاریخ تولد در آینده
  • مدیریت تاریخ ۲۹ فوریه در سال‌های غیرکبیسه
  • امکان محاسبه سن چند نفر بدون اجرای مجدد فایل

پیش‌نیازهای پروژه

در این پروژه به نصب هیچ کتابخانه خارجی نیاز نداریم. از ماژول‌های استاندارد datetime و calendar استفاده می‌کنیم.

آشنایی با تابع input در پایتون، شرط‌های if و else و مدیریت خطا در پایتون کمک می‌کند منطق پروژه را راحت‌تر دنبال کنید.

فایلی با نام زیر بسازید:

age_calculator.py

سپس آن را اجرا کنید:

python age_calculator.py

چرا محاسبه سن فقط با تفریق سال‌ها کافی نیست؟

کدی مثل زیر ظاهراً سن را محاسبه می‌کند:

age = current_year - birth_year

اما این روش یک ایراد مهم دارد. اگر تولد امسال هنوز نرسیده باشد، سن واقعی یک سال کمتر است. علاوه بر این، با این روش ماه و روز سن مشخص نمی‌شود. برای یک پروژه واقعی باید تاریخ کامل تولد و تاریخ امروز با هم مقایسه شوند.

مرحله اول: کار با date در پایتون

ابتدا کلاس date را وارد می‌کنیم:

from datetime import date

تاریخ امروز از طریق تابع زیر دریافت می‌شود:

today = date.today()
print(today)

خروجی به شکل سال-ماه-روز خواهد بود و مقدار آن در هر روز به‌صورت خودکار تغییر می‌کند.

مرحله دوم: تبدیل ورودی کاربر به تاریخ

از کاربر می‌خواهیم تاریخ تولد را با فرمت YYYY-MM-DD وارد کند. برای مثال:

1998-11-24

برای تبدیل این رشته به شیء تاریخ از date.fromisoformat() استفاده می‌کنیم:

birth_date = date.fromisoformat(
    "1998-11-24"
)

اگر فرمت یا خود تاریخ اشتباه باشد، برای مثال 2020-13-50، پایتون ValueError ایجاد می‌کند. این رفتار برای اعتبارسنجی ورودی بسیار مفید است.

ساخت تابع دریافت تاریخ تولد

def get_birth_date():
    while True:
        value = input(
            "تاریخ تولد را با فرمت YYYY-MM-DD وارد کنید: "
        ).strip()

        try:
            birth_date = date.fromisoformat(value)
        except ValueError:
            print(
                "تاریخ معتبر نیست. نمونه صحیح: 1998-11-24"
            )
            continue

        if birth_date > date.today():
            print(
                "تاریخ تولد نمی‌تواند در آینده باشد."
            )
            continue

        return birth_date

این تابع تا زمانی که تاریخ معتبر دریافت نکند ادامه پیدا می‌کند. اگر کاربر تاریخی در آینده وارد کند، برنامه آن را رد می‌کند.

مرحله سوم: محاسبه تعداد سال‌های کامل

اول تعداد سال‌های اولیه را محاسبه می‌کنیم:

years = today.year - birth_date.year

اما برای تعیین اینکه تولد امسال گذشته یا نه، باید یک تاریخ سالگرد بسازیم. این موضوع برای افرادی که ۲۹ فوریه متولد شده‌اند کمی حساس است.

ساخت تابع انتقال تاریخ به چند سال بعد

def add_years(source_date, years):
    target_year = source_date.year + years

    try:
        return source_date.replace(
            year=target_year
        )
    except ValueError:
        return source_date.replace(
            year=target_year,
            month=2,
            day=28,
        )

خطای این تابع معمولاً زمانی رخ می‌دهد که تاریخ اولیه ۲۹ فوریه باشد اما سال مقصد کبیسه نباشد. در این پروژه برای چنین سالی ۲۸ فوریه را به‌عنوان سالگرد در نظر می‌گیریم.

محاسبه سال‌های کامل

years = today.year - birth_date.year
anniversary = add_years(
    birth_date,
    years,
)

if anniversary > today:
    years -= 1
    anniversary = add_years(
        birth_date,
        years,
    )

اکنون متغیر years تعداد سال‌های کامل زندگی را نگه می‌دارد و anniversary آخرین سالگرد تولد را مشخص می‌کند.

مرحله چهارم: اضافه کردن ماه‌ها به تاریخ

برای محاسبه ماه‌های کامل بعد از آخرین تولد، یک تابع کمکی برای افزودن ماه می‌سازیم:

import calendar


def add_months(source_date, months):
    total_months = (
        source_date.year * 12
        + source_date.month - 1
        + months
    )

    year = total_months // 12
    month = total_months % 12 + 1

    last_day = calendar.monthrange(
        year,
        month,
    )[1]

    day = min(
        source_date.day,
        last_day,
    )

    return date(
        year,
        month,
        day,
    )

استفاده از calendar.monthrange() مهم است، چون همه ماه‌ها تعداد روز یکسان ندارند. برای مثال اگر تاریخ ما روز ۳۱ باشد و ماه مقصد فقط ۳۰ روز داشته باشد، تابع روز آخر همان ماه را انتخاب می‌کند.

محاسبه ماه و روز باقی‌مانده

months = 0
cursor = anniversary

while True:
    next_month = add_months(
        cursor,
        1,
    )

    if next_month > today:
        break

    cursor = next_month
    months += 1

    if months == 11:
        break


days = (today - cursor).days

از آخرین سالگرد تولد شروع می‌کنیم و تا زمانی که اضافه کردن یک ماه از تاریخ امروز عبور نکرده باشد، ماه‌ها را جلو می‌بریم. در پایان اختلاف روز بین آخرین نقطه و امروز محاسبه می‌شود.

تابع کامل calculate_age

def calculate_age(
    birth_date,
    today=None,
):
    if today is None:
        today = date.today()

    if birth_date > today:
        raise ValueError(
            "تاریخ تولد در آینده است."
        )

    years = (
        today.year - birth_date.year
    )

    anniversary = add_years(
        birth_date,
        years,
    )

    if anniversary > today:
        years -= 1
        anniversary = add_years(
            birth_date,
            years,
        )

    months = 0
    cursor = anniversary

    while months < 11:
        next_month = add_months(
            cursor,
            1,
        )

        if next_month > today:
            break

        cursor = next_month
        months += 1

    days = (today - cursor).days

    return years, months, days

مرحله پنجم: محاسبه روزهای باقی‌مانده تا تولد بعدی

قابلیت کاربردی بعدی این است که مشخص کنیم چند روز تا تولد بعدی باقی مانده است.

def birthday_for_year(
    birth_date,
    year,
):
    try:
        return date(
            year,
            birth_date.month,
            birth_date.day,
        )
    except ValueError:
        return date(
            year,
            2,
            28,
        )

سپس:

def days_until_birthday(
    birth_date,
    today=None,
):
    if today is None:
        today = date.today()

    next_birthday = birthday_for_year(
        birth_date,
        today.year,
    )

    if next_birthday < today:
        next_birthday = birthday_for_year(
            birth_date,
            today.year + 1,
        )

    return (
        next_birthday - today
    ).days

اگر امروز روز تولد باشد، نتیجه صفر خواهد بود. اگر تولد امسال گذشته باشد، تاریخ تولد سال آینده استفاده می‌شود.

کد کامل برنامه محاسبه سن با پایتون

import calendar
from datetime import date


def add_years(source_date, years):
    target_year = source_date.year + years

    try:
        return source_date.replace(
            year=target_year
        )
    except ValueError:
        return source_date.replace(
            year=target_year,
            month=2,
            day=28,
        )


def add_months(source_date, months):
    total_months = (
        source_date.year * 12
        + source_date.month - 1
        + months
    )

    year = total_months // 12
    month = total_months % 12 + 1

    last_day = calendar.monthrange(
        year,
        month,
    )[1]

    day = min(
        source_date.day,
        last_day,
    )

    return date(
        year,
        month,
        day,
    )


def calculate_age(
    birth_date,
    today=None,
):
    if today is None:
        today = date.today()

    if birth_date > today:
        raise ValueError(
            "تاریخ تولد در آینده است."
        )

    years = (
        today.year - birth_date.year
    )

    anniversary = add_years(
        birth_date,
        years,
    )

    if anniversary > today:
        years -= 1
        anniversary = add_years(
            birth_date,
            years,
        )

    months = 0
    cursor = anniversary

    while months < 11:
        next_month = add_months(
            cursor,
            1,
        )

        if next_month > today:
            break

        cursor = next_month
        months += 1

    days = (today - cursor).days

    return years, months, days


def birthday_for_year(
    birth_date,
    year,
):
    try:
        return date(
            year,
            birth_date.month,
            birth_date.day,
        )
    except ValueError:
        return date(
            year,
            2,
            28,
        )


def days_until_birthday(
    birth_date,
    today=None,
):
    if today is None:
        today = date.today()

    next_birthday = birthday_for_year(
        birth_date,
        today.year,
    )

    if next_birthday < today:
        next_birthday = birthday_for_year(
            birth_date,
            today.year + 1,
        )

    return (
        next_birthday - today
    ).days


def get_birth_date():
    while True:
        value = input(
            "تاریخ تولد (YYYY-MM-DD): "
        ).strip()

        try:
            birth_date = date.fromisoformat(
                value
            )
        except ValueError:
            print(
                "تاریخ معتبر نیست. نمونه: 1998-11-24"
            )
            continue

        if birth_date > date.today():
            print(
                "تاریخ تولد نمی‌تواند در آینده باشد."
            )
            continue

        return birth_date


def main():
    print(
        "=== برنامه محاسبه سن با پایتون ==="
    )

    while True:
        birth_date = get_birth_date()

        years, months, days = calculate_age(
            birth_date
        )

        remaining = days_until_birthday(
            birth_date
        )

        print(
            "\nسن دقیق شما:"
        )
        print(
            f"{years} سال، {months} ماه و {days} روز"
        )

        if remaining == 0:
            print(
                "امروز تولد شماست! 🎂"
            )
        else:
            print(
                f"{remaining} روز تا تولد بعدی باقی مانده است."
            )

        again = input(
            "\nمحاسبه دیگری انجام شود؟ (بله/خیر): "
        ).strip()

        if again != "بله":
            print("برنامه پایان یافت.")
            break


if __name__ == "__main__":
    main()

نمونه اجرای برنامه

=== برنامه محاسبه سن با پایتون ===
تاریخ تولد (YYYY-MM-DD): 1998-11-24

سن دقیق شما:
27 سال، 8 ماه و 15 روز
107 روز تا تولد بعدی باقی مانده است.

عددهای خروجی بسته به روزی که برنامه را اجرا می‌کنید تغییر خواهند کرد، چون تاریخ امروز با date.today() گرفته می‌شود.

تست چند حالت مهم

برای پروژه‌های مرتبط با تاریخ بهتر است فقط یک تاریخ معمولی را تست نکنیم.

تاریخ تولد امروز

باید تعداد روزهای باقی‌مانده تا تولد بعدی صفر باشد.

تاریخ آینده

برنامه باید آن را رد کند و پیام مناسب نمایش دهد.

تاریخ نامعتبر

2024-15-80

باید توسط ValueError کنترل شود.

تولد ۲۹ فوریه

در سال‌های غیرکبیسه، این پروژه ۲۸ فوریه را به‌عنوان سالگرد در نظر می‌گیرد. این یک تصمیم طراحی است و در سامانه‌های رسمی ممکن است قواعد متفاوتی وجود داشته باشد.

چرا محاسبه تاریخ از چیزی که به نظر می‌رسد سخت‌تر است؟

ماه‌ها ۲۸، ۲۹، ۳۰ یا ۳۱ روز دارند. سال‌های کبیسه هم یک روز بیشتر دارند. به همین دلیل تبدیل سن به تعداد ثابتی روز یا فرض اینکه هر ماه ۳۰ روز است، برای محاسبه تقویمی دقیق مناسب نیست.

در این پروژه به‌جای تخمین، از اشیای واقعی تاریخ و تعداد روز واقعی هر ماه استفاده شده است.

اشتباه‌های رایج

تقسیم تعداد روزها بر 365

این کار می‌تواند یک تخمین از سن ایجاد کند، اما برای سن تقویمی دقیق مناسب نیست؛ چون سال کبیسه و ماه‌های متفاوت را نادیده می‌گیرد.

فقط کم کردن سال‌ها

تا زمانی که تولد امسال نرسیده باشد، نتیجه یک سال بیشتر از سن واقعی خواهد بود.

پذیرفتن تاریخ آینده

اگر این بررسی انجام نشود، برنامه می‌تواند سن منفی یا خروجی غیرمنطقی تولید کند.

عدم توجه به ۲۹ فوریه

کدهایی که مستقیماً سال یک تاریخ ۲۹ فوریه را تغییر می‌دهند ممکن است در سال غیرکبیسه با خطا متوقف شوند.

چطور پروژه را توسعه دهیم؟

  1. نمایش روز هفته تولد: مشخص کنید کاربر در چه روزی از هفته متولد شده است.
  2. نمایش تعداد روزهای زندگی: اختلاف مستقیم تاریخ امروز و تولد را محاسبه کنید.
  3. نمایش تعداد هفته‌های زندگی: تعداد روزها را بر ۷ تقسیم کنید.
  4. محاسبه تولد بعدی: علاوه بر تعداد روزها، تاریخ دقیق تولد آینده را نمایش دهید.
  5. پشتیبانی از چند نفر: اطلاعات چند کاربر را در List یا Dictionary ذخیره کنید.
  6. ذخیره در JSON: نام و تاریخ تولدها را ذخیره و در اجرای بعدی بازیابی کنید.
  7. نسخه GUI: با Tkinter فیلد تاریخ و دکمه محاسبه ایجاد کنید.
  8. پشتیبانی از تقویم شمسی: در یک پروژه جداگانه با کتابخانه مناسب، تاریخ جلالی را اضافه کنید.

تمرین پیشنهادی

تابعی با نام lived_days() بسازید که تاریخ تولد را دریافت کند و تعداد کل روزهای گذشته از تولد تا امروز را برگرداند. سپس خروجی را به تعداد هفته کامل و روز باقی‌مانده تبدیل کنید.

نمونه ساختار:

def lived_days(birth_date):
    return (
        date.today() - birth_date
    ).days

این پروژه چه مفاهیمی را تمرین می‌دهد؟

  • Import کردن ماژول‌های استاندارد
  • کار با datetime.date
  • کار با calendar.monthrange
  • توابع و پارامترهای اختیاری
  • شرط‌ها
  • حلقه while
  • Exception Handling
  • اعتبارسنجی ورودی
  • تفریق تاریخ‌ها
  • طراحی چند تابع کوچک به‌جای یک تابع بزرگ
  • مدیریت Edge Caseهای تقویمی

جمع‌بندی

در این آموزش یک برنامه محاسبه سن با پایتون ساختیم که تاریخ تولد را دریافت می‌کند و سن را به‌صورت سال، ماه و روز نمایش می‌دهد. علاوه بر این، برنامه تعداد روزهای باقی‌مانده تا تولد بعدی را محاسبه می‌کند و تاریخ‌های نامعتبر، آینده و حالت خاص ۲۹ فوریه را مدیریت می‌کند.

هدف اصلی پروژه فقط نمایش یک عدد برای سن نیست؛ بلکه تمرین طراحی منطق برای داده‌های تقویمی است. قبل از رفتن به پروژه بعدی، قابلیت «تعداد روزهای زندگی» یا «نمایش تاریخ تولد بعدی» را خودتان به برنامه اضافه کنید.

📌 همچنین بخوانید: تمرین پایتون با جواب، ساخت بازی با پایتون و Pygame و ساخت ربات با پایتون

می‌خواهی پیشرفتت ذخیره شود؟با ورود یا ثبت‌نام رایگان، درس‌های تمام‌شده را علامت بزن و درصد پیشرفتت را در حساب کاربری ببین.ورود / ثبت‌نام برای ذخیره پیشرفت
درس بعدی · پروژه‌های پایتون (6 از 19)ساخت برنامه محاسبه BMI با پایتون؛ پروژه کامل با اعتبارسنجی و تحلیل نتیجه ←
آموزش‌های تازه، کد آماده و رفع خطاهر هفته در کانال Dev Source؛ رایگان.
🧭 مسیر یادگیری پایتون از صفر
ماژول 8: ماژول‌ها و کتابخانه‌ها — درس 38 از 44

برای ذخیره پیشرفت در همه دستگاه‌ها و دریافت گواهی پایان مسیر، رایگان ثبت‌نام کن یا وارد شو.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *