You don't have to pay attention too much to the derived traits, some I would have to implement myself to cater to specific behavior.
Basically, I want to implement some sort of inheritance/interface-like hierarchy for the timestamps, but I'm not sure if this is the best way to do it in rust.
I like that idea. I guess the only difference/issue with that is that I could now pass any type that implements PartialOrd to Timeframe, even though I technically only want it to "work" with the two timestamp variants I have.
I'd say yes. num::Unsigned is a famous example for such a trait that is pretty much only used to mark any unsigned number type, independent of the precision. All the functionality you can use is provided by the supertrait Num in that case which is Eq + Ord in your case. Though PartialOrd like @jbe suggested would be sufficient.
Note that, because Timestamp is a public trait, any downstream user will be able to implement it for their own types— You can't rely on the fact that it will only be one of the two types you provide.
I don't know any crates using Unsigned in their apis, but it offers the same interface like Num, so you can do basic arithmetic, equality comparisons and string conversion.
I think generally, the purpose of a trait without methods is to describe certain properties (usually of other methods which are not defined by that trait). Maybe the most prominent example would be Eq:
pub trait Eq: PartialEq<Self> { }
If I have a function or method that takes an argument T: Eq, then I guarantee that the type's PartialEq::eq implementation (which isn't part of Eq) will behave in a certain way. Eq is neither an unsafe trait nor sealed. Violating that guarantee must not cause undefined behavior but may result in other errors.
So getting back to the OP, I think the question is: Does Timestamp give any guarantees that go beyond its supertrait Ord? These guarantees don't need to be enforced (compare Eq, which also doesn't enforce a compliant PartialEq::eq implementation). But if there are no guarantees at all (not even unenforced ones), then I would consider the Timestamp trait to be non-idiomatic.
Hmm, but wouldn't the guarantee that only TimestampSeconds and TimestampMillis implement this trait be a sufficient to make this code idiomatic in your opinion? The guarantee being upheld with a sealed supertrait. Or more broadly speaking, the guarantee that the crate's author has the full control which types will be accepted by their api?
You don't even need to seal the trait for that purpose, I guess (then it's not enforced, but it can still be the "meaning" of the trait).
The question is: What's the purpose of that guarantee?
Sometimes unsafe code relies on that (in which case the trait can be defined as unsafe, so implementing it requires unsafe).
Maybe it can make sense if the documentation doesn't want to describe which behavior in particular is expected and if the behavior can change (without breaking the API).
I agree that in these cases, sealing the trait may be best.
I don't really know. But I've seen the wish for restricting what types are accepted further than the implementors of the needed trait (often times a widely implemented one from the standard library, like in OP's case) crop up here or on Stack Overflow occasionally. I always thought restricting an interface like that is for the purpose of api ergonomics, maybe to make your crate feel more self-contained or be closer to your domain model? I like your idea that such a trait can help in case one wants to change the api in the future. Or maybe you anticipate the need to actually add methods in a future release.