UPDATE: @SimonCropp just tweeted a link to his FluentDateTime library which handles some of the extensions below. It's even available via NuGet. Nice one.
One of the ways I like to keep my code readable and expressive is around anything to do with the DateTime
and TimeSpan
classes. Using DateTime
or TimeSpan
directly can be a little hard to comprehend when reading later. Here's an example. Say we have an NHibernate Linq query retrieving all widgets that are less than a day old.
var widgets = session.Query<Widget>()
.Where(w => w.CreatedOn > DateTime.Now.AddDays(-1));
Forget the Linq in this example, the bit I find hard to read is the date we're comparing with.
w.CreatedOn > DateTime.Now.AddDays(-1)
The code isn't immediately communicating its intent. It's the sort of code that really begs for a quick comment.
var widgets = session.Query<Widget>()
// Get widgets that are less than a day old.
.Where(w => w.CreatedOn > DateTime.Now.AddDays(-1));
The problem with adding a comment however is that the code is now repeated in English. Changing one now requires a change to the other. If these ever get out of sync chaos will abound, cats and dogs will rain from the sky, or worse, the technical lead may get a headache. Keep it DRY (yes, I think most comments violate DRY) and readable. Enter extension methods and a neat little trick from Ruby on Rails.
w.CreatedOn > 1.Day().Ago()
How do we achieve such awesomeness? It's surprisingly easy. First we create an extension method that gives us the time span we care about.
public static class TimeSpanHelper
{
public static TimeSpan Days(this int days)
{
return TimeSpan.FromDays(days);
}
}
We only care about one day in this particular example and it's not hard to cater for the singlular form.
public static TimeSpan Day(this int day)
{
return Days(day);
}
Next, we create something that can cast our time span into the past.
public static class DateTimeHelper
{
public static DateTime Ago(this TimeSpan timeSpan)
{
return DateTime.Now.Subtract(timeSpan);
}
}
And the future (while we're here).
public static DateTime FromNow(this TimeSpan timeSpan)
{
return DateTime.Now.Add(timeSpan);
}
Now we can write the Linq query like so.
var widgets = session.Query<Widget>()
.Where(w => w.CreatedOn > 1.Day().Ago());
Much clearer. I don't think that code needs a comment. Nothing to get out of sync either. Your technical lead may still get a headache from the extension methods, but we can't have it all. ;)
Of course TimeSpanHelper
can be extended to cater for seconds, minutes, hours, etc. Here's one more example. Say we wanted to find the time half a week from now. There's a few ways to do that.
84.Hours().FromNow();
(3.Days() + 12.Hours()).FromNow();
3.5.Days().FromNow();
0.5.Weeks().FromNow();
The last two require extending double
instead of int
, like this.
public static TimeSpan Weeks(this double weeks)
{
return TimeSpan.FromDays(weeks * 7);
}
The double
extensions can be handy at times, but I prefer to use int
whenever I can. I think it's a little more readable.
Before I go, what do you think of this statement?
w.CreatedOn.IsNewerThan(1.Day().Ago())
I'll leave it up to you to implement.
There are 0 comments.
browse with Pivot
Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue
Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)
Comments
Leave a Comment
Please register or login to leave a comment.