What is equivalent of TimeZoneInfo.ConvertTime in NodaTime? - nodatime

I want to use the NodaTime library to convert the date from one timezone to another like this.
string fromSystemTimeZoneId = "GMT Standard Time";
string toSystemTimeZoneId = "Central Standard Time";
TimeZoneInfo fromTimeZone = TimeZoneInfo.FindSystemTimeZoneById(fromSystemTimeZoneId);
TimeZoneInfo toTimeZone = TimeZoneInfo.FindSystemTimeZoneById(toSystemTimeZoneId);
var convertedTime = TimeZoneInfo.ConvertTime(inputDateTime, fromTimeZone, toTimeZone);
The above code works perfectly fine for me, But now I want to use IANA standard time zone (like Europe/London and America/Chicago) in place of windows OS-provided time zone ids.
I am using .net 4.7.2 and cannot upgrade the framework due to some limitations.
I have gone through this answer, But I am looking for simple few lines of code nothing complicated.

But I am looking for simple few lines of code nothing complicated.
But you're trying to do something complicated. Noda Time makes everything explicit, which means it's clear exactly what's going on - at the cost of being a little more verbose.
DateTime has no concept of a time zone itself (other than for the special cases where the Kind is Utc or Local). So TimeZoneInfo.ConvertTime has to:
Consider what instant in time is represented by inputDateTime in fromTimeZone
Work out what that instant in time looks like in outputDateTime
In Noda Time, those are are two separate operations, if you want to start and end with a LocalDateTime:
LocalDateTime inputDateTime = ...;
DateTimeZone fromTimeZone = ...;
DateTimeZone toTimeZone = ...;
// There are options for how this conversion is performed, as noted in other questions
ZonedDateTime inputDateTimeInFromTimeZone = inputDateTime.InZoneLeniently(fromTimeZone);
// This conversion is always unambiguous, because ZonedDateTime unambiguously
// refers to a single instant in time
ZonedDateTime inputDateTimeInToTimeZone = inputDateTimeInFromTimeZone.WithZone(toTimeZone);
LocalDateTime localPart = inputDateTimeInToTimeZone.LocalDateTime;
So that's basically the equivalent conversion - but you need to be explicit about how you want to handle skipped/ambiguous inputs. If you want everything in your app to use the same conversion, you can wrap that in a method that looks just like TimeZoneInfo.ConvertTime. But why not just keep things in a ZonedDateTime instead of LocalDateTime to start with? Then you don't get into the ambiguous position - other than potentially when converting user input (which is entirely separate from converting from one time zone to another).

Related

Is there a primitive type for DateTime in Bixby?

My model will optionally take a time in many utterances.
So examples...
add wet diaper. <-------- assumes current time
add wet diaper at 4:00 PM
add bottle at noon
What would be the best way to model 4 PM/AM so that in my actions I can collect the time?
You'll want to define an action that accepts an optional input, and make sure the type is 'DateTimeExpression'. In your NL training, you can say things like 'do this thing at 4PM' or 'do this thing'. Both are valid because you made the date optional. In your javascript, check to see if the user said a date. If they did, use it. If not, you can default to now. Refer to this link for grabbing the time from javascript. The date api will parse dates to the device's local time by default (can be overridden).
In your action
input (searchDate) {
type (time.DateTimeExpression)
min(Optional) max (One)
}
In your javascript
var currentTime = dates.ZonedDateTime.now().getDateTime();
if (searchDate)
currentTime = //Grab the date from searchDate.
Use this for reference.
There is no primitive type DateTime, but there is a viv.time library you can use, and in fact there is viv.time.DateTimeExpression concept you can use.
viv.time.DateTimeExpression not only handles "4 AM" but also "tomorrow 4 AM", you can read more here. I would say it is one of the most commonly used library concepts.

Decoded Timestamp is not the same date as original DateTime value

We have a .NET application that defines a DateTime in the schema like so:
[ProtoMember(20)]public DateTime? Birthdate;
The application is able to serialize the data using protobuf-net and later upon deserialization the date is retrieved accurately as expected.
I am now trying to deserialize the same buffer in our node.js application using protobuf.js. I've defined that data point in the .proto file like so:
google.protobuf.Timestamp Birthdate = 20;
Upon decoding it the resulting Birthdate is not the same date as the original data. For example, when the date is originally 10/10/1976, the deserialized date is:
"Birthdate": {
"seconds": "4948"
}
When creating a JavaScript Date from that (new Date(4948 * 1000)), the result is 1/1/1970. What is going wrong here?
This comes down to history. protobuf-net had support for DateTime a long time before there was a well-defined Timestamp in the shared libraries. So: it invented something that would allow round-trip between protobuf-net and itself. Many years later, along comes Timestamp, and ... put simply, it uses a different storage layout. The good news is: protobuf-net can talk that dialect, but because we couldn't break existing code, it is "opt in".
If you use a Timestamp in a .proto, you can see that protobuf-net generates, for that .proto:
[global::ProtoBuf.ProtoMember(20, DataFormat = global::ProtoBuf.DataFormat.WellKnown)]
public global::System.DateTime? Birthdate { get; set; }
or more simply, to match your existing code:
[ProtoMember(20,DataFormat=DataFormat.WellKnown)]public DateTime? Birthdate;
With that in place - it should be using the same data layout tand you should get the same values. This is the recommended option if you need to exchange data between platforms. However, note that this is a change to your existing layout. If you need tips on migrating without breaking existing usage, let me know - it is possible (the short version would be "leave field 20 as the old style; add a new property that acts similarly and uses the new format - only serialize the new one, but allow the old one to deserialize").

Convert DateTime with DateTimeKind.Unspecified from utc to local

I have a database where all dates are UTC, and the driver always sends me DateTime objects with DateTimeKind.Unspecified. I want to assume they are in UTC and convert to local time.
What is the simplest way to achieve this using NodaTime?
Instant.FromDateTimeUtc(utcDate).InZone(localZone); keeps complaining DateTimeKind should be Utc.
If you're definitely using Noda Time and want a ZonedDateTime, you could "tell" Noda Time that you've really got a UTC value, then convert it:
var zoned = LocalDateTime.FromDateTime(utcDate)
.InZoneStrictly(DateTimeZone.Utc)
.WithZone(localZone);
Or you could just use DateTime.SpecifyKind to start with:
var utcDateTime = DateTime.SpecifyKind(unspecifiedDateTime, DateTimeKind.Utc);
... and then use your existing code, or take Noda Time out of the picture entirely if you want.
Indeed, just DateTime.ToLocalTime() would be fine if you don't need Noda Time elsewhere:
var localDateTime = utcDateTime.ToLocalTime();
... the BCL will assume that an unspecified date/time is UTC if you call ToLocalTime() on it. Of course, I'd recommend using Noda Time everywhere though :)
Instant now = SystemClock.Instance.GetCurrentInstant();
DateTimeZone TimeZone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
var zoned = LocalDateTime.FromDateTime(msg.MessageSentUtc)
.InZoneStrictly(DateTimeZone.Utc)
.WithZone(TimeZone);
Simplified...

How do I get the list of strings in tzdb used as time zone initializer?

SO I am new to NodaTime and trying to use it to for storing timezone information using DateTimeZone object.
I came across below sample in user guide etc. which give me a nice DateTimeZone object from tzdb, which is great.
var london = DateTimeZoneProviders.Tzdb["Europe/London"];
My question is - how do I get a list of timezone strings ("Europe/London") which are used in the tzdb. I looked around, nowhere to find. Is there a standard list somewhere which I can refer to? How does this work? ex. - what is the string I should pass for EST?
Thanks!
To fetch the time zone IDs programmatically, use the Ids property in IDateTimeZoneProvider. For example, to find all zones:
var provider = DateTimeZoneProviders.Tzdb;
foreach (var id in provider.Ids)
{
var zone = provider[id];
// Use the zone
}
For Eastern Time, you probably want America/New_York.
More generally, these identifiers are the ones from IANA - and they're the ones used in most non-Windows systems.

NodaTime - Errors after time switch

Yesterday Uruguay changed their clocks, and now I keep seeing exceptions when converting specific times for their timezone:
ERROR Exception: - DateTime ConvertTimeToUtc(DateTime, String) (05/10/2014 02:31:00, America/Montevideo)
NodaTime.SkippedTimeException: Specified argument was out of the range of valid values.
Parameter name: Local time 05/10/2014 02:31:00 is invalid in time zone America/Montevideo
I understand how a local time can be invalid:
"For example, suppose the time zone goes forward at 2am, so the second after 01:59:59 becomes 03:00:00. In that case, local times such as 02:30:00 never occur."
However, what I don't understand (and I probably need more coffee), is why NodaTime is not accounting for this? Should it not be aware that 02:31 is now an invalid local time - or should I be doing additional handling to account for this?
Functions I'm calling:
var timeZone = DateTimeZoneProviders.Tzdb[timezoneName];
var localTime = LocalDateTime.FromDateTime(timeToConvert).InZoneStrictly(timeZone);;
return DateTime.SpecifyKind(localTime.ToDateTimeUtc(), DateTimeKind.Utc);
Yes, it is aware that it's an invalid local time - which is why when you specifically ask it to convert that local time into UTC, it throws an exception. It's roughly equivalent to calling Math.sqrt(-1).
The InZoneStrictly call you're making specifically throws an exception on either ambiguous or skipped times. If you use InZoneLeniently you won't get an exception, but it may not give you the result you want. Or, you could use LocalDateTime.InZone(DateTimeZone, ZoneLocalMappingResolver) which will allow you to map invalid local date/time values however you want.
As side-notes:
Your localTime variable is a ZonedDateTime, so the name is a bit misleading
You don't need to call SpecifyKind - ToDateTimeUtc will always return a DateTime with a kind of Utc, hence the name.

Resources