בעיית שנה מעוברת

בעיית שנה מעוברת (המכונה גם באג שנה מעוברת או באג יום מעובר) היא בעיה בעולמות המחשב שנובעת מחישוב שגוי של השנים שהן שנים מעוברות, או ממניפולציה של תאריכים ללא התחשבות בהבדל בין שנים מעוברות לשנים שאינן מעוברות.

== סוגים ==
באגים של שנה מעוברת מתחלקים לשתי קטגוריות:<ref></ref>

# באגים המובילים לקריסת התוכנית, כגון זריקת חריגות, החזרת קוד שגיאה, משתנים לא מאותחלים או לולאות אינסופיות
# באגים לוגיים המובילים לתוצאה שגויה

== דוגמאות ==
=== פייתון ===
קוד הפייתון הבא הוא דוגמה לבאג שנה מעוברת שגורם לזריקת חריגה. הוא יעבוד כמו שצריך עד ש-<code>today</code> יהפוך ל-29 בפברואר. לאחר מכן, הוא ינסה ליצור תאריך 29 בפברואר בשנה לא מעוברת, שאינו קיים. הבאי של ה-<code>date</code> יציג <code>ValueError</code> עם ההודעה "day is out of range for month".<ref></ref><syntaxhighlight lang="python" line="1">
from datetime import date
today = date.today()
later = today.replace(year = today.year + 1)
</syntaxhighlight>

=== ++C ===
קוד ה-++C הבא הוא דוגמה לבאג של שנה מעוברת שגורם לתוכנית לסיים את ריצתה בצורה לא תקנית, ולהחזיר קוד שגיאה. הקוד יעבוד כמו שצריך עד ש-<code>st</code> יהפוך ל-29 בפברואר. לאחר מכן, הוא ינסה ליצור 29 בפברואר של שנה לא מעוברת, שאינו קיים. העברה זו לכל פונקציה שמקבלת מבנה <code>SYSTEMTIME</code> תיכשל.

לדוגמה, <code>SystemTimeToFileTime</code> המוצגת כאן תחזיר קוד שגיאה. מכיוון שערך ההחזרה אינו נשמר במשתנה ונבדק לאחר מכן, הדבר יביא לכך ש-<code>ft</code> יישאר לא מאותחל.<ref></ref><syntaxhighlight lang="cpp" line="1">
SYSTEMTIME st;
FILETIME ft;

GetSystemTime(&st);
st.wYear++;

SystemTimeToFileTime(&st, &ft);
</syntaxhighlight>

=== #C ===
קוד ה-C sharp .NET הבא הוא דוגמה לבאג של שנה מעוברת שגורם לזריקת חריגה. הוא יעבוד כמו שצריך עד ש-<code>dt</code> יהפוך ל-29 בפברואר. לאחר מכן, הוא ינסה ליצור 29 בפברואר של שנה לא מעוברת, שאינו קיים. הבנאי של <code>DateTime</code> יזרוק את החריגה <code>ArgumentOutOfRangeException</code>.<ref></ref><syntaxhighlight lang="csharp" line="1">
DateTime dt = DateTime.Now;
DateTime result = new DateTime(dt.Year + 1, dt.Month, dt.Day);
</syntaxhighlight>

=== JavaScript ===
קוד ה-JavaScript הבא הוא דוגמה לבאג בלוגיקה שנגרם עקב שנה מעוברת, ומוביל להתנהגות לא צפויה. הקוד יעבוד כמו שצריך עד ש-<code>dt</code> יהפוך ל-29 בפברואר, למשל בתאריך 29 בפברואר 2020, אז ינסה לקדם את השנה ל-2021. מכיוון ש-29 בפברואר 2021 לא קיים, <code>Date</code> יתגלגל קדימה לתאריך החוקי הבא, שהוא 1 במרץ 2021, שעלול להוביל להתנהגות לא צפויה בהמשך הקוד.<ref></ref><syntaxhighlight lang="js" line="1">
var dt = new Date();
dt.setFullYear(dt.getFullYear() + 1);
</syntaxhighlight>

=== אלגוריתם שגוי לחישוב שנה מעוברת (נכון למגוון שפות תכנות) ===
הקוד הבא הוא דוגמה לבאג נפוץ של שנה מעוברת שיכול להתרחש במגוון שפות תכנות. הבאג עלול לגרום למגוון תופעות שחלקן לוגיות וחלקן גורמות לתוכנית לקרוס, כתלות בשימוש שנעשה בתוצאה. הקוד מניח באופן שגוי ששנה מעוברת מתרחשת בדיוק כל ארבע שנים.<ref></ref><syntaxhighlight lang="csharp" line="1">
bool isLeapYear = year % 4 == 0;
</syntaxhighlight>

== היסטוריה ==
הופעת באגים שנגרמים עקב שנה מעוברת התרחשה פעמים רבות לאורך ההיסטוריה, וככל הנראה תמשיך להתרחש גם בשנים מעוברות עתידיות. מספר דוגמאות:
* בשנת 2020 התרחשו מספר באגים של שנה מעוברת, ואלו קוטלגו ב-List of 2020 Leap Day Bugs באתר Code of Matt.<ref></ref>
* בשנת 2016, באג שנה מעוברת שהתרחש ב-29 בפברואר במערכת הובלת המזוודות בנמל התעופה דיסלדורף גרם למעל 1,200 משאים להחמיץ את טיסותיהם.<ref></ref>
* בשנת 2016 התרחשו מספר באגים של שנה מעוברת, ואלו קוטלגו ב-[https://codeofmatt.com/list-of-2016-leap-day-bugs List of 2016 Leap Day Bugs] באתר Code of Matt.<ref></ref>
* בשנת 2012, Microsoft Azure ירד ממצב מקוון בעקבות באג השנה המעוברת ב-28 בפברואר, בשעה 17:45 בשעון של החוף המערבי. צוות Windows Azure טען שהבאג התרחש בגלל חישוב שגוי.
* בשנת 2012, היסטוריית הצ'אטים של Gmail הציגה את התאריך 31 בדצמבר 1969 לכל הצ'אטים שנשמרו ב-29 בפברואר.
* בשנת 2012, התקני ניווט לווייניים של TomTom היו תקולים עקב באג שנה מעוברת שהופיע לראשונה ב-31 במרץ.<ref></ref>
* בשנת 2010 פלייסטיישן 3 של חברת סוני התייחס באופן שגוי לשנה כאל שנה מעוברת, כך שהתאריך הלא קיים 29 בפברואר 2010 הוצג במקום 1 במרץ 2010 וגרם לשגיאות תוכנה.<ref></ref>
* בחצות ב-31 בדצמבר 2008, דגמי Zune 30 רבים מהדור הראשון קפאו.<ref name="Official response"></ref><ref name="gizmodo-zune-fail"></ref><ref name="techdigest-breaking"></ref> מיקרוסופט הצהירה כי הבעיה נגרמה על ידי מנהל השעון הפנימי שנכתב על ידי פריסקייל, והאופן שבו המכשיר מטפל בשנה מעוברת. הבעיה תוקנה אוטומטית כעבור 24 שעות, אך מי שלא שש להמתין 24 שעות יכול היה לרוקן את הסוללה של המכשיר ואז לטעון אותו מחדש לפי UTC, ב-1 בינואר 2009.<ref name="official-fix"></ref><ref>
</ref>
* בשנת 1996, שני מפעלי התכת אלומיניום - טיוואי פוינט שבניו זילנד ובל ביי שבטסמניה, אוסטרליה - חוו באג שנה מעוברת ב-31 בדצמבר, כאשר כל אחד מ-660 המחשבים השולטים בקווי ההיתוך נסגרו בחצות במקביל, בלי אזהרה. המחשבים לא תוכנתו לטיפול ביום ה-366 בשנה. עלויות התיקון נאמדו ביותר מ-1,000,000 דולר ניו זילנדי.<ref></ref>
* מאז הגרסאות המוקדמות ביותר שלה, מיקרוסופט אקסל החשיבה באופן שגוי את שנת 1900 כשנה מעוברת, ולכן 29 בפברואר מגיע בין 28 בפברואר ל-1 במרץ של אותה שנה. מקורו של הבאג הוא בלוטוס 1-2-3, והוא הוטמע בכוונה באקסל לצורך תאימות לאחור. מיקרוסופט כתבה מאמר על הבאג, והסבירה את הסיבות שלה להתייחסות לשנת 1900 כשנה מעוברת.<ref name="Microsoft article about leap year bug">[https://docs.microsoft.com/office/troubleshoot/excel/wrongly-assumes-1900-is-leap-year Excel incorrectly assumes that the year 1900 is a leap year]. Retrieved 2019-05-01.</ref> באג זה הפך לדרישה במפרט .<ref name="ECMA-376">[http://www.ecma-international.org/publications/standards/Ecma-376.htm Standard ECMA-376 / Open Office XML File Formats]. Retrieved 2016-09-10.</ref><ref name="ISO/IEC 29500">[http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html ISO/IEC 29500 / Open Office XML File Formats]. Retrieved 2016-09-10.</ref>

== ראו גם ==
* באגים הקשורים לשעה ותאריך
* באג 2000

== הערות שוליים ==

קטגוריה:באגים הקשורים לשעה ותאריך
קטגוריה:באגים
קטגוריה:לוחות שנה