Edit

Share via


Warning C26861

Field of a date-time object var has been modified without proper leap year checking: expr

This rule was added in Visual Studio 2022 17.8.

Remarks

In the Gregorian calendar, every year exactly divisible by four is a leap year--except for years that are exactly divisible by 100. The centurial years are also leap years if they're exactly divisible by 400.

A leap year bug occurs when software doesn't account for this leap year logic, or uses flawed logic. The can affect reliability, availability, or even the security of the affected system.

It isn't safe to add or subtract some number to or from the year, month, or day field of a date-time object without taking leap years into account. This calculation is commonly performed to determine the expiration date for a certificate, for example. On many dates, a naive calculation may produce the desired result. However, when the result is February 29 (a leap day) and the year isn't a leap year, the result is invalid.

For example, adding a year to 2020-01-31 produces 2021-01-31. But adding a year to 2020-02-29 produces 2021-02-29, which isn't a valid date because 2021 isn't a leap year.

Be cautious when manipulating variables that represent date values. Handle leap years and leap days properly, or use an API or library that handles date arithmetic safely.

Code analysis name: DATETIME_MANIPULATION_WITHOUT_LEAPYEAR_CHECK

Example

The following code advances the system time by a year by incrementing the year field of the date-time object representing the system time. However, it can produce an invalid date-time object if the date was February 29 before the modification, because the next year isn't a leap year:

SYSTEMTIME st; 
GetSystemTime(&st); 
st.wYear++;  // warning C26861 

To avoid creating an invalid date-time object due to a leap year, check if the resulting date is still valid and make the necessary adjustments to make it valid, as in this example:

SYSTEMTIME st; 
GetSystemTime(&st); 
st.wYear++; 
if (st.wMonth == 2 && st.wDay == 29) 
{ 
    // move back a day when landing on Feb 29 in a non-leap year 
    bool isLeapYear = st.wYear % 4 == 0 && (st.wYear % 100 != 0 || st.wYear % 400 == 0); 
    if (!isLeapYear) 
    { 
        st.wDay = 28; 
    } 
}

Heuristics

Currently, this rule only recognizes the Windows SYSTEMTIME struct and C tm struct.

This rule employs a simplified heuristic to find potentially risky changes and reports warnings unless there's appropriate leap year or leap day checking. It doesn't try to verify if the leap year or leap day checking is performed correctly for the modified date-time object.

This rule is an opt-in rule, which means that code analysis should use a ruleset file, and the rule should be explicitly included in the ruleset file, and enabled for it to be applied. For more information on creating a custom ruleset for code analysis, see: Use Rule Sets to Specify the C++ Rules to Run.

See also

C6393
C6394
C26862
C26863
C26864