Jon Skeet: Coding Blog : Noda Time, Design Noda Time, DesignenCommunityServer 2008.5 SP2 (Build: 40407.4157)The perils of conditional mutability, 06 May 2012 13:58:45 GMTd67277c4-116b-43f1-b688-e9ef184ea916:1809562skeet6<p>This morning I was wrestling with trying to make some Noda Time unit tests faster. For some reason, the <a href="">continuous integration host we&#39;re using</a> is <em>really</em> slow at loading resources under .NET 4. The unit tests which run in 10 seconds on my home laptop take over three <em>hours</em> on the continuous integration system. Taking stack traces at regular intervals showed the problem was with the NodaFormatInfo constructor, which reads some resources.</p> <p>I may look into streamlining the resource access later, but before we get to that point, I wanted to try to reduce the number of times we call that constructor in the first place. NodaFormatInfo is meant to be cached, so I wouldn&#39;t have expected thousands of instances to be created - but it&#39;s only cached when the System.Globalization.CultureInfo it&#39;s based on is read-only. This is where the problems start...</p> <p><a href="">CultureInfo</a> is <em>conditionally mutable </em>(not an official term, just one I&#39;ve coined for the purposes of this post). You can ask whether or not it&#39;s read-only with the <a href="">IsReadOnly property</a>, and obviously if it&#39;s read-only you can&#39;t change it. Additionally, CultureInfo is composed of other conditionally mutable objects - <a href="">DateTimeFormatInfo</a>, <a href="">NumberFormatInfo</a> etc. There&#39;s a static <a href="">ReadOnly method</a> on CultureInfo to create a read-only wrapper around a mutable CultureInfo. It&#39;s not clearly documented whether that&#39;s <em>expected</em> to take a deep copy (so that callers can really rely on it not changing) or whether it&#39;s expected to reflect any further changes made to the culture info it&#39;s based on. To go in the other direction, you can call Clone on a CultureInfo to create a mutable copy of any existing culture.</p> <p>Further complications are introduced by the properties on the composite objects - we have properties such as <a href="">DateTimeFormatInfo.MonthNames</a> which returns a string array. Remember, arrays are <em>always</em> mutable. So it&#39;s really important to know whether the array reference returned from the property refers to a <em>copy</em> of the underlying data, or whether it refers to the array that&#39;s used internally by the type. Obviously for read-only DateTimeFormatInfo objects, I&#39;d expect a copy to be returned - but for a mutable DateTimeFormatInfo, it would potentially make sense to return the underlying array reference. Unfortunately, the documentation doesn&#39;t make this clear - but in practice, it always returns a copy. If you need to change the month names, you need to clone the array, mutate the clone, and then set the MonthNames property.</p> <p><em>All of this makes CultureInfo hard to work with</em>. The caching decision earlier on only really works if a &quot;read-only&quot; culture genuinely won&#39;t change behind the scenes. The type system gives you no help to catch subtle bugs at compile-time. Making any of this robust but efficient (in terms of taking minimal copies) is tricky to say the least.</p> <p>Not only does it make it hard to work with from a client&#39;s point of view, but apparently it&#39;s hard to implement correctly too...</p> <h2>First bug: Mono&#39;s invariant culture isn&#39;t terribly invariant...</h2> <p>(Broken in 2.10.8; apparently fixed later.)</p> <p>I discovered this while getting Noda Time&#39;s unit tests to pass on Mono. Unfortunately there are some I&#39;ve had to effectively disable at the moment, due to deficiencies in Mono (some of which are being fixed, of course).</p> <p>Here&#39;s a program which builds a clone of the invariant culture, changes the <em>clone&#39;s</em> genitive month names, and then prints out the first <em>non-genitive</em> name from the plain invariant culture:</p> <div class="code"><span class="Namespace">using</span> System; <br /><span class="Namespace">using</span> System.Globalization; <br /> <br /><span class="ReferenceType">class</span> Test <br />{ <br />&#160;&#160;&#160; <span class="Modifier">static</span>&#160;<span class="ValueType">void</span> Main() <br />&#160;&#160;&#160; {&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; CultureInfo clone = (CultureInfo) CultureInfo.InvariantCulture.Clone(); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span class="InlineComment">// Note: not even deliberately changing MonthNames for this culture!</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; clone.DateTimeFormat.MonthGenitiveNames[0] = <span class="String">&quot;Changed&quot;</span>; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span class="InlineComment">// Prints Changed</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(CultureInfo.InvariantCulture.DateTimeFormat.MonthNames[0]); <br />&#160;&#160;&#160; } <br />} </div> <p>I believe this bug is <em>really</em> due to the lack of support for genitive month names in Mono at the moment - the MonthGenitiveNames property always just returns a reference to the month names for the invariant culture - without taking a copy first. (It always returns the invariant culture&#39;s month names, even if you&#39;re using a different culture entirely.) The code above shows an &quot;innocent&quot; attempt to change a mutable clone - but in reality we could have used <em>any</em> culture (including an immutable one) to make the change.</p> <p>Note that in the .NET implementation, the change would only have been to a copy of the underlying data, so even the <em>clone</em> wouldn&#39;t have reflected change anywhere.</p> <h2>Second bug: ReadOnly losing changes</h2> <p>The second bug is the one I found this morning. It looks like it&#39;s fixed in .NET 4, but it&#39;s present in .NET 3.5, which is where it bit me this morning. When you try to make a read-only wrapper around a mutated culture, some of the properties are preserved... but some aren&#39;t:</p> <div class="code"><span class="Namespace">using</span> System; <br /><span class="Namespace">using</span> System.Globalization; <br /> <br /><span class="ReferenceType">class</span> Test <br />{ <br />&#160;&#160;&#160; <span class="Modifier">static</span>&#160;<span class="ValueType">void</span> Main() <br />&#160;&#160;&#160; { <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; CultureInfo clone = (CultureInfo) CultureInfo.InvariantCulture.Clone(); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; clone.DateTimeFormat.AMDesignator = <span class="String">&quot;ChangedAm&quot;</span>; <br /> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span class="InlineComment">// The array is recreated on each call to MonthNames, so changing the</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span class="InlineComment">// value within the array itself doesn&#39;t work :(</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span class="ReferenceType">string</span>[] months = (<span class="ReferenceType">string</span>[]) clone.DateTimeFormat.MonthNames; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; months[0] = <span class="String">&quot;ChangedMonth&quot;</span>; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; clone.DateTimeFormat.MonthNames = months; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; CultureInfo readOnlyCopy = CultureInfo.ReadOnly(clone); <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(clone.DateTimeFormat.AMDesignator); <span class="InlineComment">// ChangedAm</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(clone.DateTimeFormat.MonthNames[0]); <span class="InlineComment">// ChangedMonth</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(readOnlyCopy.DateTimeFormat.AMDesignator); <span class="InlineComment">// ChangedAm</span> <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(readOnlyCopy.DateTimeFormat.MonthNames[0]); <span class="InlineComment">// January (!)</span> <br />&#160;&#160;&#160; } <br />} </div> <p>I don&#39;t know what&#39;s going on here. In the end I just left the test code using the mutable clone, having added a comment explaining <em>why</em> it wasn&#39;t created a read-only wrapper.</p> <p>I&#39;ve experimented with a few different options here, including setting the MonthNames property on the clone <em>after</em> creating the wrapper. No joy - I simply can&#39;t make the new month names stick in the read-only copy. &lt;sigh&gt;</p> <h2>Conclusion</h2> <p>I&#39;ve been frustrated by the approach we&#39;ve taken to cultures in Noda Time for a while. I haven&#39;t worked out exactly what we should do about it yet, so it&#39;s likely to stay somewhat annoying for v1, but I may well revisit it significantly for v2. Unfortunately, there&#39;s nothing I can do about CultureInfo itself.</p> <p>What I would have <em>preferred</em> in all of this is the builder pattern: make CultureInfo, DateTimeFormatInfo etc all immutable, but give each of them mutable builder types, with the ability to create a mutable builder based on an existing immutable instance, and obviously to create a new immutable instance from a builder. That would make all kinds of things simpler - including caching.</p> <p>For the moment though, I hope we can all learn lessons from this - or have old lessons reinforced, at least:</p> <ul> <li>Making a single type behave in different ways based on different &quot;modes&quot; makes it hard to use correctly. (Yes, this is the same first conclusion as with DateTime in the previous post. Funny, that.)</li> <li>Immutability has to be deep to be meaningful: it&#39;s not much use having a supposedly read-only object which composes a StringBuilder...</li> <li>Arrays should be <a href="">considered somewhat harmful</a>. If you&#39;re going to return an array from a method, make sure you document whether this is a <em>copy</em> of the underlying data, or a &quot;live&quot; reference. (The latter should be very rare, particularly for a public API.) The exception here is if you return an empty array - that&#39;s effectively immutable, so you can always return it with no problems.</li> <li>The builder pattern rocks - use it!</li> </ul> <p>In my next post I&#39;ll try to get back to the TimeZoneInfo oddities I mentioned last time.</p><div style="clear:both;"></div><img src="" width="1" height="1">C#Noda TimeDesignSubtleties in API design - member placement, 29 Feb 2012 17:27:25 GMTd67277c4-116b-43f1-b688-e9ef184ea916:1806600skeet38<p><a href="">Noda Time</a> is nearing v1.0, which means I&#39;m spending more time writing documentation than code. It also means reviewing the APIs we&#39;ve got with a critical eye - whether that&#39;s removing extraneous members, adding useful ones, or moving things around. (In particular, writing documentation often suggests where a change would make calling code read more naturally.)</p> <p>This post is about one particular section of the API, and the choices available. Although I <em>do</em> go into some detail around the specific calls involved, that&#39;s just for context... the underlying choices are ones which could be faced when designing any API. I&#39;ve rarely spent as much time thinking about API decisions as I have with Noda Time, so hopefully this will prove interesting to you even if you really don&#39;t care about Noda Time itself as a project.</p> <h1>Introduction: time zones, local date/times and zoned date/times - oh my!</h1> <p>(Okay, so that&#39;s not quite as snappy as <a href="">the Judy Garland version</a>, but hey...)</p> <p>The area of API we&#39;re going to focus on is time zones, and converting between &quot;local&quot; date/time values and &quot;zoned&quot; ones. The three types involved are:</p> <ul> <li><strong>LocalDateTime</strong>: a &quot;local&quot; date and time, with no specific time zone. So, something like &quot;7:30 in the evening on February 27th 2012&quot;. This means different instants in time in different time zones, of course: if you&#39;re arranging a meeting, it&#39;s good enough when the attendees are in the same time zone, but not good enough if you&#39;re meeting with someone on the other side of the world. (A LocalDateTime also has an associated calendar system, but I&#39;m not going to talk about that much in this post.) </li> <li><strong>DateTimeZone</strong>: a time zone. At its core, this maps <em>any</em> &quot;instant&quot; in time to an <em>offset</em> - the difference between UTC and local time in that time zone. The offset changes over time, typically (but not always) due to daylight saving changes. </li> <li><strong>ZonedDateTime</strong>: a date and time in a particular time zone, with an offset from UTC to avoid ambiguity in some cases (and for efficiency). This identifies a specific instant in time (simply by subtracting the offset from the local date/time). Conceptually this is equivalent to just maintaining the &quot;instant&quot; value, the time zone, and the calendar system - but it&#39;s generally cleaner to think of it as a &quot;local&quot; value with an offset from UTC. </li> </ul> <p>If those brief descriptions don&#39;t make sense for you at the moment (this sort of thing is quite hard to describe concisely and precisely) you may want to see whether the Noda Time <a href="">user guide &quot;concepts&quot; page</a> helps.</p> <h3>The API task: mapping from { LocalDateTime, DateTimeZone } to ZonedDateTime</h3> <p>It&#39;s easy to get from a ZonedDateTime to a LocalDateTime - you can just use the LocalDateTime property. The difficult bit is the other way round. We obviously want to be able to create a ZonedDateTime from the combination of a LocalDateTime and a DateTimeZone, but the question is where to put this functionality. Three options suggest themselves:</p> <ul> <li>A static method (or constructor) in ZonedDateTime which takes both the time zone and the local date/time as arguments </li> <li>An instance method on LocalDateTime which just takes the time zone as an argument </li> <li>An instance method on DateTimeZone which just takes the local date/time as an argument </li> </ul> <p>It gets more complicated though - we&#39;re not really talking about <em>one</em> operation here, but potentially several. Although the mapping from instant to offset is unambiguous in DateTimeZone, the mapping from LocalDateTime to offset is <em>not </em>straightforward. There can be 0, 1 or 2 possible results. For example, in the America/Los_Angeles time zone the clocks go forward from 2am to 3am on Sunday March 11th 2012, and go back from 2am to 1am on Sunday 4th November 2012. That means:</p> <ul> <li>The mapping from local date/time to offset at 7.30pm on February 27th 2012 is unambiguous: it&#39;s definitely -8 hours (L.A. is 8 hours <em>behind</em> UTC). </li> <li>The mapping at 2.30am on March 11th 2012 is <em>impossible</em>: at 2am the clocks were put forward to 3am, so 2.30am simply never occurs. </li> <li>The mapping at 2.30am on November 4th 2012 is <em>ambiguous</em>: it happens once <em>before</em> the clocks go back at 3am, and once <em>afterwards</em>. The offset is either -7 or -8 hours, depending on which occurrence you mean. </li> </ul> <p>When mapping a local time to &quot;global&quot; time, this is something you should really think about. Most APIs obscure the issue, but one of the purposes of Noda Time is to <em>force</em> developers to think about issues which they should be aware of. This one is particularly insidious in that it&#39;s the kind of problem which is <em>much</em> more likely to arise when you&#39;re asleep than during daylight hours - so it&#39;s unlikely to be found during manual testing. (Ditto the day of week - most time zones have daylight saving transitions early on a Sunday morning.)</p> <p>So, Noda Time exposes four ways of mapping a LocalDateTime and DateTimeZone to a ZonedDateTime:</p> <ul> <li>Exact: if there&#39;s a single mapping, return it. Otherwise, throw an exception. </li> <li>Earlier: if there&#39;s a single mapping, return it. If there&#39;s more than one, return the earlier one. If the time is skipped, throw an exception. </li> <li>Later: if there&#39;s a single mapping, return it. If there&#39;s more than one, return the later one. If the time is skipped, throw an exception. </li> <li>All information: find out all the information relevant to mapping the given local value - how many matches there are, what they would be, what the time zone information is for each mapping, etc. The caller can then do what they want. </li> </ul> <h1>Options available</h1> <p>The question is <em>how</em> we expose these operations. Let&#39;s look at some options, then discuss the pros and cons.</p> <h3>Option 1: methods on LocalDateTime</h3> <p>A lot of Noda Time is designed to be &quot;fluent&quot; so it makes a certain amount of sense to be able to take a LocalDateTime, perform some arithmetic on it, then convert it to a ZonedDateTime, then (say) format it. So we could have something like:</p> <ul> <li>var zoned = local.InZone(zone); // implicitly exact </li> <li>var zoned = local.InZoneOrEarlier(zone); </li> <li>var zoned = local.InZoneOrLater(zone); </li> <li>var mapping = local.MapToZone(zone); </li> </ul> <h3>Option 2: methods on DateTimeZone</h3> <p>All the calculations involved are really about the time zone - the local date/time value is just a simple value as far as most of this is concerned. So we can put the methods on DateTimeZone instead:</p> <ul> <li>var zoned = zone.AtExactly(local); </li> <li>var zoned = zone.AtEarlier(local); </li> <li>var zoned = zone.AtLater(local); </li> <li>var mapping = zone.MapLocalDateTime(local); </li> </ul> <h3>Option 3: methods (or constructors) on ZonedDateTime</h3> <p>Maybe we consider the two inputs to be fairly equal, but the result type is more important:</p> <ul> <li>var zoned = ZonedDateTime.FromLocal(zone, local); </li> <li>var zoned = ZonedDateTime.FromLocalOrEarlier(zone, local); </li> <li>var zoned = ZonedDateTime.FromLocalOrLater(zone, local); </li> <li>var mapping = ZoneLocalMapping.FromLocal(local) </li> </ul> <p>(I&#39;m not terribly happy about the names here; there could be better ones of course.)</p> <h3>Variation a: any of the above options, but with an enum for ambiguity resolution</h3> <p>We don&#39;t really need four methods on any of these APIs; the first three only differ by how they handle ambiguity (the situation where a particular local date/time occurs twice). We could use an enum to represent that choice instead:</p> <ul> <li>var zoned = local.InZone(zone, ZoneAmbiguityResolver.Error); </li> <li>var zoned = local.InZone(zone, ZoneAmbiguityResolver.Earlier); </li> <li>var zoned = local.InZone(zone, ZoneAmbiguityResolver.Later); </li> </ul> <p>(Or a &quot;smart enum&quot; with behaviour, if we wanted. A normal class type with methods etc, but only a restricted set of valid values.)</p> <h3>Variation b: always go via the mapping result</h3> <p>Given that we already have the idea of getting the full mapping results, we can reduce the API to just <em>one</em> method to return the mapping information, and then put extra methods on that:</p> <ul> <li>var zoned = local.MapInZone(zone).SingleMatch; </li> <li>var zoned = local.MapInZone(zone).SingleOrEarlier; </li> <li>var zoned = local.MapInZone(zone).SingleOrLater; </li> </ul> <p>(Again, the names aren&#39;t fixed in stone, and the second part could be methods instead of properties if we wanted.)</p> <h3>Variation c: return a sequence of results</h3> <p>If we return a sequence with 0, 1 or 2 ZonedDateTime values, the user can just use LINQ to get the one they want. Again, this can apply wherever we decide to put the method:</p> <ul> <li>var zoned = zone.At(local).Single(); </li> <li>var zoned = zone.At(local).First(); </li> <li>var zoned = zone.At(local).Last(); </li> </ul> <p>So, it looks like we effectively have two mostly-orthogonal decisions here:</p> <ul> <li>Where to &quot;start&quot; the conversion - the target type for the method call </li> <li>How to represent the multiple options </li> </ul> <p>We&#39;ll consider them separately.</p> <h1>Regarding the &quot;source&quot; type</h1> <p>To start with, I&#39;ll reveal my bias: the existing implementation is option 2 (four methods on DateTimeZone). This was after a small amount of debate on the Noda Time mailing list, and this was the most relevant bit of the discussion:</p> <p>Me (<em>before</em> going with the current implementation):</p> <blockquote> <p>It feels a little odd to me to use the zone as the principal class here - just in terms of usability. It makes total sense in terms of the logic, but I tend to think of having a LocalDateTime first, and then converting <em>that</em> to use a particular zone - it&#39;s not an operation which feels like it acts on the zone itself.</p> </blockquote> <p>David N:</p> <blockquote> <p>I actually feel the opposite: asking a DateTimeZone how a particular LocalDateTime would be represented in that zone feels natural, while asking the LocalDateTime how it would be represented in a zone feels odd. The zone is a first-class entity, with identity and behavior; the LocalDateTime is just a set of values. Why would the LocalDateTime be expected to know how it is represented in a particular zone?</p> </blockquote> <p>Even though I replied to that in a &quot;maybe&quot; kind of tone, the argument basically convinced me. The trouble is, a colleague was then <em>surprised</em> when he read the documentation around calendar arithmetic and conversions. Surprising users is pretty much a cardinal sin when it comes to API design - and although in this case it was the relatively harmless sort of surprise (&quot;I can&#39;t find the member I want in A; oh, it turns out it&#39;s in B&quot;) rather than a <em>behavioural</em> surprise (&quot;I thought it would do X, but instead it does Y&quot;) it&#39;s still bad news. I should reveal my colleague&#39;s bias too - he has experience of <a href="">Joda Time</a>, where the relevant call is <a href="">LocalDateTime.toDateTime(DateTimeZone)</a>. (There <em>are</em> calls in DateTimeZone itself, but they&#39;re lower-level.)</p> <p>We&#39;ve discussed this a fair amount (leading to this blog post) and so far we&#39;ve concluded that it really depends on how you <em>think</em> about time zones. As a Noda Time user, would you consider them to be rich objects with complex behaviour, or would you think of them as mere &quot;tokens&quot; to be passed around and used without much direct interaction? The two ways of viewing the type aren&#39;t necessarily in conflict - I&#39;ve <em>deliberately</em> designed CalendarSystem to hide its (fairly ugly) innards. There are a few public instance members, but most are internal. But what about time zones?</p> <p>There&#39;s an argument to be made for <em>educating</em> Noda Time users to think about time zones as more complex beasts than just tokens, and I&#39;m happy to do that in other areas (such as choosing which type to use in the first place) but here it feels like it&#39;s one step too far. On the other hand, I don&#39;t want to stifle users who <em>are</em> thinking of DateTimeZone in that way. In the mailing list thread, David also expressed a dislike for the approach of including functionality in multiple places - and to a certain extent I agree (one of the things I <em>dislike</em> about its API is that it lets you do just about anything with anything)... but in this case it feels like it&#39;s justified.</p> <p>Regardless of how you&#39;re thinking about DateTimeZone, it&#39;s more likely that you&#39;re going to want to <em>use</em> a LocalDateTime value which is the result of some other expression, and then apply some &quot;constant&quot; zone to it, then potentially keep going. If you think about a LINQ-style pipeline of operations, the part that varies in the conversion is much more likely to be the LocalDateTime than the time zone. As such, a method on LocalDateTime allows for a more fluent calling style:</p> <div class="code"><span class="Linq">var</span> zoned = parseResult.Value <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .PlusMonths(1) <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .InZone(LondonTimeZone); </div> <p>versus:</p> <div class="code"><span class="Linq">var</span> zoned = LondonTimeZone.AtExactly(parseResult.Value.PlusMonths(1)); </div> <p>Or to keep the code order the same as the execution order:</p> <div class="code"><span class="Linq">var</span> local = parseResult.Value.PlusMonths(1); <br /><span class="Linq">var</span> zoned = LondonTimeZone.AtExactly(local); </div> <p>Obviously the effects become more noticeable the more operations you perform. Personally I&#39;m happy with either the first or third approach - although it&#39;s worth being aware that either of the first <em>two</em> have the advantage of being one expression, and therefore easy to use when assigning a static readonly field or something similar.</p> <p>I&#39;m <em>reasonably</em> happy with having one method on each type, or possibly two (MapLocalDateTime and At*) on DateTimeZone and one (just InZone) on LocalDateTime. I <em>really</em> don&#39;t like the idea of having four methods on DateTimeZone and three methods on LocalDateTime. So, let&#39;s consider the different variations which cut down the number of methods required.</p> <p>&#160;</p> <h1>Expressing &quot;exactly,&quot; &quot;earlier,&quot; and &quot;later&quot; in one method</h1> <p>This is essentially a discussion of the &quot;variations&quot; above. Just to recap, the possibilities we&#39;ve come up with are:</p> <ul> <li>Add another parameter to the method to indicate how to handle ambiguities (or impossibilities) - just return a ZonedDateTime </li> <li>Return a value of a different type (e.g. ZoneLocalMapping) which can be used to get at all the information you could want </li> <li>Return a sequence of possible ZonedDateTime values, expecting the caller to probably use LINQ&#39;s First/Last/Single/FirstOrDefault etc to get at the value they want </li> </ul> <p>The last of these is the only one which gives an easy way of handling the extreme corner case of a local time occurring <em>more</em> than twice - for example, a time zone which goes back one hour at 2am (to 1am) and then goes back another two hours at 3am. I think it&#39;s reasonable to dismiss this corner case; however mad time zones can be, I haven&#39;t seen anything <em>quite</em> that crazy yet.</p> <p>At the time of the original discussion, Noda Time was targeting .NET 2.0, which was one reason for not going with the final option here - we couldn&#39;t guarantee that LINQ would be available. Now, Noda Time is targeting .NET 3.5 in order to use TimeZoneInfo, but it still doesn&#39;t feel like an ideal fit:</p> <ul> <li>Returning a sequence doesn&#39;t give information about (say) the two zone intervals &quot;surrounding&quot; a gap </li> <li>A sequence may be surprising to users who expect just a single value </li> <li>The exceptions thrown by First, Single etc when their expectations aren&#39;t met are very domain-neutral; we can give more information </li> <li>FirstOrDefault will return the default value for ZonedDateTime in the case of ambiguity. That would be unfortunate, as ZonedDateTime is a value type, and its default value is actually somewhat unhelpful. (It has a null calendar system, effectively. There&#39;s not a lot we can do about this, but that&#39;s a post for another day.) We could make it a sequence of Nullable&lt;ZonedDateTime&gt; and guarantee that any values in it are actually non-null, but that&#39;s really straining things. </li> </ul> <p>Putting these together, there are enough negative points to this idea that I&#39;m happy to rule it out. But what about the first two?</p> <p>The first has the advantage that the caller only needs to make a single method call, effectively passing in a &quot;magic token&quot; (the ambiguity resolver) which they don&#39;t <em>really</em> need to understand. On the other hand, if they want more information, they&#39;ll have to call a different method - and I&#39;m not really sure we want to encourage too much of this &quot;magic token&quot; behaviour.</p> <p>The second has three disadvantages, all fairly slight:</p> <ul> <li>The user may initially expect the result of a method mapping a LocalDateTime to a ZonedDateTime to <em>be</em> a ZonedDateTime... what&#39;s this extra intermediate result doing? This is &quot;only&quot; a matter of user education, and it&#39;s pretty discoverable. It&#39;s an extra concept the user needs to understand, but it&#39;s a <em>good</em> concept to understand. </li> <li>Calling two methods or a method and a property (e.g. zone.MapLocalDateTime(localDateTime).Earlier) may end up being slightly more long-winded than a single method call. I can&#39;t get excited about this. </li> <li>We have to allocate an extra object for the mapping, even when we know it&#39;s unique. Usually, this object will become eligible for garbage collection immediately. We <em>could</em> make it a struct, but I don&#39;t think it&#39;s a natural value type - I&#39;d rather trust that allocating objects in gen0 is pretty cheap. </li> </ul> <p>With the second method, we can replace <em>all</em> the existing methods in DateTimeZone with a single one (or rather, just remove the AtXyz methods, leaving MapLocalDateTime). We can then create pleasantly-named methods on ZoneLocalMapping (which isn&#39;t quite right for this purpose at the moment).</p> <h1>Conclusion</h1> <p>This has been an interesting thought experiment for me, and it&#39;s suggested some changes I <em>will</em> be applying before v1. We&#39;ll see how they pan out. If you want to follow them, look for relevant <a href="">source code changes</a>.</p> <p>The important points I&#39;ve been thinking about are:</p> <ul> <li>What would a new user <em>expect</em> to be available? If they haven&#39;t read any documentation, what are they likely to try? </li> <li>What <em>should</em> the user know about? Where there are important decisions to make, how can we provide guidance? </li> <li>What would an <em>experienced</em> user (who is already thinking about the Noda Time concepts in the way that we want to encourage) expect to be available? </li> <li>Where does the balance lie between providing a &quot;too crowded&quot; API (with lots of different ways of achieving the same thing) and a &quot;sparse&quot; API (where there&#39;s always one way of achieving a goal, but it may not be the most convenient one for your situation) </li> <li>How does our choice fit in with other technologies? For example, the final &quot;variation&quot; seems like it plays nicely with LINQ at first - but a few subtleties make it a worse fit than it might seem. </li> <li>How does this affect performance? (Performance is important in Noda Time - but there would have to be a <em>significant</em> performance problem for me to deviate from an elegant solution.) </li> </ul> <p>So, any other thoughts? Did we miss some options? What other factors should we have taken into consideration?</p><div style="clear:both;"></div><img src="" width="1" height="1">C#Noda TimeDesign