Reputation: 1019
As a software architect, I would really like to use NodaTime but it's concept of 'Local' is making our code extremely difficult to work with (and it is driving me and my team nuts)! The documentation, when referring to "local" and "global" times states the following:
The key difference is that people all around the world will agree on a global value simultaneously, whereas they may all see different local values for the same global value, due to time zones.
This sentence is defining what Global time means by using the definition of Local time. Therefore, before we can understand Global time we must first understand what Local time means.
Google (and many other dictionaries similarly) defines "Local Time" as:
Time as reckoned in a particular region or time zone.
- time at a particular place as measured from the sun's transit over the meridian at that place, defined as noon.
So, Local time is a specific time that is defined by (or is bound to) a time zone - got it. Now we fully understand what Global time means; okay, next sentence:
A local value has no associated time zone in Noda Time;
Wait! What the...?! The documentation just got done defining Global time by using the universally accepted definition of Local time (which is associated to a time zone) and now they decides to completely negates its meaning, if that's true, what does Global time now mean? IMHO, absolutely nothing.
The documentation goes on to state...
in particular it is not just "a date and time fixed to the local time of the computer running the code"
I have two problems with this statement:
X is equal to (NOT X) is true
.I vote that we use a better definition than 'Local', how about the word 'Raw'? Which means:
Being in or nearly in the natural state: not processed or purified.
Hmmm....
A [raw] value has no associated time zone in Noda Time; in particular it is not just "a date and time fixed to the local time of the computer running the code"
Now that sounds more like the definition the documentation is trying to convey.
Or perhaps, someone else can come up with a better word than 'Raw'; but the point is, please give us back the real meaning of 'Local' since 'Local' has a very real and very useful meaning in software and in life.
Therefore, can we rename the Local[Date][Time]
classes to Raw[Date][Time]
and (optionally) add new classes called Local[Date][Time]
that is bound to the computer's Local time zone?
@Michal & @J... -
The problem we are having isn't understanding LocalDateTime
in 'our' local (because it is zoneless). The problem we are having with LocalDateTime is that it is zoneless. In essence, LocalDateTime
should be, by definition, a special type of ZonedDateTime
. (Just like UTC is conceptually (and concretely) a special type of ZonedDateTime
. Thus, UTC could have it's own set of types, like: Utc[Date][Time]
; which, given the right problem domain, could be very useful. And because UtcDateTime
hasn't been 'taken' we could create such a concept without interfering with NodaTime.)
Back to our problem, for example: if I have two ZonedDateTimes, one from New York and the other from Seattle and I want to study these two ZonedDateTimes from my current location (let's say: Dallas). I would like to write...
LocalDateTime fromNy = newYork.LocalDateTime;
LocalDateTime fromWa = seattle.LocalDateTime;
But instead, I get two unrelated local date times that have absolutely no relationships to each other. In fact, if it wasn't for the var names I wouldn't know which is which.
What I want, is two ZonedDateTimes that have been converted to my local time zone, as the property name suggests. Now, these two LocalDateTime values (where LocalDateTime is a special type of ZonedDateTime) have relevant meaning. I know I can get some what close to what I want by doing the following; but it is messy, doesn't convey the same semantics and it looses the time zone information.
DateTimeZone ct = BclDateTimeZone.ForSystemDefault();
LocalDateTime tx1 = newYork.WithZone(ct);
LocalDateTime tx2 = seattle.WithZone(ct);
I would also like to be able to convert the values again like so:
ZonedDateTime wa = tx2.WithZone(seattleTimeZone);
ZonedDateTime nw = tx2.WithZone(nyTimeZone);
Which would also allow for:
instant.InUtc();
instant.InZone(zone);
instant.InLocal();
Thus having the ability to map Instant time to the 3 most common time zones; UTC, local and other. But I can't since LocalDateTime isn't a special type of ZonedDateTime.
IMHO, NodaTime 1.x is nice but it isn't flexible (or fluid) enough for prime time use. But the good news is, it isn't that far from it. Hopefully, in 2.0 (or sooner) these issues can be addressed.
@Michal - I like your idea of Noda[Date][Time]
as opposed to Raw[Date][Time]
.
Upvotes: 1
Views: 1174
Reputation: 13888
What you are probably getting confused with is this:
LocalDateTime
= A generic time in a specific calendar (e.g. Gregorian) but as there is no TimeZone
attached to it, you cannot distinguish which exact point on the Global time line this happened. Imagine this to be just a generic value. E.g. 14:00
. You won't know when exactly 14:00
is because your are missing what TimeZone this is in.
ZonedDateTime
= LocalDateTime
in a specific time zone (what you probably are imagining as LocalDateTime
).
I probably do agree with you that it is a bit mis-leading. And I would prefer for it to be called NodaDateTime
and ZonedDateTime
to be called LocalDateTime
as it does seem more logical that way.
EDIT - following comment
Personally, I rarely use the LocalDateTime
if you stick between Instant
and ZonedDateTime
you will probably get rid of a lot of your headaches. There is a very nice overview diagram of converting between the types:
Taken From: nodatime.org - User Guide - Converting Between Types
So in your case what you want to be doing, is converting from a specificZoneDateTime
to Instant
, and then back to your desired ZonedDateTime
(your system tz)
E.g.
var myTz = BclDateTimeZone.ForSystemDefault();
var fromNy = SystemClock.Instance.Now.InZone(DateTimeZoneProviders.Tzdb["America/New_York"]);
// fromNy = 2015-03-22T08:28:56 America/New_York (-04)
var fromLa = SystemClock.Instance.Now.InZone(DateTimeZoneProviders.Tzdb["America/Los_Angeles"]);
// fromLa = 2015-03-22T05:28:56 America/Los_Angeles (-07)
var fromNyInMyTz = fromNy.ToInstant().InZone(myTz);
// localFromNy = 2015-03-22T12:28:56 GMT Standard Time (+00)
var fromLaInMyTz = fromLa.ToInstant().InZone(myTz);
// localFromLa = 2015-03-22T12:28:56 GMT Standard Time (+00)
You could also create an extension method:
public static class NodaExtensions
{
public static ZonedDateTime InUtc(this ZonedDateTime dt)
{
return dt.ToInstant().InUtc();
}
public static ZonedDateTime InZone(this ZonedDateTime dt, DateTimeZone tz)
{
return dt.ToInstant().InZone(tz);
}
public static ZonedDateTime InLocal(this ZonedDateTime dt)
{
return dt.ToInstant().InZone(BclDateTimeZone.ForSystemDefault());
}
}
allowing for:
var fromNyInUtc = fromNy.InUtc();
var fromLaInUtc = fromLa.InUtc();
var fromNyInZone = fromNy.InZone(myTz);
var fromLaInZone = fromLa.InZone(myTz);
var fromNyInLocal = fromNy.InLocal();
var fromLaInLocal = fromLa.InLocal();
Is this something you would prefer?
Upvotes: 4