481

What's the quickest and easiest way to get the Min (or Max) value between two dates? Is there an equivalent to Math.Min (& Math.Max) for dates?

I want to do something like:

 if (Math.Min(Date1, Date2) < MINIMUM_ALLOWED_DATE) {
      //not allowed to do this
 }

Obviously the above Math.Min doesn't work because they're dates.

hawbsl
  • 15,313
  • 25
  • 73
  • 114

11 Answers11

631

There's no built in method to do that. You can use the expression:

(date1 > date2 ? date1 : date2)

to find the maximum of the two.

You can write a generic method to calculate Min or Max for any type (provided that Comparer<T>.Default is set appropriately):

public static T Max<T>(T first, T second) {
    if (Comparer<T>.Default.Compare(first, second) > 0)
        return first;
    return second;
}

You can use LINQ too:

new[]{date1, date2, date3}.Max()
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 2
    The `LINQ` way is the best way! – Serj Sagan Feb 14 '23 at 06:15
  • @SerjSagan `LINQ` is the **worst** way. It has the slowest speed and allocates additional memory. See my post regarding benchmarking: https://stackoverflow.com/a/75869992/9232174 – Will Bixler Mar 28 '23 at 18:52
  • 1
    It's not always about performance, in fact, if performance is not an issue then clean readable code is FAR more important. – Serj Sagan Mar 30 '23 at 09:59
500

There is no overload for DateTime values, but you can get the long value Ticks that is what the values contain, compare them and then create a new DateTime value from the result:

new DateTime(Math.Min(Date1.Ticks, Date2.Ticks))

(Note that the DateTime structure also contains a Kind property, that is not retained in the new value. This is normally not a problem; if you compare DateTime values of different kinds the comparison doesn't make sense anyway.)

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 2
    this seemed to me to be closest to a one line replacement/equivalent for Math.Min but the other answers were great too, for completeness – hawbsl Jan 04 '10 at 10:07
  • 7
    -, you completely loose every information (except the ticks) of the original `System.DateTime`-instance –  May 08 '12 at 08:15
  • 2
    @AndreasNiedermair: The only information that exist in a `DateTime` value is the `Ticks` and the `Kind` properties. All other properties are derived from those. – Guffa May 08 '12 at 08:58
  • 1
    @Guffa `Kind` is the main issue when talking about and/or using globalization - sorry for considering `Kind` as the main benefit and therefore using *every information* :) –  May 08 '12 at 09:08
  • 11
    @AndreasNiedermair: If you are comparing dates of different kinds, you can't just compare them straight off anyway. I already covered this in the answer. – Guffa May 08 '12 at 09:13
  • @Guffa thanks for clarification and sry - but still ... creating a new `System.DateTime`-instance for each comparison-result seems *wrong* for me ... (even that i am unsure of the *magic* performance impact ...) –  May 08 '12 at 09:48
  • @AndreasNiedermair: The difference from copying the value is minimal. The constructor makes a range check on the `Ticks` value, other than that it's just an assignment. – Guffa May 08 '12 at 11:55
  • 1
    @Guffa "can't just compare them straight off anyway" however this code thinks that it can... – Iain Jan 02 '13 at 07:47
  • 6
    @Iain: You always *can* compare them, but if you compare values that are not comparable, the outcome is useless. It's just the same as with any other values that are not comparable, like comparing a distance in kilometers with a distance in miles; you can compare the values just fine, but it doesn't mean anything. – Guffa Jan 02 '13 at 12:36
  • 4
    -1 for handwaving about Kind. As shown in @user450 's answer, it's quite easy to convert both times to UTC and compare them safely without discarding information. – piedar Mar 03 '17 at 17:29
  • 2
    Interresting idea. Unfortunately, 1: DateTime.Kind is lost, 2: it involves many additional computation than a (x > y ? x : y) - As it is valid, this should not be the accepted answer. – Larry Apr 12 '19 at 08:39
59

Linq.Min() / Linq.Max() approach:

DateTime date1 = new DateTime(2000,1,1);
DateTime date2 = new DateTime(2001,1,1);

DateTime minresult = new[] { date1,date2 }.Min();
DateTime maxresult = new[] { date1,date2 }.Max(); 
Toshi
  • 2,532
  • 4
  • 17
  • 45
41

How about:

public static T Min<T>(params T[] values)
{
    if (values == null) throw new ArgumentNullException("values");
    var comparer = Comparer<T>.Default;
    switch(values.Length) {
        case 0: throw new ArgumentException();
        case 1: return values[0];
        case 2: return comparer.Compare(values[0],values[1]) < 0
               ? values[0] : values[1];
        default:
            T best = values[0];
            for (int i = 1; i < values.Length; i++)
            {
                if (comparer.Compare(values[i], best) < 0)
                {
                    best = values[i];
                }
            }
            return best;
    }        
}
// overload for the common "2" case...
public static T Min<T>(T x, T y)
{
    return Comparer<T>.Default.Compare(x, y) < 0 ? x : y;
}

Works with any type that supports IComparable<T> or IComparable.

Actually, with LINQ, another alternative is:

var min = new[] {x,y,z}.Min();
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • For the first method, it can just be `return values.Min();` after the null check. – Him Aug 12 '20 at 01:51
30

If you want to use use Linq.Max() but call it more like Math.Max, you can do something like this very short expression body:

public static DateTime Max(params DateTime[] dates) => dates.Max();
[...]
var lastUpdatedTime = DateMath.Max(feedItemDateTime, assemblyUpdatedDateTime);
R. Salisbury
  • 1,954
  • 16
  • 17
20
public static class DateTool
{
    public static DateTime Min(DateTime x, DateTime y)
    {
        return (x.ToUniversalTime() < y.ToUniversalTime()) ? x : y;
    }
    public static DateTime Max(DateTime x, DateTime y)
    {
        return (x.ToUniversalTime() > y.ToUniversalTime()) ? x : y;
    }
}

This allows the dates to have different 'kinds' and returns the instance that was passed in (not returning a new DateTime constructed from Ticks or Milliseconds).

[TestMethod()]
    public void MinTest2()
    {
        DateTime x = new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc);
        DateTime y = new DateTime(2001, 1, 1, 1, 1, 1, DateTimeKind.Local);

        //Presumes Local TimeZone adjustment to UTC > 0
        DateTime actual = DateTool.Min(x, y);
        Assert.AreEqual(x, actual);
    }

Note that this test would fail East of Greenwich...

user450
  • 415
  • 4
  • 3
10

For anybody wondering about the performance of some of the answers previously provided, I ran benchmarks on a few of them. I used ternary operators, LINQ, and Math.max to determine the best solution for speed and memory.

Ternary

return a > b ? a : b;
  • Average Speed: 0.0198 nanoseconds
  • Additional Memory Allocation: none
  • Garbage Collection Time: none

Math.max

return new DateTime(Math.Max(a.Ticks, b.Ticks));
  • Average Speed: 2.4346 nanoseconds
  • Additional Memory Allocation: none
  • Garbage Collection Time: none

LINQ

return new[]{a, b}.Max();
  • Average Speed: 22.8383 nanoseconds
  • Additional Memory Allocation: 72 bytes
  • Garbage Collection Time: 0.0057 nanoseconds
Environment

.Net Version: 6.0

C# Version: 10.0

Configuration: Release

References

Benchmarking was done using BenchmarkDotNet 0.13.5

DateTime comparison benchmarks


TLDR

A ternary operator is the most performant method by far. It performs over 100x faster than the runner-up and allocates no additional memory.

Will Bixler
  • 151
  • 1
  • 7
7

How about a DateTime extension method?

public static DateTime MaxOf(this DateTime instance, DateTime dateTime)
{
    return instance > dateTime ? instance : dateTime;
}

Usage:

var maxDate = date1.MaxOf(date2);
Oundless
  • 5,425
  • 4
  • 31
  • 33
  • I would prefer to use it as `DateTime.MaxOf(dt1, dt2)`, but I don't know how to do that... – Zach Smith Jul 03 '18 at 12:01
  • 1
    @ZachSmith You cannot overload the DateTime class because it is not partial. Maybe we can use DateTimeHelper.Max(dt1, dt2) – shtse8 Aug 06 '18 at 22:16
  • 1
    @shtse8 - what do you mean by overloading? i was thinking of adding an extension method. but have since learnt that cannot be done without an instantiated instance. sounds like you are saying that adding an extension method is a form of overloading? i've never considered that.. is that correct? now that I think about it... what exactly does overloading mean? – Zach Smith Aug 08 '18 at 09:51
  • @ZachSmith Oh, I thought you are adding an extension called "DateTime.MaxOf(dt1, dt2)" just like "DateTime.Now" which is based on DateTime static members. – shtse8 Aug 08 '18 at 10:34
2

Put these two methods in a Utility class and use them to get Min/Max of any number of DateTimes:

public static DateTime Min(params DateTime[] dates)
{
    if (dates.Length == 1) return dates[0];

    long minTicks = dates[0].Ticks;

    for (int i = 1; i < dates.Length; i++)
    {
        minTicks = Math.Min(minTicks, dates[i].Ticks);
    }

    return new DateTime(minTicks);
}

public static DateTime Max(params DateTime[] dates)
{
    if (dates.Length == 1) return dates[0];

    long maxTicks = dates[0].Ticks;

    for (int i = 1; i < dates.Length; i++)
    {
        maxTicks = Math.Max(maxTicks, dates[i].Ticks);
    }

    return new DateTime(maxTicks);
}
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
ralofpatel
  • 49
  • 3
-1

We can convert dates into Number primitive with Date.parse(), then we can use Math.min() and Math.max() for processing and storing. With that primitive we can render in any format we want. It's a 2-3 steps process, but we virtually eliminates the risk of getting funky results.

const unixTimeZero = Date.parse('01 Jan 1970 00:00:00 GMT');
const javaScriptRelease = Date.parse('04 Dec 1995 00:12:00 GMT');
const today = Date.parse(new Date());
const d1 = Date.parse(new Date("2004-02-01"));
const d2 = Date.parse(new Date("2017-01"));
const d3 = Date.parse(new Date("2018"))
const t = [unixTimeZero, d1, d2, d3, today, javaScriptRelease];
const min = Math.min(...t)
const max = Math.max(...t)
console.log(unixTimeZero); // expected output: 0
console.log(javaScriptRelease); // expected output: 818035920000
console.log(today);
console.log(t);
console.log(["unixMin: " + min, "earliestDate: " + new Date(min).toUTCString()]);
console.log(["unixMax: " + max, "latestDate: " + new Date(max).toDateString()]);
imstoopid
  • 9
  • 3
-6
// Two different dates
var date1 = new Date(2013, 05, 13); 
var date2 = new Date(2013, 04, 10) ;
// convert both dates in milliseconds and use Math.min function
var minDate = Math.min(date1.valueOf(), date2.valueOf());
// convert minDate to Date
var date = new Date(minDate); 

http://jsfiddle.net/5CR37/

  • Good suggestion (really), but without description it's hard to find why it is good. Please describe where the trick is. Without the description it's just a block of code and not an answer. – Artemix Nov 07 '13 at 10:44
  • 4
    question is tagged .NET not Javascript – hawbsl Nov 07 '13 at 11:37
  • Sorry, didn't notice .NET. Anyway, we can find min date on client side and send it to server. – Sergey Suvorov Nov 08 '13 at 11:46
  • 4
    You assume that we are talking about asp, which is not necessarily right. It also would be quite stupid to calcluate the difference on the Client, because there are far more risks (JavaScript disabled) , it would generate unnecessary traffic and it would be (depending on the network speed) slower – jalgames Jul 21 '14 at 14:03
  • 1
    Create a datecomparison microservice running in node.js and then you can call it to answer the original question – stannius Jan 12 '21 at 01:49