How do I accurately represent a Date Range in NodaTime? - c#

Goals
I have a list of LocalDate items that represent sets of start dates and end dates.
I would like to be able to store date ranges, so that I can perform operations on them as a set of overlapping or distinct ranges, etc.
Questions
Does NodaTime provide some sort of DateRange construct that I've missed in the docs?
Am I thinking about this wrong? Is there a more natural / preferred way to accomplish this that NodaTime already allows for?
Am I setting myself up for trouble by attempting to think about a date range using a LocalDate for a start and an end date?
I'm completely new to NodaTime and assuming that this is a conceptual misunderstanding on my part.
Update: I noticed a similar question on the subject from 2009, but that seems to refer to another utilies class; I'm assuming that since then NodaTime may have evolved to accomodate this situation.

Noda Time provides an Interval type for a range of Instant values, but it doesn't provide range types for the other types. Part of the reason for this is the nuance of how ranges are used for different types.
If I give you a range of instants, it is always treated as a half open interval. The start value is included, but the end value is excluded. Humans do this naturally any time we provide a time value, such as when I say an event runs from 1:00 to 2:00, clearly I mean that the event is over at 2:00, so 2:00 is excluded.
But with whole calendar date ranges, typically the end dates are inclusive. To represent the entire month of January (as a range of LocalDate values), I would probably say Jan 1st through Jan 31st, and I am including the last day in its entirety.
We could probably add some additional range types to enforce these things, but we would need to think about how much value there is in having them in the API when you could probably just create them as needed. I'm not saying I'm for or against it either way, but that's probably something to be debated on the Noda Time user group.
To answer your specific questions:
No, there is no predefined range class for local dates.
The only other thing to consider is that calendar math is usually done via the Period class. For example, to determine how many days there are between two calendar dates:
LocalDate ld1 = new LocalDate(2012, 1, 1);
LocalDate ld2 = new LocalDate(2013, 12, 25);
Period period = Period.Between(ld1, ld2, PeriodUnits.Days);
long days = period.Days;
No, there's nothing wrong with creating a range class of local dates, there just might not be a whole lot of advantage. You may do just as well by having two properties, StartDate and EndDate, on your own classes. Just be careful about the inclusiveness of the end dates, vs the exclusiveness you'd see with an interval or time range.
And lastly, you said:
... so that I can perform operations on them as a set of overlapping or distinct ranges, etc.
You're probably looking for operations like intersection, union, calculating gaps, sorting, etc. These and more are defined by the Time Period Library, but Noda Time doesn't currently have anything like that. If one was to create it, it should probably be in a companion library ("NodaTime.Ranges", perhaps?). Likely it wouldn't be desired to pull it into the core, but you never know...
If you do end up using that Time Period Library, please make sure you recognize that it works with DateTime only, and is completely oblivious to DateTimeKind. So in order to be productive with it, you should probably make sure you are only working with UTC values, or "unspecified" calendar dates, and try not to ask it things like "how many hours are in a day" because it will get it wrong for days with daylight saving time transitions.

Related

Convert discrete non-UTC date/time parts to a UTC DateTimeOffset

When using Noda Time, is there a more direct way to go from a set of discrete non-UTC values (that is to say, separate year, month, day, hour, minute values) to a UTC DateTimeOffset variable? I'm doing the following in a loop and it seems a bit odd to create LocalDateTime then apply two different timezones in a row.
DateTimeOffset target =
new LocalDateTime(year, month, day, hour, minute)
.InZoneStrictly(dataTimeZone)
.WithZone(utcTimeZone)
.ToDateTimeOffset();
I realize I'm probably overthinking/micro-optimizing, but since I'm new to Noda Time I'm mostly asking in case there is a way with fewer steps or that is better in some way. (I'm aware InZoneStrictly can throw exceptions, this is actually related to a scheduling system, so I definitely don't want invalid or auto-adjusted results.)
Your implementation is fine, though you could use DateTimeZone.Utc instead of utcTimeZone.
Here's another implementation that will give the same results:
DateTimeOffset target =
new LocalDateTime(year, month, day, hour, minute)
.InZoneStrictly(dataTimeZone)
.ToInstant()
.ToDateTimeOffset();
You might consider whether you actually need DateTimeOffset or not. Unless you're calling to some other API that requires one, you could use an OffsetDateTime or just an Instant instead.
As for the part about InZoneStrictly, consider that a user might pass in a perfectly valid set of date and time values that just happen to be ambiguous within the given time zone because they represent a local time during a backward transition (either for DST or change in standard time). Do you really want to throw in such cases?
Also, consider that if you have logic that applies a daily recurrence for the same time on multiple days, eventually you could run into an invalid time during the gap of a forward transition (again either for DST or change in standard time).
In Noda Time 2.x, InZoneLeniently was updated to generally do the right thing in such scenarios (first occurrence of ambiguous values, skip ahead for invalid values). Scheduling was a primary use case in this decision.

Generating DateTimeOffsets for a 'local' time range

I'm unsure how best to approach a date/time problem in C#. I have a dataset which lists the working hours for a set of people. Basically, 'person X works 09:00 to 17:30, Monday-Friday'. Basically two TimeSpan-s, which are assumed to be local times for any given date. From this, I need to be able to create two DateTimeOffset-s for any day of the year for a given locale.
For example, if I wanted to generate the UK-based DateTimeOffset for the following dates, I'd get the following results:
2015-01-01: From 2015-01-01T09:00:00+00:00, to 2015-01-01T17:30:00+00:00
2015-07-01: From 2015-01-07T09:00:00+01:00, to 2015-01-07T17:30:00+01:00
Obviously the difference is just the timezone - the first is GMT, the second is BST. A working day isn't normally affected by time zone changes. This is dependent on the date given.
I need to be able to do this for any specified country / region, including those whose timezones change (as demoed above).
If it matters, this will be .NET on Windows.
Thanks for any help.

.Net - Time of the day

I am working on an application that needs to set rules for periods of time. The company has different branches, each branch can set its own rules (i.e a branch starts work at 8.30 am, ends work at 17.30 pm, with 30 minutes pause for lunch; another branch start at 9.00, ends at 19.00 with 1 hour pause...)
So I need to define a class (let's call it WorkingDayDefinition for the moment) where start and end are not actually a DateTime, because they are not referred to any specific day in particular.
At the moment the only option I see in C# is using Timespan for setting a duration from the beginning of the day, so that 8.30 pm would be TimeSpan(8,30,0) to be added to the Day part of whichever day.
Is this a best practice in C#?
I searched for third parties libraries that could help me, but so far my best bet is this one:
http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET
that is not strictly what I need
You could use Noda Time. It provides a LocalTime (see here):
LocalTime is an immutable struct representing a time of day, with no reference to a particular calendar, time zone or date.
For 8.30 you would do something like:
LocalTime openingAt = new LocalTime(8, 30);
To me TimeSpam seems very suitable for what you want. It holds an interval of time, sometimes between two events, but in your case between the start of the day and the time you start/finish work. There is no reason I can think of not to use it just because the name might suggest this wasn't the original intention of the class. Plus it already integrates well with DateTimes for any time calculations you need to do later on down the road.

Why is DateTime.Now.Subtract(DateTime.UtcNow) not (close to) zero?

If I try:
DateTime.Now.Subtract(DateTime.UtcNow)
I would expect the result to be very close to zero. But it's not, instead it's the time zone difference, in my case, -4 hours. There is a .Kind -- the DateTime KNOWS the timezones are different. Why doesn't it track this for me? Is there a flavor of Subtract that DOES use Kind correctly?
(For reference, a good rundown of what each one outputs can be seen at: https://stackoverflow.com/a/3229429/237091)
Eh? The Kind property does not alter date math. It is only used by time zone methods.
You get exactly the result I would expect you to get. Not sure I understand why you were expecting zero.
There is a .Kind -- the DateTime KNOWS the timezones are different. Why doesn't it track this for me?
Because DateTime is fundamentally broken (and there's more...). IMO it should complain if you try to subtract a value of one kind from another. But no, it just uses the uninterpreted date/time in each value. Very few operations actually take any notice of the Kind, unfortunately. (If you use TimeZoneInfo, those operations do take notice of it.)
Kind was hacked into .NET 2.0; before then a DateTime value didn't even know what kind it was - if you used:
dt = dt.ToLocalTime().ToLocalTime().ToLocalTime();
it would apply the same offset change several times. The BCL team found a couple of spare bits in the binary representation, and used it for Kind.
Basically, I feel your pain. Personally I would prefer it if operations like this threw an exception - subtracting a UTC DateTime from a local DateTime or vice versa makes little sense, IMO.
As an entirely biased plug, you could use Noda Time which separates the ideas of Instant, LocalDate, LocalTime, LocalDateTime, OffsetDateTime and ZonedDateTime, and doesn't let you perform non-sensical arithmetic. Our aim is to provide a saner API than the BCL one. That doesn't necessarily mean we've succeeded, of course :)
Each DateTime object represents a local time (as opposed to a UTC time plus a time zone offset). Even if the Kind property equals UTC, it's just storing the local time at the zero time zone. If it were not a local time, there would be no reason for the UtcNow property.
DateTime does not even store the timezone. If Kind equals UTC, then at least you know it's timezone is zero, but if Kind is local or unspecified, there is no way of knowing the timezone (the Kind property equals Unspecified by default).
Therefore, the Subtract method cannot incorporate the timezone into its calculation because the timezone is unknown.

A type for Date only in C# - why is there no Date type?

In our C# project we have the need for representing a date without a time.
I know of the existence of the DateTime, however, it incorporates a time of day as well.
I want to make explicit that certain variables and method-arguments are date-based.
Hence I can't use the DateTime.Date property
What are the standard approaches to this problem?
Why is there no Date class in C#?
Does anyone have a nice implementation using a struct and maybe some extensionmethods on DateTime and maybe implementing some operators such as == and <, > ?
Allow me to add an update to this classic question:
DateOnly (and TimeOnly) types have been added to .NET 6, starting with Preview 4. See my other answer here.
Jon Skeet's Noda Time library is now quite mature, and has a date-only type called LocalDate. (Local in this case just means local to someone, not necessarily local to the computer where the code is running.)
I've studied this problem significantly, so I'll also share several reasons for the necessity of these types:
There is a logical discrepancy between a date-only, and a date-at-midnight value.
Not every local day has a midnight in every time zone. Example: Brazil's spring-forward daylight saving time transition moves the clock from 11:59:59 to 01:00:00.
A date-time always refers to a specific time within the day, while a date-only may refer to the beginning of the day, the end of the day, or the entire range of the day.
Attaching a time to a date can lead to the date changing as the value is passed from one environment to another, if time zones are not watched very carefully. This commonly occurs in JavaScript (whose Date object is really a date+time), but can easily happen in .NET also, or in the serialization as data is passed between JavaScript and .NET.
Serializing a DateTime with XML or JSON (and others) will always include the time, even if it's not important. This is very confusing, especially considering things like birth dates and anniversaries, where the time is irrelevant.
Architecturally, DateTime is a DDD value-object, but it violates the Single Responsibly Principle in several ways:
It is designed as a date+time type, but often is used as date-only (ignoring the time), or time-of-day-only (ignoring the date). (TimeSpan is also often used for time-of-day, but that's another topic.)
The DateTimeKind value attached to the .Kind property splits the single type into three, The Unspecified kind is really the original intent of the structure, and should be used that way. The Utc kind aligns the value specifically with UTC, and the Local kind aligns the value with the environment's local time zone.
The problem with having a separate flag for kind is that every time you consume a DateTime, you are supposed to check .Kind to decide what behavior to take. The framework methods all do this, but others often forget. This is truly a SRP violation, as the type now has two different reasons to change (the value, and the kind).
The two of these lead to API usages that compile, but are often nonsensical, or have strange edge cases caused by side effects. Consider:
// nonsensical, caused by mixing types
DateTime dt = DateTime.Today - TimeSpan.FromHours(3); // when on today??
// strange edge cases, caused by impact of Kind
var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
var dt = new DateTime(2016, 3, 27, 2, 0, 0); // unspecified kind
var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt); // side effect!
Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!
In summary, while a DateTime can be used for a date-only, it should only do so when when every place that uses it is very careful to ignore the time, and is also very careful not to try to convert to and from UTC or other time zones.
I suspect there is no dedicate pure Date class because you already have DateTime which can handle it. Having Date would lead to duplication and confusion.
If you want the standard approach look at the DateTime.Date property which gives just the date portion of a DateTime with the time value set to 12:00:00 midnight (00:00:00).
I've emailed refsrcfeedback#microsoft.com and that's their answer
Marcos, this is not a good place to ask questions like these. Try http://stackoverflow.com
Short answer is that you need a model to represent a point in time, and DateTime does that, it’s the most useful scenario in practice. The fact that humans use two concepts (date and time) to mark points in time is arbitrary and not useful to separate.
Only decouple where it is warranted, don’t do things just for the sake of doing things blindly. Think of it this way: what problem do you have that is solved by splitting DateTime into Date and Time? And what problems will you get that you don’t have now? Hint: if you look at DateTime usages across the .NET framework: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references
You will see that most are being returned from a method. If we didn’t have a single concept like DateTime, you would have to use out parameters or Tuples to return a pair of Date and Time.
HTH,
Kirill Osenkov
In my email I'd questioned if it was because DateTime uses TimeZoneInfo to get the time of the machine - in Now propriety. So I'd say it's because "the business rules" are "too coupled", they confimed that to me.
I created a simple Date struct for times when you need a simple date without worrying about time portion, timezones, local vs. utc, etc.
https://github.com/claycephus/csharp-date
System.DateOnly and System.TimeOnly types were recently added to .NET 6, and are available in the daily builds.
They were included with the .NET 6 Preview 4 release.
See https://github.com/dotnet/runtime/issues/49036
They are in the .NET source code here:
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
I've blogged about them here.
If you need to run date comparisons then use
yourdatetime.Date;
If you are displaying to the screen use
yourdatetime.ToShortDateString();
Allow me to speculate: Maybe it is because until SQL Server 2008 there was no Date datatype in SQL so it would be hard so store it in SQL server?? And it is after all a Microsoft Product?
Who knows why it's that way. There are lots of bad design decisions in the .NET framework. However, I think this is a pretty minor one. You can always ignore the time part, so even if some code does decide to have a DateTime refer to more than just the date, the code that cares should only ever look at the date part. Alternatively, you could create a new type that represents just a date and use functions in DateTime to do the heavy lifting (calculations).
Why? We can only speculate and it doesn't do much to help solve engineering problems. A good guess is that DateTime contains all the functionality that such a struct would have.
If it really matters to you, just wrap DateTime in your own immutable struct that only exposes the date (or look at the DateTime.Date property).
In addition to Robert's answer you also have the DateTime.ToShortDateString method. Also, if you really wanted a Date object you could always use the Adapter pattern and wrap the DateTime object exposing only what you want (i.e. month, day, year).
There is always the DateTime.Date property which cuts off the time part of the DateTime. Maybe you can encapsulate or wrap DateTime in your own Date type.
And for the question why, well, I guess you'll have to ask Anders Heljsberg.
Yeah, also System.DateTime is sealed. I've seen some folks play games with this by creating a custom class just to get the string value of the time as mentioned by earlier posts, stuff like:
class CustomDate
{
public DateTime Date { get; set; }
public bool IsTimeOnly { get; private set; }
public CustomDate(bool isTimeOnly)
{
this.IsTimeOnly = isTimeOnly;
}
public string GetValue()
{
if (IsTimeOnly)
{
return Date.ToShortTimeString();
}
else
{
return Date.ToString();
}
}
}
This is maybe unnecessary, since you could easily just extract GetShortTimeString from a plain old DateTime type without a new class
Because in order to know the date, you have to know the system time (in ticks), which includes the time - so why throw away that information?
DateTime has a Date property if you don't care at all about the time.
If you use the Date or Today properties to get only the date portion from the DateTime object.
DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;
Then you will get the date component only with the time component set to midnight.

Categories