FluentDateTime: Ruby-style date arithmetic for C#
Allows cleaner DateTime expressions and operations
At a glance
- What is it?
- FluentDateTime is a small C# extension library that lets you write DateTime.Now - 1.Weeks() - 3.Days() + 14.Minutes() and 2022.March(23).At(10, 26), taking its inspiration from the Ruby DateTime extensions. The surface is small and pleasant, and the newest release on NuGet is from 2022 even though the repository is still being pushed to.
- Who is it for?
- Adopt FluentDateTime if you write a lot of date arithmetic in C# and want it to read the way the Ruby extensions do, particularly for relative periods and for constructing dates in tests. Do not adopt it if you need time zone aware scheduling, since the library is built around DateTime and DateTimeOffset only, and do not expect it to answer calendar questions such as month lengths, because the README does not document any calendar-aware behaviour.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A period expressed as a value, not a constructor call
FluentDateTime allows cleaner DateTime expressions and operations. The description is modest, and the way it delivers on it is a set of extension methods that make a period of time into an object you can hold and use.
The README's first example is arithmetic with periods as the operands:
// DateTime operations
DateTime.Now - 1.Weeks() - 3.Days() + 14.Minutes()
DateTime.Now + 5.Years()The mechanism is that a method such as 1.Weeks() returns a value representing one week, and the operator overloads on DateTime know how to subtract it. So 1.Weeks() is not a method call on the date at all, it is a value in its own right, and it can be stored in a variable, passed to a function or used in the middle of a longer expression. That is what makes the resulting expression read as a sentence about time rather than as a chain of constructor calls.
The comparison with the built-in way is worth making. The alternative in plain C# is TimeSpan.FromDays(7) or new TimeSpan(7, 0, 0, 0), and both read as a number of something rather than as a duration named in the same words as the unit you care about. The library's contribution is mostly lexical, and that is not nothing: date code is read far more often than it is written.
The library is inspired by the Ruby DateTime extensions, and the README links the two Active Support pages it follows, the extensions to DateTime and the extensions to Time. That is a useful signal for a .NET developer, because the semantics of those Ruby extensions are widely known and the naming here follows them.
Relative evaluations that read backwards from now
The second family is relative evaluation, where the library attaches a method to a period rather than to a date:
// Relative DateTime evaluations
3.Days().Ago()
2.Days().Since(DateTime.Now)Ago() is the notable one. It takes no argument and returns the date that was three days before the current moment, which means the value depends on when it is called, and the README groups it under relative DateTime evaluations rather than operations. That distinction matters when you read a chain like 3.Days().Ago() in a test: it is not a constant, and two calls a second apart can differ.
Since(DateTime.Now) is the same idea with an explicit reference point, and it reads better than the equivalent subtraction because it names the direction. Both forms put the period first and the reference point last, which is the opposite of the arithmetic example where the date comes first. The library covers both orderings, and the fluency comes from the fact that the same period object serves both.
This is also where the design has a cost. An expression that hides the current time inside a method call is harder to test and harder to reason about in a system where the clock matters, and for anything where the reference moment is a domain concept, a business date rather than the wall clock, the explicit form is the one to use.
Fluent estimates and direct time manipulation
The third group in the README is labelled fluent DateTime estimations, and these are the operations that move you to a named point rather than by an offset:
// Fluent DateTime estimations
DateTime.Now.NextDay()
DateTime.Now.NextYear()
DateTime.Now.PreviousYear()
DateTime.Now.WeekAfter()
DateTime.Now.Midnight()
DateTime.Now.Noon()NextDay, NextYear and PreviousYear move by a unit, and WeekAfter moves by a week. Midnight and Noon are different in kind: they collapse the time of day component to a fixed value, which is the operation you want before truncating a timestamp to a day boundary or when you need the start of the following midnight.
The fourth group is direct manipulation of the current DateTime:
// Current DateTime manipulation
DateTime.Now.SetTime(11, 55, 0)SetTime replaces the hour, minute and second while keeping the date part. Note the naming, SetTime rather than something like WithTime, which in .NET usually implies returning a new value rather than mutating in place. The README groups these under current DateTime manipulation, which suggests the date being manipulated is read as the current moment.
Whether these mutate or return a copy is not something the README states, and that distinction is the kind of thing to check in the source or the unit tests before relying on it, because a mutating SetTime in a chain like DateTime.Now.SetTime(11, 55, 0).AddDays(1) behaves very differently from an immutable one.
Date literals and the implicit conversion to DateTime
The last group is the one that most directly attacks the .NET date construction problem, the date literal:
// DateTime expressions
2022.Mar(23) // (implicitly convertible to DateTime and DateTimeOffset)
2022.March(23).At(10, 26)The literal form writes 2022.Mar(23) rather than new DateTime(2022, 3, 23). The comment in the README notes that the result is implicitly convertible to DateTime and DateTimeOffset, which is what makes the syntax work in an expression without an explicit cast. The full month name is accepted as well as the short form.
At(10, 26) then attaches a time of day to the date, giving 2022.March(23).At(10, 26) as a single fluent expression rather than a constructor with six arguments. The contrast with the standard constructor is the argument: new DateTime(2022, 3, 23, 10, 26, 0) is legal and unambiguous, but it is a sequence of raw integers in a fixed order where a mistake in position compiles fine and means something different.
Because the conversion is implicit, a literal can be handed to anything that takes a DateTime, which is what makes the form usable in test setup and in configuration. The limitation is the same as the underlying type: the literal is a local time with no offset, and DateTimeOffset only appears because of the conversion, not because the literal carries offset information.
A small package with an old published release
The repository layout is minimal: a src/ directory holding the library and its tests, a global.json file, an .editorconfig, a license.txt and a readme.md. The project is MIT licensed and distributed on NuGet as FluentDateTime, with a build status badge wired to a GitHub Actions workflow.
The release history is the fact to weigh. The tags are 1.15.0 and 2.0.0, both dated 2020-01-26, and 2.1.0 dated 2022-05-03. The README says to see the project milestones for release notes, and there is a Milestones link pointing at the closed milestones list.
The last push to the repository was on 2026-10-14 and the repository is not archived, so work is happening on the branch even though the newest published package is more than four years old. That gap is the interesting part. It is normal for a small utility library to sit unchanged on NuGet for years, because the surface is small and the target framework rarely moves. It is also normal for the same library to need a release when a new runtime lands and the old target framework stops being supported, and the difference between those two situations is exactly what you cannot see from the version list alone.
The global.json file at the repository root is the piece of the layout worth noting. In a .NET repository it pins the SDK version the project builds with, which for a library means the toolchain is reproducible for contributors, and it also hints at which runtime generation the code was written against.
Where a fluent date library stops earning its place
The first limitation is that the whole surface is built on DateTime and DateTimeOffset, and neither of those types carries time zone information. A library that makes it pleasant to say 3.Days().Ago() is not going to make a daylight saving transition correct, and the implicit conversions it offers carry no offset either. Any scheduling work that spans zones still has to reason about offsets explicitly, and the fluent syntax does not change that.
The second limitation is whether the operations are immutable. DateTime and DateTimeOffset are value types, and the framework's own arithmetic returns new values rather than changing the original, so a fluent library that adds periods and a SetTime style method has to pick a model. If a method such as SetTime mutates a held object rather than returning a copy, the library becomes the one place in a codebase where a date can change underneath you, and that inconsistency is a genuine cost. The README does not state which model it uses, so check the unit tests under src/Tests before you rely on either behaviour.
The third is that the arithmetic does not stop at the units. A library of this shape invites you to write 1.Weeks() + 30.Days() and then wonder about month lengths, since a fixed number of days is a different thing from a calendar month. The README does not document any calendar-aware behaviour, so treat the year and month units as approximations unless the unit tests say otherwise.
The real alternative is the plain framework, and the difference in approach is mostly lexical rather than functional. Writing the arithmetic directly with the built-in DateTime and TimeSpan types gives you the same result and keeps every operation inside the framework's own semantics, with no third party package and no question about which model the extension follows. What you give up is the sentence-like readability that the fluent form provides, and the date literals that replace a wall of constructor arguments. A library like this earns its place most convincingly in a codebase where date expressions are the dominant shape of the logic, in test setup where constructing a specific date is repeated constantly, and in older projects that have not standardised on a different approach. In a small codebase with a handful of dates, the dependency is more ceremony than the expression saves.
Editorial conclusion
Adopt FluentDateTime if you write a lot of date arithmetic in C# and want it to read the way the Ruby extensions do, particularly for relative periods and for constructing dates in tests. Do not adopt it if you need time zone aware scheduling, since the library is built around DateTime and DateTimeOffset only, and do not expect it to answer calendar questions such as month lengths, because the README does not document any calendar-aware behaviour. Before you pull it in, check the target framework the package supports, since the newest published release is 2.1.0 from 2022-05-03, and read the unit tests under src/Tests to see whether operations like SetTime return a new value or change the receiver.
Frequently asked questions
What is FluentDateTime?
It is a C# library of extension methods that allows cleaner DateTime expressions and operations, distributed on NuGet as FluentDateTime under the MIT licence. It is inspired by the Ruby DateTime extensions, and the README links the Active Support pages it follows.
How do I write a relative date with FluentDateTime?
The README shows 3.Days().Ago() for a relative evaluation and 2.Days().Since(DateTime.Now) when you want to name the reference point. For arithmetic, periods are operands: DateTime.Now - 1.Weeks() - 3.Days() + 14.Minutes().
How do I create a date with FluentDateTime?
The date literal form is 2022.Mar(23) or 2022.March(23), and a time can be attached with At, giving 2022.March(23).At(10, 26). The result is implicitly convertible to DateTime and to DateTimeOffset.
Is FluentDateTime still maintained?
The repository is not archived and the last push was on 2026-10-14, but the most recent published release is 2.1.0 from 2022-05-03, with 2.0.0 and 1.15.0 both dated 2020-01-26. The README directs readers to the project milestones for release notes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fluentdatetime-fluentdatetime)Was this useful?