Converting DateTimes by Offsets with NodaTime
There must be a simple way to convert one DateTime
to another DateTime
using only a GMT/UTC Offset integer… right?
What do I mean by DateTime
?
- The local time on my watch?
- The present global moment in time?
There are constraints on converting between and within these Times. For example, Offset != Time Zone so you must be aware of ambiguities resulting from Daylight Savings Time transitions.
Time is difficult, especially when you start thinking about it. Big mistakes can be made when you don’t think about it.
BCL or No?
There are ways to manipulate Time with the BCL’s DateTime
and DateTimeOffset
classes. However, with its DateTimeKind
property, DateTime
violates the Single Responsibility Principle. It can have different behaviors based on this property (Matt Johnson)
Let’s rephrase the original problem so I can to introduce you to a library that I guarantee will help you work with Time more clearly.
Is there a simple way to:
- Given a UTC Offset integer in hours
- *Convert one Global
DateTime
to another GlobalDateTime
by UTC Offset?*
There is a simple way, and NodaTime lets us be clear about what we’re trying to do.
NodaTime
Noda Time is an alternative date and time API for .NET. It helps you to think about your data more clearly, and express operations on that data more precisely. - NodaTime.org
NodaTime forces you to make decisions about your use case for Time. This is a good thing. Beyond a slight learning curve, it makes Time easier.
In our case, we have a Global Instant - a moment that the world is observing. Let’s say this Instant at UTC (Offset = 0) is 2016-01-21T20:06:00Z
Instant instant = Instant.FromUtc(2016, 1, 21, 20, 6);
// 2016-01-21T20:06:00Z
What Time would this be at UTC-05?
Since we don’t know what Time Zone we are in, we can use Noda’s OffsetDateTime
class. “It preserves the offset information you genuinely have, but without giving the impression of knowing the “real” time zone” (NodaTime User Guide)
Offset offset = Offset.FromHours(-5);
OffsetDateTime sourceOffsetDateTime = instant.WithOffset(offset);
// 2016-01-21T15:06:00-05
It’s that simple… for this use case.
If we were given a Local DateTime
, we could use Noda’s LocalDateTime
class. If we were concerned about Daylight Savings Time and other Time Zone rules, we could use Noda’s ZonedDateTime
class.
NodaTime gives you clear separation of concerns with its Instant
, LocalDateTime
, OffsetDateTime
, and ZonedDateTime
classes.
Summary
We asked if there was a simple way to manipulate DateTime
by UTC Offset.
While there are ways to do this with the BCL’s DateTime
classes, NodaTime forces you to be aware of the different representations of Time and their constraints.
Noda provided us with Instant
and OffsetDateTime
to help us stay honest about what we could do with UTC Offset. It turned out to be a simple calculation once we clarified our use case.
Some sample code where I was testing a few different calculations with both NodaTime and the BCL can be found here.
As always, stay tuned by sign up for my newsletter. I look forward to hearing from you! Have a great day!
Sources:
- timeanddate.com. The Difference Between GMT and UTC
- NodaTime.org. NodaTime Core Types Quick Reference
- NodaTime Source. Github
- Matt Johnson. Code of Matt
- Matt Johnson. Code of Matt. Beware the Edge Case of Time
- Matt Johnson. NodaTime Blog. What’s Wrong with DateTime anyway?
- Matt Johnson. TimeZoneNames Library
- Stack Overflow. DateTime vs DateTimeOffset