Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Time v0.2 pre-release feedback #190

Closed
jhpratt opened this issue Nov 21, 2019 · 51 comments
Closed

Time v0.2 pre-release feedback #190

jhpratt opened this issue Nov 21, 2019 · 51 comments
Labels
C-seeking-input 📣 Category: community input is desired

Comments

@jhpratt
Copy link
Member

jhpratt commented Nov 21, 2019

Hello rustaceans! Until recently, the time crate was soft-deprecated, in that bug fixes would be considered, but no features would be actively added. It is the 25th most downloaded crate all-time, and is still being downloaded slightly more than chrono. As such, I began a bottom-up rewrite of the time crate, taking inspiration from time v0.1, chrono, the standard library, and some RFCs, along with some localization pulled in from glibc.

My goal is to release time v0.2 alongside Rust 1.40, which is currently scheduled to be released on December 19. By going to the community for a public review now, this will allow me sufficient time to make any necessary changes.

To view the documentation for the master branch, you can use time-rs.github.io. There are also some helpful shortcuts, so you can go to time-rs.github.io/Duration and be brought to the relevant documentation immediately.

Follows are two pages from the wiki, which provide some detail as to what will occur in the future and the differences between existing crates.


Differences from chrono and time v0.1

Chrono has been the de facto crate for time management for quite a while. Like time, its original implementation predates rust 1.0. Presumably due to its age, chrono still relies on time v0.1 (though it's partially inlined the relevant code). The disadvantage of this is that any third-party functions that rely on chrono essentially mandate you import chrono or time v0.1 to use its Duration type. This single fact makes interoperability with the standard library quite difficult. Time v0.1 is identical to chrono in this manner.

Time v0.1 is what the standard library was built upon. Time v0.2 flips this, being built upon the standard library. This provides for a higher-level abstraction, while also supporting the same targets as the standard library.

In time v0.2, there is full interoperability with the standard library. Every type can perform the same arithmetic that its standard library counterpart can and vice versa. Types are freely convertible to and from their standard library equivalents.

Vision

Scope

Most things that simplify handling of dates and times are in the scope of the time crate.

Some things are specifically not in scope.

  • Nothing should implicitly rely on the system's time zone. Passing a time zone as a parameter or a function returning the time zone is acceptable. This restriction ensures that nothing surprising happens when running the same code on different systems.

Explicitly in scope, but not yet implemented include the following:

  • Full timezone support, relying on tzdb.
  • Period type, which would be usable with a zoned DateTime. While this would expose a similar (if not identical) API to Duration, the advantage is that it could take into account leap seconds, DST, etc. As such, a Period would not be directly convertible to a Duration - one minute could be either 59, 60, or 61 seconds.

Both lists are explicitly non-exhaustive. Additional items that are not in scope will be added to this list as they are brought up.

Strategy

Wherever possible, types are implemented as wrappers around those in the standard library. Additional functionality is still able to be provided, such as allowing a Duration to be negative.

Some types (like Sign) were helpful to implement generically, simplifying arithmetic implementations. They may be extracted to a separate crate in the future and re-exported in time.

Future plans

If all necessary reviews go well, my intent is to release time v0.2 shortly after the release of rust 1.40.

For post-v0.2 features, eventually I'd like to support tzdb, including automatic time zone shifts for DST and native leap second support. I'd also like to be able to have 5.seconds() be able to return either a time::Duration or std::time::Duration, but the compiler is currently unable to infer the return type and choose the appropriate trait to use.


Let me know your thoughts, positive or negative. All I ask is that you be constructive and follow the code of conduct. I can be reached on Matrix either via direct message or on the #time-rs channel.

@jhpratt jhpratt added the C-seeking-input 📣 Category: community input is desired label Nov 21, 2019
@jhpratt jhpratt pinned this issue Nov 21, 2019
@wezm
Copy link

wezm commented Nov 21, 2019

Some questions that come to mind after reading this issue and poking through the docs and code briefly:

  • Is this crate compatible with 0.1?
    • If not, maybe the deprecated things should be omitted?
  • What's the thought behind using log to emit the deprecation warnings?
    • Seems people are unlikely to see these messages and the parent functions will emit a deprecation warning at compile time anyway. I guess this mostly seems like an opportunity to avoid bringing in the log crate in the default set of feature flags.

@jstrong-tios
Copy link

my wish list item: a u64 wrapper representing a nanosecond timestamp with serialization/deserialization and interoperability with the other types. it's not always possible to use u64 to store timestamps (e.g. dates > 2554-07-21) but in many cases it is, and u64 is half the size of a libc timespec. (i64 would be ok as well, max date is 2262-04-11). relatedly, a i32/u32 wrapper representing hourly precision timestamps, u16 representing daily, etc. I've used these approaches in certain circumstances but it would be a joy to have a library that allows painless conversion between these and more full-fledged datetime types.

@jhpratt
Copy link
Member Author

jhpratt commented Nov 21, 2019

@wezm

Some questions that come to mind after reading this issue and poking through the docs and code briefly:

  • Is this crate compatible with 0.1?

    • If not, maybe the deprecated things should be omitted?
  • What's the thought behind using log to emit the deprecation warnings?

    • Seems people are unlikely to see these messages and the parent functions will emit a deprecation warning at compile time anyway. I guess this mostly seems like an opportunity to avoid bringing in the log crate in the default set of feature flags.
  1. No, it is not fully compatible with 0.1. Some methods and types are included (and deprecated) where names and/or signatures have changed. Without looking at the full API, I believe most of the Duration and Instant types are compatible. As they're deprecated, they shouldn't be used in new code.

    I've also received a direct message on Matrix asking about an equivalent of the time::at method from 0.1. The equivalent would be DateTime::from_unix_timestamp(seconds) + nanos.nanoseconds(). Because it's relatively simple, I'll likely push the additional deprecated method (and similar methods) in a bit for additional back-compatibility. After looking at the old API, these functions return Tm and Timespec structs, which no longer exist.

  2. Strictly speaking, it's not a deprecation warning. It's a warning against a slight change in behavior that I felt was favorable to panicking. Users might not necessarily run across the specific situation (overflowing). It's not a bad idea to just add it to the built-in deprecation warning, or even revert to the old behavior of panicking. Either of those would eliminate the default dependency on log.


@jstrong-tios

my wish list item: a u64 wrapper representing a nanosecond timestamp with serialization/deserialization and interoperability with the other types. it's not always possible to use u64 to store timestamps (e.g. dates > 2554-07-21) but in many cases it is, and u64 is half the size of a libc timespec. (i64 would be ok as well, max date is 2262-04-11). relatedly, a i32/u32 wrapper representing hourly precision timestamps, u16 representing daily, etc. I've used these approaches in certain circumstances but it would be a joy to have a library that allows painless conversion between these and more full-fledged datetime types.

The primary problem with doing something like this is that the epoch is not immediately obvious. You could always do something like DateTime::unix_epoch() + 1_000.days() +. To go the other direction, you could do (datetime - DateTime::unix_epoch()).whole_days().

@jstrong-tios
Copy link

@jhpratt do you mean it wouldn't be obvious whether the type uses 1970-01-01 or some other epoch (1582! who's with me!?) as its reference point? My assumption was it would use the unix epoch; I think that would be the obvious choice. But there's a variety of ways to mitigate possible confusion, including, as you suggest, explicitly named "constructor" methods. In uuid crate, I was involved in improving the api for their v1 timestamp functionality which used the same idea: https://docs.rs/uuid/0.8.1/uuid/v1/struct.Timestamp.html. At a deeper level, I think that someone who cares about the memory layout of their timestamps can be trusted to read the docs specifying the epoch.

@jhpratt
Copy link
Member Author

jhpratt commented Nov 21, 2019

@jstrong-tios Yes, that is what I meant with regard to the epoch. I would personally assume the unix epoch as well, but I don't think that should be the case without being clear in the method name. That would be similar to other items excluded in the scope, where any assumptions are clearly indicated.

However, you do make a decent argument with the size of the struct. Running std::mem::size_of::<T>() gives the following currently:

Size (bytes) Struct
8 Date
16 DateTime
16 Instant
20 OffsetDateTime
0 OutOfRangeError
8 Time
4 UtcOffset

On initial thought, I could do something along the lines of Week, Day,Nanosecond, implemented as thin wrappers around the appropriate integer type. However, I'd only permit conversions to Durations, not to DateTimes directly (due to the epoch ambiguity). They would likely be placed into a submodule, though I'm not sure on the name. period would conflict with the future Period type, which won't be directly convertible to a Duration.

Feel free to join the Matrix channel if you'd like to discuss this outside of this issue! I'll likely respond quicker there as well.

@aloucks
Copy link

aloucks commented Nov 22, 2019

Why not name the functions the same as their std library counterparts?

For example:

as_secs_f32
vs
as_seconds_f32

I think there would be value in having this as a drop-in replacement.

@jhpratt
Copy link
Member Author

jhpratt commented Nov 22, 2019

@aloucks I presume you're only referring to Duration here, as the only other type with an identically-named counterpart is Instant, where I used the same API.

Basically what it comes down to is I think that "secs" is odd to use — some others like millis, micros, and nanos are more common, but as a native English speaker, I've never seen anyone use "secs" when referring to seconds in any context other than that one. Plus, at what point do you stop shortening names? Why not just Duration::as_s()?

While I understand the concern, I think it's more logical to just spell it out. Time wouldn't be able to be a drop-in replacement regardless, due to differing behaviors and function headers due to its ability to be negative. And on top of that, you picked probably the most similar method name 😛 Most of the methods are whole_minutes and similar, which provide a more descriptive name of what is returned.

Side note! If Rust had method overloading, my ideal API would actually be along the following lines:

impl Duration {
    fn seconds(i64) -> Self; // same as current
    fn seconds(self) -> i64; // same as `.whole_seconds()`
    fn seconds(self) -> f32; // same as `.as_seconds_f32()`
    fn seconds(self) -> f64; // same as `.as_seconds_f64()`
}

Unfortunately that's not quite the case. I looked into just supporting .as_seconds() in a trait and trying to infer whether it was f32 or f64, but alas that's not even currently possible.

@jhpratt
Copy link
Member Author

jhpratt commented Nov 22, 2019

@jstrong-tios Looking into this some. It looks doable, though I'm not 100% sold on it being in the 0.2 release. There would be 8 types and conversions in between all of them. Then we'd also have arithmetic between the various types (adding, subtracting, and dividing, plus multiplying by integers), equality, ordering, etc. Needless to say, there would likely be a few hundred manual trait impls that could only be partially generated with macros.

@jstrong-tios
Copy link

@jhpratt if it's any use to you, I just dug up this implementation of a daily and hourly integer wrapper types I wrote for an unpublished project. What do you have in mind in terms of things that cannot be done with macros? Comparing between the types?

@jhpratt
Copy link
Member Author

jhpratt commented Nov 22, 2019

Yeah, that's roughly what I had in mind for the API, sans chrono of course.

With regard to macros, off the top of my head I think a large portion wouldn't be able to due to the conversion constants. I believe there's a way around it, but I'd have to check to be sure (it deals with the orphan rule). I could, of course, convert it to a Duration and then to the target type, but that defeats the goal of the smaller memory footprint.

@KodrAus
Copy link

KodrAus commented Nov 24, 2019

I’m a little unsure about trying to bake in our own i18n effort here. Specifically whether it’s likely to hinder broader support in the future for apps that need to work with more than just dates and might run into inconsistencies with libs that take different approaches. Do you know of any other time-like libraries in other language ecosystems that follow this path?

@jhpratt
Copy link
Member Author

jhpratt commented Nov 24, 2019

Not entirely sure what you mean by potential inconsistencies with other libraries. I suppose there could be differences in spellings or abbreviations if that's what you mean, but I'm not aware of any major i18n/l10n crate in the ecosystem. Upon a quick search, i18n is reserved and l10n is available, so I could spin it into a separate crate (with wider support) and depend on that if that would be preferred.

With regard to other languages, yes! I actually pulled the localization from glibc.

  • glibc uses the "current" locale, so it's not possible to manually set it (aside from changing environment variables).
  • Python and Ruby, using the "current" locale.
  • JavaScript has Intl.DateTimeFormat, which uses full locales.
  • JodaTime in Java has partial support for locales (ctrl+F for "locale" on that page). However, JodaTime was largely absorbed into a core Java API (notably not the locale support, though).
  • Swift supports arbitrary locales

Of those that I searched, PHP, C♯, and Go appear to have neither language nor locale support (PHP says so explicitly). The others are above. So at least from first glance, it seems a significant number of languages have some level of support, even if not customizable.

I think it's important to have it not implicitly rely on the system locale or language, as that causes inconsistencies when running otherwise identical systems. At the minimum, the user should have to call Language::system() (or something along that line). Having support in general is useful for anyone doing l10n on web servers or client-facing software.

@jhpratt
Copy link
Member Author

jhpratt commented Nov 25, 2019

@wezm I've just removed the log dependency in favor of a more detailed compile-time message. time::precise_time_ns() and time::precise_time_s() have also been added & deprecated.

@jstrong-tios After thinking about your idea a bit more, I think it would be best to push this past 0.2, given the inherently large amount of code & complexity.

@jhpratt
Copy link
Member Author

jhpratt commented Nov 29, 2019

@KodrAus Would you prefer 0.2 not have localization? Everything could be in English, which is currently assumed if not specified. It would be relatively easy to remove the languages as it currently is.

Looking down the road, I recently ran across the Unicode Common Locale Data Repository, which seems to be the de facto authoritative source on locale information, being used by Windows, Mac, and most Linux distros, as well as by browsers like Chrome and Firefox. I am going to look through the raw data some more; I'm trying to determine how hard it would be to extract the requisite information and publish a l10n crate, which time could then depend upon. That would be a ways away, though.

@KodrAus
Copy link

KodrAus commented Nov 29, 2019

Thanks for your patience on this @jhpratt! And I should’ve mentioned before that the new API you’ve designed is great and a real leap forward. I’m trying to start at a higher-level before digging into specific API decisions.

Not entirely sure what you mean by potential inconsistencies with other libraries. I suppose there could be differences in spellings or abbreviations if that's what you mean, but I'm not aware of any major i18n/l10n crate in the ecosystem.

Would you prefer 0.2 not have localization? Everything could be in English, which is currently assumed if not specified. It would be relatively easy to remove the languages as it currently is.

I haven’t actually worked on a localized app myself, so this is really just my impression based on the scope of the localization API. If anybody who has needed to work in this space has different impressions I would defer to those 🙂

I imagine a project that requires localization would want a fully-featured localization system like Project Fluent, which looks like it has out-of-the-box datetime support, and an implementation for Rust. In this case we don’t need to build localization support into time, and consumers don’t need to work with potentially inconsistent localization APIs from different libraries in their project when supporting different cultures.

@jhpratt
Copy link
Member Author

jhpratt commented Nov 30, 2019

No problem @KodrAus. I haven't actually done a large-scale app that requires localization (yet), so I'd likewise defer as well if anyone else is following this and has input.

Fluent seems useful, and handles both full and abbreviated text as well; it appears to be based off of JavaScript's API. You're probably right that anything needing localization would be on a scale higher than just dates and times.

After taking a quick look at Fluent on the playground, I'll almost certainly pull the localization code tonight, unless I see some significant reason not to.

Update: Language handling has been fully removed.

@Y0ba
Copy link

Y0ba commented Dec 2, 2019

Currently functions which create Date/Time objects from parts (e.g. Date::from_ymd) panic if you supply wrong parts. Is it possible to make them return Result, or add another methods to return Result, so i can track these errors?

@jhpratt
Copy link
Member Author

jhpratt commented Dec 2, 2019

@Y0ba I can look into additional methods that would return an Option or Result, though the latter may be slightly more complex (not too bad, though). What do you think they should be called? Date::from_ymd_opt and similar? The main thing I'll have to look into is how it'll interact with the macro for checking the value is in range.

@Y0ba
Copy link

Y0ba commented Dec 2, 2019

My usecase is that these parts come from device via COM-port, and they might be either wrong or just corrupted, therefore both Option and Result are fine by me since I don't need exact reason, just the fact of failure (but maybe someone else will need it for better logging or error reporting 🤷‍♂).

What do you think they should be called?

_res or _opt suffixes are fine. _fallible looks better but it's probably too long.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 2, 2019

Ok, sounds good! I'll take a look into it tomorrow or Tuesday; hopefully updating the macros won't be too tricky.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 3, 2019

Well, I just tried implementing a trait and a variety of objects to allow for a very detailed error (actual range, given value, what type it was), but it seems like it's not currently possible to do without a ton of effort. I could likely work around it by creating another layer of abstraction, but then it gets confusing to follow for the end user.

So, they'll be methods returning an Option. And the name in the form of try_foo, which is a bit more descriptive.

Update: Implemented returning an Option! @Y0ba

@jhpratt
Copy link
Member Author

jhpratt commented Dec 4, 2019

Issue I would like to resolve: std::time::Duration allows for values between 0 and u64::max_value(); time::Duration between -u64::max_value() and u64::max_value().

Should Duration::whole_seconds() return an i128 rather than an i64? That would allow the full range to be captured if someone did Duration::seconds(i64::max_value()) * 2 (which results in a valid value). Right now, whole_seconds() overflows. I believe the alternative is to forcibly restrict the range, which should be doable without too much effort.

I can't think of any conceivable reason why i64::max_value() seconds (positive or negative) wouldn't be sufficient for any use case; it's literally longer than the age of the universe.

How overflows are handled in general is something that I do need to check and possibly modify behavior before a release, though.


Update: Overflow handling should be sensible now. max_value and min_value have been undeprecated, and the range of Duration is restricted.

@stevenroose
Copy link

Are there any good reasons to require Rust v1.40? I'm quite curious. It seems like a time crate should be able to be built using older compilers.
We're currently using v1.22, v1.32 and v1.36 because of different reasons/constraints. I realize that 1.22 is very old, especially in Rust land, but it should be possible to at least target a version that has been stable for a few months instead of a version that is not even released yet..

@jhpratt
Copy link
Member Author

jhpratt commented Dec 9, 2019

@stevenroose #[non_exhaustive] is used, which is currently in beta. The alternative is to have a __nonexhaustive variant on the relevant enums, which requires explicit panics inside the library where it's used.

After the initial release, bumping the minimum version for rustc will allow for the then-current stable and the two previous versions (which would currently be 1.37, as stable is 1.39). I don't think I wrote that down anywhere, so I'll add it to the wiki in a bit.

Keep in mind that once rust-lang/rust#65262 lands, that stable can be the permanent MSRV, as any future changes could be gated behind that. This is currently possible via rustversion crate, but that would require adding in a mandatory proc macro dependency, increasing compilation time. If I implement #191 using proc_macro_hack instead of waiting for nightly APIs to stabilize, I might consider using rustversion as well.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 10, 2019

@alexcrichton @KodrAus Any final concerns, or can publishing rights be granted on crates.io in anticipation of a release next Thursday(ish)? That's the final item in the rough plan here from back in September.

Pinging @Manishearth as well, who had initially expressed reservations about transferring control of the crate.

@KodrAus
Copy link

KodrAus commented Dec 11, 2019

Thanks for the ping @jhpratt! This is looking great!

The only real concern I have besides localization, which we’ve sorted, is the presence of a raw DateTime type with no associated offset or zone information. In practice, I think you never really want the raw date and time without an offset because it’s simply not possible to convert into any meaningful instant on the time line. I know a lot of standard libraries in languages have a type like DateTime, but languages like Java and C# also have ecosystem alternatives that ‘fix’ issues with ambiguous date time code that comes from not knowing a time zone or offset.

Can we think of any really compelling usecase for DateTime over OffsetDateTime that isn’t just a variant of not wanting to think about offsets that makes it worth including?

@jhpratt
Copy link
Member Author

jhpratt commented Dec 11, 2019

Would PrimitiveDateTime work? I feel like that has a less negative connotation than "naive", at least as a native English speaker.

@KodrAus
Copy link

KodrAus commented Dec 11, 2019

Primitive is a bit wordy, but I get what you mean, and would personally be on board with PrimitiveDateTime 👍 At least I can't think of any better names right now!

@jhpratt
Copy link
Member Author

jhpratt commented Dec 11, 2019

Yeah, I just feel like "naive" would lead users away from it, even when it's the appropriate type to use. I'll make that change in a minute — should be relatively easy.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 11, 2019

Just pushed up the change, should be all set @alexcrichton (unless @KodrAus has anything else).

@KodrAus
Copy link

KodrAus commented Dec 11, 2019

I'll just do one more quick pass over the public API now 🙂

@KodrAus
Copy link

KodrAus commented Dec 11, 2019

So I just had a few more questions:

Do we need our own public Sign enum? It seems like a very general-purpose type?

Having the try_from_* methods return Option instead of Result seems a bit unfortunate, because it's not really consistent with the TryFrom trait. I saw you couldn't really give detailed information back from those methods in an Error without a lot of work, but what about something like an opaque public top-level Error type? As a start it could be:

pub struct Error {
    kind: ErrorKind,
}

enum ErrorKind {
    Message(&'static str),
    Parse(ParseError),
}

impl From<ParseError> for Error { .. }

impl std::error::Error for Error {
    fn cause(&self) -> Option<&dyn std::error::Error> {
        match self.kind {
            ErrorKind::Parse(e) => Some(e),
            _ => None,
        }
    }
}

impl Error {
    fn as_parse(&self) -> Option<&ParseError> { .. }
}

and then return this top-level Error type from fallible methods, which may mean wrapping up a ParseError or some general message. I find having a single top-level Error type in a library makes it easier to integrate into the error handling of applications so that they only have a single type to consider. What do you think?

And I promise that's the last of it from me! 😁 This really is a phenomenal effort!

@jhpratt
Copy link
Member Author

jhpratt commented Dec 12, 2019

The Sign enum is certainly very broad, as it makes any code interacting with it very easy to understand. There's actually a similar type in the num crate that serves a similar purpose. The alternative is to have Duration::sign return -1, 0, or 1, similar to the signum method on primitive numerical types, though that doesn't have the space-saving advantage when doing Option<_>.

I was originally going to split the enum into its own crate, but the "sign" name is being squatted, and despite the obviousness of it, crates.io policy is ambivalence.

I'd actually prefer a Sign enum be present in the standard library, but regardless.

For try_from_* methods, I'd absolutely prefer to return a Result. I actually just had an idea that might allow for some detailed error reporting like I wanted. I'll create an error type that will be forward-compatible with that idea, returning that instead of None.

And I swear I don't mind! Feedback is important 😄

@alexcrichton
Copy link

cc @skade IIRC you mentioned you were also curious in this, would you like a chance to review this API?

@galich
Copy link

galich commented Dec 12, 2019

can't help but ++ @Y0ba request about introducing non-panic API.
in fact, it would be better to remove APIs that can panic.

time is used directly or indirectly by many crates, it's a real shame to have otherwise solid Rust app to panic because there is user input with invalid data. It's a little burden to deal with Result<>, but we do not have much control over what 3rd party crate pass to time internally.

@Y0ba
Copy link

Y0ba commented Dec 12, 2019

in fact, it would be better to remove APIs that can panic.

👍 Or at least mark them as deprecated and remove in v1. That way v0.2 and 0.1 will be more compatible and developers will have more time to migrate to fallible versions.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 12, 2019

Would it be satisfactory to clearly indicate in documentation that the Result-returning variant should be preferred when handling user input?

I hesitate to remove them entirely, because if you're using a hard-coded value, you'd still have to use .unwrap() (even if you know it's valid). For a 1.0 release, I definitely want to have #191 land, which would provide compile-time verification of those hard-coded values. Once that happens, I'd have zero problems removing the panicking functions, only having them return Results.

Note that there will almost certainly be released between 0.2 and 1.0, which would give sufficient time for deprecation.

@galich
Copy link

galich commented Dec 12, 2019

may be introduce panicing-api feature to make it harder to ignore the problem? i realize we can't change world over night, but it has to be done eventually. Any change made in time crate, will have to ripple through dependencies and it may take a year or two.

just to illustrate what the pain the panic is: i have 2 crates outside of my control between my app and time crate. My QA figured to try Jan 1st 1820 input, which immediately broke app and i have to either start adding custom data checking before it reaches panic point or wrap calls i can't control with panic::catch_unwind.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 12, 2019

Hmm…a feature flag may work, provided it's disabled by default. It could be clearly indicated in documentation that the feature is necessary to use the function, similar to existing methods (rustdoc is built using nightly).

The fact that everything has to cascade through the ecosystem is a good point, and is definitely a reason to do something now as opposed to later.

I'll think about it tomorrow at some point, but my initial thought is that it would be an acceptable solution.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 12, 2019

I've decided to go ahead with gating the panicking API behind a feature flag. This will not apply to the various "format" methods, as the only time they can panic is if the developer provided an invalid formatting string.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 14, 2019

@galich @Y0ba Panicking methods (that aren't formatters) have been placed behind a feature flag (panicking-api). This is clearly documented in multiple places, and tests have been updated to match.

@ymtvx
Copy link

ymtvx commented Dec 15, 2019

May I ask what needs to be done to convert a UTC time to a local time in 0.2? Before we could do:

tzset();
let tm = at(timespec);

@jhpratt
Copy link
Member Author

jhpratt commented Dec 15, 2019

@ymtvx Unfortunately getting the system time zone is not possible in 0.2, at least yet. I am going to investigate how difficult it would be to support, at a minimum, Unix and Windows APIs, which would cover the major three operating systems. However, it shouldn't come as a surprise that it's basically impossible to cover all systems (that's one of the large problems with 0.1).

Note that this is probably the one thing where I'd be okay with changing #![forbid(unsafe_code)] into #![deny(unsafe_code)], mandating that each usage be manually checked and commented.

If this is included in a future version, it would definitely be gated, since it would add a runtime dependency (libc).

@KodrAus
Copy link

KodrAus commented Dec 16, 2019

@galich @Y0ba Panicking methods (that aren't formatters) have been placed behind a feature flag (panicking-api).

Hm, I personally find the use of a feature flag for panicking a big strange unless it's an explicit goal to remove them, in which case I think they should just be changed to return Result (just the try_ variants without the prefix). I don't see a reason to delay ripping the band-aid off. Since the panicking versions are gated, and I don't imagine users are likely to go looking for a feature to enable them, they're effectively soft-deprecated already. But I don't want to overstate the strength of my opinion here :)

@KodrAus
Copy link

KodrAus commented Dec 16, 2019

So I'm 👍 on updating the crate owners and publishing a 0.2 release at any point now.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 16, 2019

@KodrAus Long-term (like 1.0), I would like to have them removed. The panicking methods, when used with known values, can be verified at compile-time. I actually have a proof of concept time macro now, though it relies on proc_macro_hygiene to expand into expressions. It also uses diagnostics to provide helpful error messages. I don't think there's a ton holding those up from being stabilized, so hopefully it'll be sooner rather than later.

Would it be preferable to just nuke the panicking versions right now, before a release? That would force users to explicitly handle the case where input is invalid, at least until a macro of that sort is allowed on stable.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 16, 2019

Also, awesome! @alexcrichton unless anyone else has objections, we should be good to go for Thursday.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 18, 2019

Closing this issue, as all necessary reviews have been performed. v0.2 will be published on crates.io at some point tomorrow. If you encounter any issues, please file a new issue.

Thanks everyone for your input!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-seeking-input 📣 Category: community input is desired
Projects
None yet
Development

No branches or pull requests

10 participants