This is a follow-on to this question. I am building a RESTful server (that is in C# but the core engine is Java (via IKVM)). We are writing clients in C# Java, etc.
So for the specific case of the C# client, if they want a ZonedDateTime on the server side, I need to pass a string similar to "2018-10-21T08:11:55-06:00[America/Denver]". For this case I think I should have them pass me a (DateTime, TimeZoneInfo) and from that build the string.
To do this I need to get the Java "America/Denver" from the setting for TimeZoneInfo. Is there any way to get this conversion.
As I understand it, there's no ISO or other widely used setting for uniquely identifying time zones.
"America/Denver" is the IANA name for that time zone. The Time Zone Database (tzdb) contains these names and their rules (which can change over time). NodaTime uses tzdb data to perform its logic.
You can use the TimeZoneConverter package to convert a TimeZoneInfo
to a tzdb time zone ID.
You've asked a few questions related to this and I'd like to piece some of that together for you here.
If it's feasible to do so, you should have the consumer pass you a ZonedDateTime
. It's a single value with all the information needed by the Java-based core engine, which is exactly what you were asking for (here). Using a single value that has been validated in the domain (as opposed to a custom container for the constituent parts) defers error-proned activities to the consumer, who would be better suited than you to resolve them and must do before calling your client. Then you don't have to be responsible for any errors or bugs related to something that shouldn't concern you.
Provided that you have a ZonedDateTime
instance, all you need now is a custom pattern that will give you a string in the format the Java side expects.
ZonedDateTimePattern customPattern = ZonedDateTimePattern.Create(
"uuuu'-'MM'-'dd'T'HH':'mm':'sso<Z-HH':'mm>'['z']'",
CultureInfo.InvariantCulture,
mapping => mapping.LocalDateTime.InZoneLeniently(mapping.Zone),
DateTimeZoneProviders.Tzdb,
default);
Based on your previous questions, it looks like you need a literal "Z
" for UTC instead of "+00:00
". The "Z-HH':'mm
" subpattern does that. Take a look at the Offset patterns documentation if you need something different.
Now you can use customPattern
to create the string you need to send.
string formatted = customPattern.Format(zonedDateTime);
The same pattern can be used to parse such a string back to ZonedDateTime
, if necessary.
If you can't expect the consumer to work with NodaTime types, that's okay. Receiving DateTimeOffset
and TimeZoneInfo
could also work. You can convert them to a ZonedDateTime
within your client without much ceremony.
// Given: DateTimeOffset dateTimeOffset, TimeZoneInfo timeZoneInfo
DateTimeZone dateTimeZone = DateTimeZoneProviders.Tzdb[TZConvert.WindowsToIana(timeZoneInfo.Id)];
ZonedDateTime zonedDateTime = OffsetDateTime.FromDateTimeOffset(dateTimeOffset).InZone(dateTimeZone);
The potential problem with this is that the collective input hasn't been validated in the domain. Matt Johnson-Pint pointed out in his answer that an offset could be passed which is incorrect for the provided time zone. Be prepared to add validation or a try/catch so you can tell the consumer what they did wrong in very clear language.
You could accept a DateTime
but you would be forced to make assumptions about ambiguous times that may not be acceptable to you and/or the consumer. Then you become responsible for logic that has nothing to do with your API's functionality.
I'll include it here for completeness but this is not an endorsement of any kind.
// Given: DateTime dateTime, TimeZoneInfo timeZoneInfo
DateTimeZone dateTimeZone = DateTimeZoneProviders.Tzdb[TZConvert.WindowsToIana(timeZoneInfo.Id)];
ZonedDateTime zonedDateTime = LocalDateTime.FromDateTime(dateTime).InZoneLeniently(dateTimeZone);
InZoneLeniently
is the big red flag there. It will "just do it" when an ambiguous time is encountered, and it might not be right. Remember, "just" is a 4-letter word.