45

Looking for a better way to compare a nullable date time than the following:

Any suggestions?

// myobject.ExpireDatetime is of DateTime?
//
if (!myobject.ExpireDateTime.IsNull() && DateTime.Compare((DateTime)myobject.ExpireDateTime, DateTime.Now.ToUniversalTime()) < 0)
{ //error! }    

Edited: Sorry for confusion...myobject.ExpireDatetime is of type DateTime.

genxgeek
  • 13,109
  • 38
  • 135
  • 217
  • In C# the word **`object`** is a keyword and it cannot be used as an identifyer (except if you write it `@object`). I think it's unclear if you have a boxed `DateTime` that may be a null reference, or if you have an unboxed "true" nullable `DateTime` (`DateTime?`). – Jeppe Stig Nielsen Dec 02 '12 at 20:09
  • Does this answer your question? [Compare nullable datetime objects](https://stackoverflow.com/questions/14251902/compare-nullable-datetime-objects) – Michael Freidgeim May 17 '21 at 02:15

5 Answers5

57

Your question is not quite clear to me, but if we have

DateTime? ExpireDateTime;  // could be a variable or a property

it's OK to say just

if (ExpireDateTime < DateTime.UtcNow)
{
  ...
}

This will be OK if ExpireDateTime is null (HasValue is false). Some inexperienced developers will struggle to understand lifted operators, so to make it more clear, you could write

if (ExpireDateTime < (DateTime?)DateTime.UtcNow)
{
  ...
}

It's the same, but easier to read and understand.

Never write .Value if the nullable might be null, of course. You will get an InvalidOperationException "Nullable object must have a value" if you do so.

Community
  • 1
  • 1
Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • Awesome! Yes, this is the explanation that I was really after. That is, how to compare a nullable datetime without short circuiting logic in by checking if the value is not null...then checking the constraint on the nullable value if (val.isnotnull && val.iswithinconstraint) – genxgeek Dec 02 '12 at 21:06
  • 2
    but, (default(DateTime?) < DateTime.UtcNow) is false and ( default(DateTime?) > DateTime.UtcNow) is also false – labroo Aug 31 '15 at 05:33
  • 3
    @labroo Certainly. So with nullables we do not have "[trichotomy](https://en.wikipedia.org/wiki/Trichotomy_(mathematics))", so sometimes `x < y` and `x == y` and `x > y` are all `false`. – Jeppe Stig Nielsen Aug 31 '15 at 15:20
  • It also depends on specific business requirements. In some occasions I might want to treat null date as "greater than" any valid date. For example: `minDate = d1 < d2 ? d1 : d2` where all three dates are nullable - in this case I want `minDate` to receive actual valid date, if any, but not null, unless both d1 and d2 are nulls. – JustAMartin May 12 '16 at 09:00
  • @JustAMartin If you really want, you can write `!(ExpireDateTime >= DateTime.UtcNow)` which is the same as `ExpireDateTime < DateTime.UtcNow` if `null` is not involved, but has the opposite value when `null` is involved. However, it may be easier to read something like `!ExpireDateTime.HasValue || ExpireDateTime.Value < DateTime.UtcNow`, depending on taste. – Jeppe Stig Nielsen Jan 11 '19 at 15:41
39

I would recommend you to use the following:

int y = Nullable.Compare<DateTime>(DateTime.UtcNow, x); 

// x is the nullable date time object.
// y will be '-1' if x is greater than DateTime.UtcNow
Michele La Ferla
  • 6,775
  • 11
  • 53
  • 79
labroo
  • 2,901
  • 3
  • 27
  • 36
11

The compiler lifts variables and generates code to check for nulls.

> new DateTime?()
null
> DateTime.Now > null
false
> DateTime.Now < null
false

> new int?()
null
> 10 >= null
false
> 10 =< null
false
> 10 == null
false
> 10 != null
true

Knowing this you can write simple code.

// d is a DateTime? 
DateTime now = DateTime.Now;

bool after = d > now;
bool before = d < now;
bool today = d?.Date == now.Date;

If d is null everything will be false, else it will work like normal DateTime comparison.

Patrick
  • 674
  • 1
  • 8
  • 22
4

If ExpireDateTime is a Nullable<DateTime>i would do following instead:

if (ExpireDateTime.HasValue && ExpireDateTime < DateTime.Now.ToUniversalTime())
{ 
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
1

Use the Value property of the nullable:

objet.ExpireDateTime.Value

if (!object.ExpireDateTime.IsNull() 
    && DateTime.Compare(objet.ExpireDateTime.Value, 
                        DateTime.Now.ToUniversalTime()) < 0)
{ 
}    
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • There's still a smell left from the Original Question as (in the usual implementation) `System.Object` does not have a `static` member called `ExpireDateTime` (so it still won't compile). – Jeppe Stig Nielsen Dec 02 '12 at 20:24
  • @JeppeStigNielsen - `ExpireDateTime` is probably an extension method. – Oded Dec 02 '12 at 20:41
  • @Oded You can't extend like that. Even with an extension method you need an identifyer or an ordinary exression before the dot (`.`). For example `(new object()).ExpireDateTime()` or `typeof(object).ExpireDateTime()` or `@object.ExpireDateTime()` all might compile with some (really crazy) extension method. But there's something wrong with just `object`. Besides, if it's a method, you can't just say `.IsNull` without first _invoking_ `ExpireDateTime`. In short, it can never ever be valid as it stands. – Jeppe Stig Nielsen Dec 02 '12 at 20:47
  • Sorry for the confusion...object.ExpireDateTime is of type DateTime. – genxgeek Dec 02 '12 at 21:01
  • @JaJ It's not legal to write `object` "dot" `ExpireDateTime` in the code. – Jeppe Stig Nielsen Dec 02 '12 at 21:14