Some months ago I was trying to create a timeline of British
pre-history and needed to be able to store dates. The .NET
DateTime
or DateOnly
structures are completely unsuitable
for these as their ranges are far too small, nor do they allow
for partial dates.
I did spent a little time poking around to see if there was some existing code, but there doesn't seem to be a lot of detail out there. I found a malformed blog post which was missing the bulk of the source code, and HistoricalDate class which was more fully fleshed, but still not fully suitable. So as typical, I went my own way and wrote my own.
Getting the library
The easiest way of obtaining the library is via NuGet.
Install-Package Cyotek.HistoricalDate
If you don't use NuGet, pre-compiled binaries can be obtained from the GitHub Releases page.
Of course, you can always grab the source and build it yourself!
About this library
The library currently offers two read-only structs, JulianDate
and HistoricalTimeSpan
.
JulianDate
The JulianDate
structure can represent a partial date between
2147483647 BP and 2147483647 AD. By partial, I mean that a year
and the era are always required, but the month and/or day is
optional. After all, a date such as the Battle of Hastings may
be documented but the start of the Mesolithic is a little more
nebulous!
HistoricalTimeSpan
The HistoricalTimeSpan
is a cut down version of the more
familiar TimeSpan
and is currently mainly used by the
Add
and Subtract
methods of a JulianDate
instance.
Using the library
Constructing instances
There are several constructors for specifying fully qualified or partial dates.
You can also use an explicit operator to convert the date
portion of a DateTime
instance into a JulianDate
.
Parsing strings
You can also try and parse a string into a JulianDate
instance.
Note: String parsing (and formatting) is somewhat basic (and potentially confusing) and will be improved in future updates to the library. Except with regards to month names, parsing is not culture-aware.
Partial Dates
JulianDate
supports partial dates, where only part of a date
is known. The year and era are always required, but month and
day are optional.
If day is specified, then the month is also available. The
HasDay
and HasMonth
properties allow you to query what
partial components are set, or if you just want to know if a
date is fully known or partial, the IsFullyKnown
and
IsPartial
properties can be used.
Accessing the Month
or Day
properties will return 0
if the
component has not been set.
Leap Years
The static IsLeapYear
method will return if a given year is a
leap year. This is calculated according to Scaliger.
Things to improve
Currently, I'm unhappy with the string parsing as the list of accepted formats is too vague, and I haven't yet built in culture support.
The Subtract
method could have serious performance issues when
used with massive AD dates.
Requirements
.NET Framework 2.0 or later.
Pre-built binaries are available via a signed NuGet package containing the following targets.
- .NET 3.5
- .NET 4.0
- .NET 4.5.2
- .NET 4.6.2
- .NET 4.7.2
- .NET 4.8
- .NET 5.0
- .NET Standard 2.0
- .NET Standard 2.1
- .NET Core 2.1
- .NET Core 3.1
Is there a target not on this list you'd like to see? Raise an issue, or even better, a pull request.
Acknowledgements
- Inspiration gleaned from Representing Large AD and BC Dates in C# and cerinman/HistoricalDate
- The NuGet package icon is from the Multimedia Solid 24px icon set by amoghdesign. Licensed under Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0).
Like what you're reading? Perhaps you like to buy us a coffee?