CodeQL documentation

Unsafe year argument for ‘DateTime’ constructor

ID: cs/unsafe-year-construction
Kind: path-problem
Severity: warning
Precision: medium
Tags:
   - date-time
   - reliability
Query suites:
   - csharp-security-and-quality.qls

Click to see the query in the CodeQL repository

When creating a System.DateTime object by setting the year, month, and day in the constructor by performing an arithmetic operation on a different DateTime object, there is a risk that the date you are setting is invalid.

On a leap year, such code may throw an ArgumentOutOfRangeException with a message of "Year, Month, and Day parameters describe an unrepresentable DateTime."

Recommendation

Creating a System.DateTime object based on a different System.DateTime object, use the appropriate methods to manipulate the date instead of arithmetic.

Example

In this example, we are incrementing/decrementing the current date by one year when creating a new System.DateTime object. This may work most of the time, but on any given February 29th, the resulting value will be invalid.

using System;
public class UnsafeYearConstructionBad
{
    public UnsafeYearConstructionBad()
    {
        DateTime Start;
        DateTime End;
        var now = DateTime.UtcNow;
        // the base-date +/- n years may not be a valid date.
        Start = new DateTime(now.Year - 1, now.Month, now.Day, 0, 0, 0, DateTimeKind.Utc);
        End = new DateTime(now.Year + 1, now.Month, now.Day, 0, 0, 1, DateTimeKind.Utc);
    }
}

To fix this bug, we add/substract years to the current date by calling AddYears method on it.

using System;
public class UnsafeYearConstructionGood
{
    public UnsafeYearConstructionGood()
    {
        DateTime Start;
        DateTime End;
        var now = DateTime.UtcNow;
        Start = now.AddYears(-1).Date;
        End = now.AddYears(-1).Date.AddSeconds(1);
    }
}