4
\$\begingroup\$

We have conception as "fiscal year" and have calculations based on start/end date (April 1, March 31).

My question is: what is better, store end date time as April 1 or March 31? Now I have next code:

public DateTime GetFiscalYearStart(int fiscalYear)
{
    return new DateTime(fiscalYear, 4, 1);
}

public DateTime GetFiscalYearEnd(int fiscalYear)
{
    return new DateTime(++fiscalYear, 4, 1).AddTicks(-1);
}
\$\endgroup\$
  • \$\begingroup\$ Just a little note, the more recent versions of C# allow you to simply do public DateTime GetFiscalYearEnd(int fiscalYear) => new DateTime(++fiscalYear, 4, 1).AddTicks(-1); \$\endgroup\$ – Danny Goodall Apr 16 '18 at 10:07
  • \$\begingroup\$ Off topics comment, Imo as I learn to stay away from sql between so the "March 31" is for me the way to go. for <= and >= should be use with consitency. Ticks -1 should be the prefered way for anyone that ever use sql Eomonth() and between.. \$\endgroup\$ – Drag and Drop Apr 16 '18 at 11:43
7
\$\begingroup\$

It doesn't really matter.

My question is: what is better, store end date time as April 1 or March 31?

This is completely up to you based on what you want:

  • How do you prefer to check your date bounds?
    • Inclusive? if(myDate <= myFiscalYear.GetFiscalYearEnd())
    • Exclusive? if(myDate < myFiscalYear.GetFiscalYearEnd())
  • Is this end date shown to the user? Which date do they prefer seeing?

On a technical level, it doesn't matter which you pick. All that matters is that you handle the value consistently. If you start mixing < and <= checks, you're going to run into problems and bugs regardless of whether you define your date inclusive or exclusive.


Taking it a bit further.

However, as I pointed out, the inconsistent use of < and <= is going to be a possible cause for bugs down the line. Your developers might mistakenly use one instead of the other.

The issue here is that external code will handle the comparison. However, if you wrap this in a class of your own, you can ensure that the evaluation only occurs in one place, which dramatically lowers the chance of developer error.

A simple example class:

public class FiscalYear
{
    private int fiscalYear;

    public DateTime Start => new DateTime(fiscalYear, 4, 1);
    public DateTime End   => new DateTime(fiscalYear+1, 4, 1).AddTicks(-1);

    public FiscalYear(int year)
    {
        this.fiscalYear = year;
    }

    public bool Contains(DateTime date)
    {
        return date >= this.Start && date <= this.End;
    }
}    

Note
There are other possible variations on this. You could calculate the dates in the constructor, or you could calculate the end date based on the start date. How you define the values is completely up to you.

Edit remark:
It's still possible for developers to implement their own evalutation based on the Start/End properties, but that is then considered developer error, they should rely on the logic that already exists instead of reinventing the wheel.
What you could also do is set Start and End to private. This way, you prevent external code from doing their own evaluation, they can only do new FiscalYear(2018).Contains(myDate), but then you do lose out on using FiscalYear as an easy way for your external callers to find out what the start and end date of a fiscal year are. That's up to you to decide.

External code can then rely on the existing Contains() method; which means that the developers of the external code no longer need to know whether they should use < or <=.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.