یادداشت

الگوریتم میلانی نیا چیست؟ روشی سبک برای تبدیل تاریخ میلادی و شمسی

الگوریتم میلانی نیا یک روش سبک برای تبدیل تاریخ میلادی و شمسی بر پایه روز سال است که در کتابخانه PersianDate توسعه یافته است. این الگوریتم بدون استفاده از عدد روز ژولیوسی، برای اجرا روی میکروکنترلرهای کم حافظه طراحی شده و تمام تاریخ های سال 2000 تا 2100 را با موفقیت روی Arduino Uno آزمایش کرده است.

الگوریتم میلانی نیا چیست؟ روشی سبک برای تبدیل تاریخ میلادی و شمسی

تبدیل تاریخ میلادی به شمسی یکی از نیازهای متداول در پروژه های الکترونیکی فارسی زبان است. ساعت های دیجیتال، تابلوهای اطلاع رسانی، دستگاه های حضور و غیاب، سیستم های ثبت اطلاعات و تجهیزات اینترنت اشیا نمونه هایی هستند که ممکن است زمان را از اینترنت یا یک ماژول RTC دریافت کنند، اما لازم باشد تاریخ را به صورت شمسی نمایش دهند.

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

الگوریتم میلانی نیا با هدف ایجاد یک روش مستقیم، سبک و قابل اجرا روی بردهایی مانند Arduino Uno و Nano توسعه یافته است. این روش در کتابخانه PersianDate با نام انگلیسی Milaninia day-of-year algorithm مستند شده است.

الگوریتم میلانی نیا چیست؟

الگوریتم میلانی نیا یک روش تبدیل تاریخ بر پایه «روز سال» است. در این روش ابتدا مشخص می شود تاریخ مورد نظر چندمین روز سال میلادی است. سپس با تعیین محل شروع فروردین در همان سال، روز متناظر در تقویم شمسی محاسبه می شود.

ایده اصلی الگوریتم بر یک مشاهده ساده استوار است: برای تبدیل تاریخ، همیشه لازم نیست تعداد روزهای گذشته از یک مبدا تاریخی بسیار دور محاسبه شود. اگر محل شروع سال شمسی در سال میلادی مورد نظر مشخص باشد، می توان فاصله تاریخ ورودی تا آن نقطه را محاسبه و نتیجه را به ماه و روز شمسی تبدیل کرد.

این ویژگی باعث می شود ساختار الگوریتم برای برنامه نویسان قابل فهم باشد و بتوان آن را روی میکروکنترلرهای دارای حافظه محدود اجرا کرد.

چرا این الگوریتم ایجاد شد؟

کتابخانه PersianDate برای استفاده در پروژه های Arduino و سیستم های نهفته ساخته شده است. در چنین پروژه هایی منابع سخت افزاری محدود هستند و هر بخش از برنامه باید تا حد امکان ساده و کم حجم باشد.

برای مثال Arduino Uno تنها 2 کیلوبایت حافظه SRAM دارد. در یک پروژه واقعی، همین حافظه محدود باید میان نمایشگر، سنسورها، ارتباط سریال، رشته های متنی، منوها و سایر بخش های برنامه تقسیم شود.

بنابراین الگوریتم تبدیل تاریخ باید ویژگی های زیر را داشته باشد:

ساختار محاسباتی ساده و قابل فهم

مصرف حافظه کم

نداشتن وابستگی به کتابخانه های خارجی

امکان تبدیل تاریخ در هر دو جهت

قابلیت اجرا روی میکروکنترلرهای 8 بیتی

امکان آزمایش تمام تاریخ های محدوده مورد استفاده

الگوریتم میلانی نیا برای پاسخ به همین نیازها شکل گرفت و سپس به بخش اصلی کتابخانه PersianDate تبدیل شد.

تفاوت این روش با الگوریتم های مبتنی بر JDN

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

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

البته در کتابخانه PersianDate از JDN برای محاسبه روز هفته استفاده شده است. بنابراین باید میان دو کاربرد تفاوت قائل شویم:

تبدیل تاریخ میلادی و شمسی با الگوریتم روز سال میلانی نیا انجام می شود.

محاسبه روز هفته با استفاده از JDN انجام می شود.

این تفکیک باعث می شود ادعای «بدون JDN» فقط به هسته تبدیل تاریخ مربوط باشد، نه تمام قابلیت های کتابخانه.

الگوریتم چگونه کار می کند؟

فرایند تبدیل تاریخ میلادی به شمسی را می توان در چند مرحله توضیح داد.

مرحله اول: اعتبارسنجی تاریخ ورودی

پیش از شروع محاسبات، سال، ماه و روز میلادی بررسی می شوند. تعداد روزهای هر ماه و وضعیت کبیسه بودن سال نیز در این بررسی در نظر گرفته می شود.

برای مثال 29 فوریه فقط در سال میلادی کبیسه معتبر است. اگر تاریخ ورودی نامعتبر باشد، کتابخانه نتیجه صفر را برمی گرداند تا برنامه بتواند خطا را تشخیص دهد.

مرحله دوم: محاسبه روز سال میلادی

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

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

مرحله سوم: تعیین شروع فروردین

در نسخه فعلی الگوریتم، تاریخ 20 مارس 2024 برابر با اول فروردین 1403 به عنوان نقطه مرجع در نظر گرفته شده است.

الگوریتم برای رسیدن از این سال مرجع به سال های قبل یا بعد، طول دقیق سال های میلادی و شمسی را محاسبه می کند. هر سال ممکن است 365 یا 366 روز داشته باشد. اختلاف طول دو تقویم مشخص می کند که آغاز فروردین در سال میلادی مورد نظر روی چه روزی قرار می گیرد.

این روش باعث می شود مرز نوروز فقط با یک شرط ثابت مانند «همیشه 21 مارس» تعیین نشود؛ زیرا اول فروردین ممکن است با 20 یا 21 مارس هم زمان باشد.

مرحله چهارم: تعیین سال شمسی

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

مرحله پنجم: محاسبه روز سال شمسی

فاصله تاریخ ورودی تا آغاز فروردین محاسبه می شود. اگر تاریخ در ماه های ژانویه، فوریه یا روزهای قبل از نوروز قرار داشته باشد، الگوریتم ادامه روزهای سال میلادی قبل را نیز در محاسبه وارد می کند.

نتیجه این مرحله مشخص می کند تاریخ مورد نظر چندمین روز سال شمسی است.

مرحله ششم: تبدیل روز سال به ماه و روز شمسی

شش ماه اول تقویم شمسی 31 روز دارند. پنج ماه بعدی 30 روزه هستند و اسفند با توجه به کبیسه بودن سال 29 یا 30 روز دارد.

الگوریتم روزهای هر ماه را به ترتیب از روز سال کم می کند تا ماه و روز نهایی به دست آید.

تبدیل شمسی به میلادی

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

در تبدیل شمسی به میلادی ابتدا تاریخ شمسی اعتبارسنجی و سپس روز سال شمسی محاسبه می شود. محل شروع فروردین در سال میلادی متناظر به دست می آید و روز سال شمسی به آن افزوده می شود.

اگر نتیجه از تعداد روزهای سال میلادی بیشتر شود، محاسبه وارد سال میلادی بعد خواهد شد. در پایان، روز سال میلادی به ماه و روز تبدیل می شود.

این ساختار متقارن امکان انجام آزمایش رفت و برگشتی را فراهم می کند. در این آزمایش یک تاریخ میلادی ابتدا به شمسی و سپس دوباره به میلادی تبدیل می شود. تاریخ نهایی باید دقیقا با ورودی اولیه برابر باشد.

نقش سال های کبیسه

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

کبیسه بودن سال میلادی با قانون شناخته شده تقویم گریگوری محاسبه می شود. سال هایی که بر 4 بخش پذیر هستند کبیسه اند، مگر سال های قرنی که فقط در صورت بخش پذیری بر 400 کبیسه محسوب می شوند. به همین دلیل سال 2000 کبیسه است، اما سال 2100 کبیسه نیست.

در نسخه فعلی کتابخانه، کبیسه بودن سال شمسی با چرخه 33 ساله محاسبه می شود. این روش در محدوده تعریف شده و آزمایش شده کتابخانه به کار می رود.

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

محدوده اعتبار الگوریتم

نسخه 2.1.2 کتابخانه PersianDate برای تاریخ های میلادی از اول ژانویه 2000 تا پایان دسامبر 2100 طراحی و اعتبارسنجی شده است. محدوده متناظر تاریخ شمسی نیز از سال 1378 تا 1479 در نظر گرفته شده است.

این محدوده برای بیشتر پروژه های عملی Arduino، سیستم های اینترنت اشیا، ساعت ها و تجهیزات الکترونیکی امروزی کافی است.

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

آزمایش سخت افزاری روی Arduino Uno

برای ارزیابی الگوریتم، تمام تاریخ های میلادی از سال 2000 تا 2100 روی Arduino Uno بررسی شدند. این محدوده شامل 36 هزار و 890 تاریخ است.

در مجموع 74 هزار و 96 بررسی انجام شد و هیچ خطایی گزارش نشد. آزمایش ها شامل موارد زیر بودند:

تبدیل تاریخ میلادی به شمسی

تبدیل مجدد تاریخ شمسی به میلادی

بررسی یکسان بودن تاریخ اولیه و تاریخ بازگشتی

کنترل مرزهای نوروز

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

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

کنترل روزهای هفته و جمعه ها

بررسی تعطیلات ثابت ثبت شده در کتابخانه

اجرای این آزمایش روی Arduino Uno اهمیت ویژه ای دارد؛ زیرا نشان می دهد الگوریتم فقط در محیط رایانه آزمایش نشده و روی یک میکروکنترلر 8 بیتی با منابع محدود نیز قابل اجرا است.

قابلیت های کتابخانه PersianDate

الگوریتم میلانی نیا هسته تبدیل تاریخ در کتابخانه PersianDate است، اما کتابخانه قابلیت های دیگری نیز دارد:

تبدیل تاریخ میلادی به شمسی

تبدیل تاریخ شمسی به میلادی

تشخیص سال کبیسه

نمایش نام فارسی ماه ها

نمایش نام کامل و کوتاه روزهای هفته

تشخیص جمعه به عنوان روز تعطیل

تشخیص تعطیلات رسمی ثابت

ارائه توابع استاتیک برای تبدیل مستقیم تاریخ

اعتبارسنجی تاریخ های ورودی

عدم وابستگی به کتابخانه RTClib

تعطیلات قمری متغیر در کتابخانه ثبت نشده اند؛ زیرا تعیین آن ها به محاسبات تقویم قمری یا دریافت اطلاعات از یک منبع به روز نیاز دارد.

الگوریتم میلانی نیا برای چه پروژه هایی مناسب است؟

این الگوریتم می تواند در پروژه های زیر استفاده شود:

ساعت و تقویم دیجیتال شمسی

نمایش تاریخ فارسی روی OLED و TFT

دستگاه های ثبت دما و اطلاعات محیطی

سیستم های حضور و غیاب

کنترلرهای آبیاری و کشاورزی

تابلوهای اطلاع رسانی

سیستم های خانگی هوشمند

تجهیزات مبتنی بر Arduino، ESP8266 و ESP32

دستگاه هایی که تاریخ میلادی را از NTP یا RTC دریافت می کنند

پروژه هایی که باید تعطیلات ثابت یا جمعه ها را تشخیص دهند

از آنجا که کتابخانه مستقل است، دریافت تاریخ می تواند از اینترنت، ماژول RTC، GPS، ورودی کاربر یا هر منبع دیگری انجام شود.

آیا الگوریتم میلانی نیا یک استاندارد جهانی است؟

خیر. الگوریتم میلانی نیا در حال حاضر نام روش تبدیل تاریخ توسعه یافته برای کتابخانه PersianDate است. این روش به صورت عمومی در مخزن ARDUnia منتشر شده و کد آن قابل بررسی است، اما نباید آن را یک استاندارد رسمی تقویم، الگوریتم دانشگاهی تثبیت شده یا جایگزین تمام روش های نجومی معرفی کرد.

اعتبار فعلی آن بر سه پایه قرار دارد:

ساختار مشخص و مستند

انتشار عمومی کد منبع

آزمایش کامل محدوده سال 2000 تا 2100

انتشار متن باز این امکان را فراهم می کند که برنامه نویسان دیگر الگوریتم را بررسی، آزمایش و اصلاح کنند.

جمع بندی

الگوریتم میلانی نیا روشی سبک برای تبدیل تاریخ میلادی و شمسی بر پایه روز سال است. این روش به جای تبدیل تاریخ به عدد روز ژولیوسی، محل آغاز فروردین را محاسبه و فاصله تاریخ مورد نظر را نسبت به آن تعیین می کند.

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

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

کد منبع و مستندات کتابخانه PersianDate در مخزن ARDUnia منتشر شده است:

https://github.com/ARDUnia/PersianDate

→ بازگشت به وبلاگ