Having been stuck in a bit of an API dilemma recently and having also found time to release Unconstrained Melody as a NuGet package, I figured it would be worth doing the same for Noda Time, in the hope of getting more potential "customers" to have a look. Even though fetching the source and building it is pretty painless, it's more than I'd expect for a casual onlooker, where NuGet makes the whole process pretty simple.
So, while you're reading the rest of this post, why not download the Noda Time 0.1 packages? There are three to pick up, although you only really need the first:
- NodaTime: the core library
- NodaTime.Experimental: our sort of sandbox; when there are multiple ways of attacking a problem, I'll probably try to have them all in here at the same time. This only works for new classes and things which can be implemented as extension methods of course - but that should cover a lot of ground. This is more for comment than production use, basically.
- NodaTime.Testing: Extra library which is designed for testing code which uses Noda Time - for example, a fake clock to be used where you're passing in IClock for dependency injection.
What do I need to know?
It's worth being aware of the core concepts in Noda Time:
- Instant: defines a universal point in time (we don't handle relativity) without reference to a time zone or calendar system. (Internally it's the number of ticks since the Unix epoch, but don't let that concern you too much.)
- CalendarSystem: A way of breaking down time into years, months, days, hours, minutes etc. You'll almost certainly only want to use the ISO calendar system, which is the universal default in Noda Time.
- DateTimeZone: Essentially, a mapping between UTC and local time. But full of subtleties like local times that don't exist or are ambiguous...
- LocalDateTime: A date and time in a particular calendar but not in a particular time zone. Something like "15th January 2010, 10:30am (ISO)". This is the type you're actually most likely to perform arithmetic - it makes sense to do things like adding a week or a month. Closely related are LocalDate and LocalTime, for situations where you only need a date or only need a time of day respectively.
- ZonedDateTime: A date and time in a particular calendar and time zone. Think of it as a local date and time + DateTimeZone + offset from UTC. A ZonedDateTime can be mapped unambiguously to an instant.
All of the above are immutable in Noda Time. All except CalendarSystem and DateTimeZone are structs.
Those are the important concepts. There are some more details on the wiki page, but hopefully that's enough to get you going.
What's in the package?
Currently, I'm reasonably confident in the API and implementation of:
- CalendarSystem (ISO, Gregorian, Julian and Coptic are implemented; a few more to come)
- DateTimeZone (Olson identifiers; we have a mapping for the Windows identifiers, but I don't think we expose them yet.)
- ZonedDateTime: Conversion to and from a LocalDateTime (with various options for handling awkward situations)
- LocalDateTime / LocalDate / LocalTime: Various conversions between them (and construction of course). Arithmetic adding periods (e.g. "this date/time plus two weeks") is implemented but may not be the final API or semantics; manipulation is still up in the air. There are some extension methods in the experimental package to say localDateTime.WithMonthOfYear(10) etc - other options include a builder API, more direct ways of adding periods localDateTime.AddDays(10) and more. Feedback very welcome on this.
- Instant/Duration/Interval: These are fairly primitive types which don't have much need for extra work
LocalDateTime and ZonedDateTime have conversions to/from .NET types (DateTime and DateTimeOffset) - that may be your best bet for getting "into" the API, unless you want to construct values explicitly.
What's not there yet?
The big feature missing from all of this is formatting and parsing. We have an implementation for Instant and Offset, but those are fairly limited types - obviously ZonedDateTime and LocalDateTime are the big ones here. It's worth understanding the basic plan though...
For each appropriate type, we're going to support the framework convention of ToString on the type itself, as well as static Parse and TryParse methods. However, I personally regard those as legacy approaches. There'll also be INodaFormatter<T> and INodaParser<T> which define interfaces for formatting and parsing - with concrete implementations like LocalDateTimeParser etc. These will be thread-safe and reusable, so you'll be able to set up the parser or formatter once for each pattern you want to use. I see this as giving better maintainability and performance, as well feeling like better OO. Thoughts on this general principle would be welcome - the actual interfaces are there now, but somewhat in flux.
We have yet to decide how to expose what I'm calling pseudo-mutators - methods which get you from one value to another, e.g. the WithYearOfMonth method mentioned earlier. What we're really missing is use cases, so if you can dump a list of feature requests based on what you actually want to do that would be wonderful.
There are no doubt some other features we'll want for v1.0 - and some that might make it, but may not. If there's something you regard as a must-have, holler about it.
Where's the documentation?
The API documentation is actually pretty reasonable - at least, some exists for pretty much everything (no warnings about missing summaries) but it may very well be skimpy in places, and there's just a chance some of it is out of date. Before the full release we'll do a proper pass through, of course. This documentation is built nightly (or on demand) so by the time you read it, it may not reflect the package you've downloaded, if I've applied some fixes etc. I wouldn't expect any really big changes in the very near future though. Before release I'll set up versioned documentation.
The project wiki is in a worse state - the key concepts page is reasonably accurate, but obviously there's a lot of work to be done overall.
How do I give feedback?
The main point of releasing 0.1 as a NuGet package is to get feedback. Lots of feedback. This should be done on the project issues page. If it's a non-trivial matter which you'd like to discuss further, then it'd be great if you'd join the Google group / mailing list, but don't feel you have to get involved to leave feedback.
While high quality feedback is obviously desirable, at this point I'd really just like lots of it. So in particular:
- If the NuGet packaging is wrong, please let me know - I'm a complete newbie on this.
- I'm not going to care much whether you report something as a bug or a feature request.
- If you don't have time to see if your feedback is already covered elsewhere, don't bother.
- Even if it's only a vague idea, I still want to hear it.
- Positive feedback is useful too - say what you like as well as what you don't.
Obviously I'll go through and prune the feedback, mark duplicates etc, but that's work that I can do afterwards. If you find any "barriers to entry" when it comes to giving feedback, let me know by email (skeet@pobox.com) and I'll do what I can do fix it.
Maybe it's just me, but given how long I've used the .NET DateTime API, the word "Local" is kind of ingrained in my brain as meaning something very different than the Noda uses it.
ReplyDeleteWould it be reasonable to suggest a completely different term for LocalDateTime/LocalDate/LocalTime? Perhaps Calendar instead of Local, given that these concepts represent an instant of time mapped to a particular calendar?
That would sure make the API more understandable to a mere mortal like me...