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

Display for SystemTime #53892

Closed
wants to merge 2 commits into from
Closed

Conversation

stepancheg
Copy link
Contributor

Implement Display for SystemTime

Time is formatted as ISO-8601 in UTC timezone.

Nanoseconds are printed by default, unless precision is specified:

1966-10-31T14:13:20.123456789Z // by default: {}
1966-10-31T14:13:20Z           // with format {.0}
1966-10-31T14:13:20.1Z         // with format {.1}

Implementation is partially derived from chrono crate (CC
@quodlibetor).

This is not fully-featured date-time implementation. The motivation
for this commit is to be able to quickly understand what system
time is (e. g. when printed in logs) without introducing external
dependencies.

Format is similar to java.time.Instant.toString() output except
that Java truncates trailing zeros by default:
https://repl.it/repls/NauticalSeveralWheel

I think it's better to not truncate zeros by default, because:

  • data output (e. g. in logs) look better when field length is the same,
    e. g. with default Java formatter this is how log output may look like:
1966-10-31T14:13:20.123456789Z Server started
1966-10-31T14:13:21Z Connection accepted
1966-10-31T14:13:21.567Z Request started
  • precision information is lost (e. g. when reading string
    1966-10-31T14:13:20Z it's not clear, is it exactly zero millis, or
    timestamp has seconds precision)

This patch depends on leap seconds clarification:
#53579

Issue: #53891

Needed to add another mod in `time`, see following commit.
Time is formatted as ISO-8601 in UTC timezone.

Nanoseconds are printed by default, unless precision is specified:

```
1966-10-31T14:13:20.123456789Z // by default: {}
1966-10-31T14:13:20Z           // with format {.0}
1966-10-31T14:13:20.1Z         // with format {.1}
```

Implementation is partially copied from `chrono` crate (CC
@quodlibetor).

This is not fully-featured date-time implementation. The motivation
for this commit is to be able to quickly understand what system
time is (e. g. when printed in logs) without introducing external
dependencies.

Format is similar to `java.time.Instant.toString()` output except
that Java truncates trailing zeros by default:
https://repl.it/repls/NauticalSeveralWheel

I think it's better to not truncate zeros by default, because:

* data output (e. g. in logs) look better when field length is the same,
  e. g. with default Java formatter this is how log output may look like:

```
1966-10-31T14:13:20.123456789Z Server started
1966-10-31T14:13:21Z Connection accepted
1966-10-31T14:13:21.567Z Request started
```

* precision information is lost (e. g. when reading string
  1966-10-31T14:13:20Z it's not clear, is it exactly zero millis, or
  timestamp has seconds precision)

This patch depends on leap seconds clarification:
rust-lang#53579
@rust-highfive
Copy link
Collaborator

r? @bluss

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 1, 2018
@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-5.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.

[00:04:36] travis_fold:start:tidy
travis_time:start:tidy
tidy check
[00:04:37] tidy error: /checkout/src/libstd/time/iso_8601.rs:154: line longer than 100 chars
[00:04:37] tidy error: /checkout/src/libstd/time/iso_8601.rs:158: line longer than 100 chars
[00:04:38] some tidy checks failed
[00:04:38] 
[00:04:38] 
[00:04:38] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-tools-bin/tidy" "/checkout/src" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "--no-vendor" "--quiet"
[00:04:38] 
[00:04:38] 
[00:04:38] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test src/tools/tidy
[00:04:38] Build completed unsuccessfully in 0:00:50
[00:04:38] Build completed unsuccessfully in 0:00:50
[00:04:38] Makefile:79: recipe for target 'tidy' failed
[00:04:38] make: *** [tidy] Error 1

The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:214dee17
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)
---
travis_time:end:2ac2c17b:start=1535843260332581007,finish=1535843260339823818,duration=7242811
travis_fold:end:after_failure.3
travis_fold:start:after_failure.4
travis_time:start:208770d7
$ ln -s . checkout && for CORE in obj/cores/core.*; do EXE=$(echo $CORE | sed 's|obj/cores/core\.[0-9]*\.!checkout!\(.*\)|\1|;y|!|/|'); if [ -f "$EXE" ]; then printf travis_fold":start:crashlog\n\033[31;1m%s\033[0m\n" "$CORE"; gdb -q -c "$CORE" "$EXE" -iex 'set auto-load off' -iex 'dir src/' -iex 'set sysroot .' -ex bt -ex q; echo travis_fold":"end:crashlog; fi; done || true
travis_fold:end:after_failure.4
travis_fold:start:after_failure.5
travis_time:start:055af442
travis_time:start:055af442
$ cat ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers || true
cat: ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers: No such file or directory
travis_fold:end:after_failure.5
travis_fold:start:after_failure.6
travis_time:start:08213c12
$ dmesg | grep -i kill

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@retep998
Copy link
Member

retep998 commented Sep 2, 2018

Can there be some windows specific tests for times that occur before the unix epoch?

@stepancheg
Copy link
Contributor Author

stepancheg commented Sep 2, 2018

@retep007 unit tests in iso_8601.rs include formatting of values below the year 1600 which is a min value for windows timestamp (FILETIME), and between years 1600 and 1970.

@danburkert
Copy link
Contributor

Big +1 on the premise. Here’s an alternate implementation which is a translation from musl: https://github.com/danburkert/kudu-rs/blob/master/src/timestamp.rs. I don’t have an opinion on which is cleaner or more performant, but thought I’d throw it out there. I don’t claim copyright for that version since it’s translated from musl.

@Mark-Simulacrum
Copy link
Member

I'd personally prefer that we perhaps have a method on SystemTime similar to display() on Path but perhaps named iso8601 so that we can use an alternative format down the line without breaking people who might rely on the fact that we emit that specific encoding from the Display implementation.

We'll want to gather consensus from @rust-lang/libs as well here, I think.

@WiSaGaN
Copy link
Contributor

WiSaGaN commented Sep 2, 2018

I'd probably prefer we have real DateTime types like in Chrono to handle leap year concepts, and keep SystemTime light weight. Maybe we can start pushing Chrono to 1.0.

@danburkert
Copy link
Contributor

@WiSaGaN one issue I've run into in the past is that chrono's DateTime has a much smaller range than SystemTime, so it's not possible to use chrono to convert a SystemTime to iso8601 format. Note that the implementation in this PR does handle leap years correctly. Can you expand more on what you mean by light weight? The implementation here doesn't change the representation of SystemTime or otherwise pessimize existing usecases for SystemTime.

test_impl("5138-11-16T09:46:40Z", 100000000000, 0, 0);
test_impl("33658-09-27T01:46:41Z", 1000000000001, 0, 0);
// Largest value GNU date can handle
test_impl("2147485547-12-31T23:59:59Z", 67768036191676799, 0, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to wikipedia, years more than 4 digits should have a leading +: https://en.wikipedia.org/wiki/ISO_8601#Years. I haven't actually read the standard so I don't know if this is required or recommended or what.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right. I've looked in the standard, and it uses a sign everywhere when a year has more than 4 digits. Also, Java implementation adds a sign: https://repl.it/repls/SphericalPrevailingTrapezoids Thank you!

@WiSaGaN
Copy link
Contributor

WiSaGaN commented Sep 2, 2018

@danburkert I am more worried about pulling in the entire date time concept into the standard library without proper vetting just for a convenience method. The Chrono crate is developed outside of the standard library to give the design and implementation time to mature without committing to it too early. Display have much higher standard for inclusion in the past. For a user facing output, assuming SystemTime is a date time concept for UTC timezone also seems too specific. For one, I would rather prefer to explicitly spell out what exactly is the formatting in the method name, or implement Display only when there is a canonical way of displaying (e.g. have DateTime type to embed timezone). This in turn requires more detailed design upfront to ensure standard library is consistent with de facto community standard Chrono.

@SimonSapin
Copy link
Contributor

-1 to this PR as-is.

This is adding calendaring functionality to the standard library. If we’re going to do this, I think we should also provide a proper API for it (with conversion to a tuple or struct with year/month/etc. components), otherwise people will be tempted to parse the result of .to_string(). This API would need a concrete design proposal that might be better made in an RFC.

@stepancheg
Copy link
Contributor Author

@SimonSapin proper calendar implementation has many features:

  • dealing with named time zones and daylight saving
  • not every timestamp is valid in any timezone
  • date with time zone, local date and time, local date, local time objects
  • days of weeks
  • month, day of week names
  • printing/parsing with lots of options
  • localization

I think that all of this is a too large addition to the standard library. It's better to do it in external crate.

The goal of this patch is to provide a simple default human-readable display and nothing more.

otherwise people will be tempted to parse the result of .to_string()

That is a possibility. Documentation may mention external crates to help people avoiding this mistake.

@stepancheg
Copy link
Contributor Author

Updated the patch:

  • Display is now implemented by a separate struct SystemTimeDisplayIso8601 which is created by display_iso_8601 method of SystemTime
  • Leading + is added to years after 9999
  • Fixed a bug which caused overflow on certain dates
  • Fixed formatting

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-5.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
[00:47:05] ....................................................................................................
[00:47:09] ....................................................................................................
[00:47:11] .......................i............................................................................
[00:47:14] ....................................................................................................
[00:47:16] ........................................................................iiiiiiiii...................
[00:47:22] ....................................................................................................
[00:47:25] ....................................................................................................
[00:47:28] .....................................................i..............................................
[00:47:31] ....................................................................................................
---
[01:07:28] .................................................................................................... 600/940
[01:07:39] ......................................................................iiii.......................... 700/940
[01:07:53] .................................................................................................... 800/940
[01:07:58] .......................................................................................iiii......... 900/940
[01:08:02] ..............................F..F......
[01:08:02] 
[01:08:02] 
[01:08:02] ---- time/mod.rs - time::SystemTime::display_iso_8601 (line 386) stdout ----
[01:08:02] error[E0658]: use of unstable library feature 'system_time_display_iso_8601' (see issue #53891)
[01:08:02]  --> time/mod.rs:390:42
[01:08:02]   |
[01:08:02] 7 |     format!("{}", SystemTime::UNIX_EPOCH.display_iso_8601()));
[01:08:02]   |
[01:08:02]   |
[01:08:02]   = help: add #![feature(system_time_display_iso_8601)] to the crate attributes to enable
[01:08:02] 
[01:08:02] error[E0658]: use of unstable library feature 'system_time_display_iso_8601' (see issue #53891)
[01:08:02]  --> time/mod.rs:392:45
[01:08:02]   |
[01:08:02] 9 |     format!("{:.3}", SystemTime::UNIX_EPOCH.display_iso_8601()));
[01:08:02]   |
[01:08:02]   |
[01:08:02]   = help: add #![feature(system_time_display_iso_8601)] to the crate attributes to enable
[01:08:02] 
[01:08:02] error[E0658]: use of unstable library feature 'system_time_display_iso_8601' (see issue #53891)
[01:08:02]   --> time/mod.rs:394:45
[01:08:02]    |
[01:08:02] 11 |     format!("{:.0}", SystemTime::UNIX_EPOCH.display_iso_8601()));
[01:08:02]    |
[01:08:02]    |
[01:08:02]    = help: add #![feature(system_time_display_iso_8601)] to the crate attributes to enable
[01:08:02] 
[01:08:02] thread 'time/mod.rs - time::SystemTime::display_iso_8601 (line 386)' panicked at 'couldn't compile the test', librustdoc/test.rs:333:13
[01:08:02] 
[01:08:02] 
[01:08:02] ---- time/mod.rs - time::SystemTimeDisplayIso8601 (line 450) stdout ----
[01:08:02] error[E0658]: use of unstable library feature 'system_time_display_iso_8601' (see issue #53891)
[01:08:02]  --> time/mod.rs:454:42
[01:08:02]   |
[01:08:02] 7 |     format!("{}", SystemTime::UNIX_EPOCH.display_iso_8601()));
[01:08:02]   |
[01:08:02]   |
[01:08:02]   = help: add #![feature(system_time_display_iso_8601)] to the crate attributes to enable
[01:08:02] 
[01:08:02] error[E0658]: use of unstable library feature 'system_time_display_iso_8601' (see issue #53891)
[01:08:02]  --> time/mod.rs:456:45
[01:08:02]   |
[01:08:02] 9 |     format!("{:.3}", SystemTime::UNIX_EPOCH.display_iso_8601()));
[01:08:02]   |
[01:08:02]   |
[01:08:02]   = help: add #![feature(system_time_display_iso_8601)] to the crate attributes to enable
[01:08:02] 
[01:08:02] error[E0658]: use of unstable library feature 'system_time_display_iso_8601' (see issue #53891)
[01:08:02]   --> time/mod.rs:458:45
[01:08:02]    |
[01:08:02] 11 |     format!("{:.0}", SystemTime::UNIX_EPOCH.display_iso_8601()));
[01:08:02]    |
[01:08:02]    |
[01:08:02]    = help: add #![feature(system_time_display_iso_8601)] to the crate attributes to enable
[01:08:02] 
[01:08:02] thread 'time/mod.rs - time::SystemTimeDisplayIso8601 (line 450)' panicked at 'couldn't compile the test', lib

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@SimonSapin
Copy link
Contributor

String parsing would definitely happen, and some of it with bugs. Documentation is not gonna stop that, even when it is read.

proper calendar implementation has many features:

Yes. What this means is that this PR is adding a non-proper (incomplete) calendar implementation. And again, if we’re gonna do that, I strongly feel that we should do it with an API that is not only string-based. To express this more formally:

@rfcbot postpone

@SimonSapin SimonSapin added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Sep 3, 2018
@SimonSapin
Copy link
Contributor

With the appropriate team label…

@rfcbot postpone

@rfcbot
Copy link

rfcbot commented Sep 3, 2018

Team member @SimonSapin has proposed to postpone this. The next step is review by the rest of the tagged teams:

Concerns:

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-postpone This issue / PR is in PFCP or FCP with a disposition to postpone it. labels Sep 3, 2018
@withoutboats
Copy link
Contributor

String parsing would definitely happen, and some of it with bugs. Documentation is not gonna stop that, even when it is read.

I don't buy this line of reasoning. What's to stop someone from doing the same thing when they import a third party crate to get this behavior?

Getting a reasonable human readable representation of SystemTime is core functionality that users shouldn't have to dig around crates.io to get. Depriving users of the functionality they need so that they don't write bad code is the philosophy that has given Rust the reputation for being antagonistic and unnecessarily difficult to get things done with.

@withoutboats
Copy link
Contributor

@rfcbot concern i-want-to-merge

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-postpone This issue / PR is in PFCP or FCP with a disposition to postpone it. labels Sep 3, 2018
@stepancheg
Copy link
Contributor Author

stepancheg commented Sep 4, 2018

@alexcrichton

How certain are we that the implementation here is correct?

It is not :)

Just to prove it's correct I wrote a simple program which validates random timestamp formatting against strftime, and found bugs.

Going to submit a fix soon.

Edit: done.

@kennytm kennytm added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 4, 2018
@stepancheg
Copy link
Contributor Author

@alexcrichton

is there documentation and/or standards that we can point to saying "look, we're covering all these cases"?

There's ISO-8601 standard, and AFAIU it is not available freely. There's a draft available online.

It leaves some details to implementation, in particular, how to format years outside of range 0000..=9999: "The interchange parties shall agree the additional number of digits in the time element year" (section 4.1.2.4). And it is not clear if -0001 is a valid year representation

I think the right thing here is to make implementation compatible with Java implementation: print minimum number of digits, but at least 4.

Note that JavaScript Date.toISOString uses 6 digits for years outside of 0000..=9999.

@withoutboats
Copy link
Contributor

I don’t understand, you’re saying that calendaring functionality might not belong in the standard library but you also want to merge this PR which adds calendaring functionality to the standard library?

Sorry, I'm saying that I initially believed this was your position. I'm all for adding some basic calendaring functionality to std.

@liigo
Copy link
Contributor

liigo commented Sep 4, 2018

+1 to
SystemTime::display_utc(&self) -> String, or
SystemTime::display_utc(&self) -> Result(String, Error)
so that we can have other display implementations in the future (dispaly_with_timezone() etc.).
and display_utc may be easily deprecated (when we have better implementation).

@SimonSapin
Copy link
Contributor

There's ISO-8601 standard, and AFAIU it is not available freely.

That standard contains a lot of variants like 2018-W36-2 that we’re not interested in here. RFC 3339 is freely available and defines a "profile" (subset) of ISO-8601.

@stepancheg
Copy link
Contributor Author

@SimonSapin RFC 3339 does not allow years outside of range 0000..=9999, which is probably the only ambiguous thing in date representation:

All dates and times are assumed to be in the "current era", somewhere between 0000AD and 9999AD.

@alexcrichton
Copy link
Member

@stepancheg ah I'm less worried actually about the ISO-8601 format itself and moreso about how we convert and epoch number of seconds to a datetime with y/m/d and such. There's lots of handling there that looks super tricky. Does ISO-8601 cover that sort of conversion though?

@stepancheg
Copy link
Contributor Author

@alexcrichton as I said, I've checked implementation is correct with this random program (assuming that strftime on macOS is correct).

AFAIU the tricky part of the calendar implementations is timezone/daylight saving handling. UTC has none of it.

Leap seconds handling is another possible complication, and SystemTime (unixtime or FILETIME) does not have it.

Leap year logic is the same as in ISO-8601, quoting:

The Gregorian calendar distinguishes common years of 365 consecutive calendar days and leap years of 366 consecutive calendar days. A leap year is a year whose year number is divisible by four an integral number of times. However, a centennial year is not a leap year unless its year number is divisible by four hundred an integral number of times.

Also,

The use of this calendar for dates preceding the introduction of the Gregorian calendar (also called the proleptic Gregorian calendar) should only be by agreement of the partners in information interchange.

Standard also says that a day is 24 hours, an hour is 60 minutes, and a minute is 60 seconds.

This is exactly how the algorithm is implemented.

@quodlibetor
Copy link
Contributor

To weigh in a little bit with my experience handling bug reports for and thinking about the future of chrono.

Overall I think that this specific change (adding an alternate UTC display for SystemTime behind some sort of method) will be good and nice.

Also, there are lots of complexities around date and time handling, such that even the smallest minimum viable calendar implementation is hugely complex. It took Java several attempts -- the initial release in the 90s barely had java.util.Date, Joda-Time came out initially in 2001, was worked on more-or less-continuously, and was finally standardized in 2014, with significant changes. The most recent attempt in C++ is in the process of getting standardized (according to a comment by the author, I'm not super familiar with the C++ chrono lib)

I think that the idea that we will be able to design anything minimal enough to be helpful that will survive half a decade without ugly warts coming up is unlikely. Some points that are just difficult to answer right now are summarized nicely by @gnzlbg when discussing a direct port of C++'s std::chrono

Lack of const trait methods could be worked around a bit, but the recursive trait declarations would be painful to workaround (one could add blanket impls). The real problem is implementing these traits. Even with const generics we would still need specialization, maybe with the lattice rule, and negative reasoning, to implement some of the traits.

I think that blocking a well partitioned, UTC- or TAI-based display that is basically rfc-3339 with year extension on a minimal calendar system will mean that you end up with neither.

That said, if anyone wants to work with me on breaking up chrono so that we can start seeing what minimal even means for calendars, that might be fun.

@alexcrichton
Copy link
Member

We discussed this briefly at today's @rust-lang/libs triage meeting, and @SimonSapin convinced me personally at least that the type returned here could and probably should have accessors for things like year/month/day (of just what we're already displaying).

Along those lines though this is getting large enough that I think we may want to have an RFC on this topic instead of just a PR. The std::time module is very conservative right now and lots of people have thoughts on how the module should look, so I think it'd be good to get broader feedback on this change.

@stepancheg
Copy link
Contributor Author

stepancheg commented Sep 6, 2018

I think Java API (which is based on the famous joda-time library) is close to perfection (it's a bit overengineered, but I'll leave that part).

Java API has following classes:

  • Instant: nanoseconds since epoch, similar to SystemTime in Rust
  • LocalDateTime: year/month/day/hour/minute/second/nanosecond in local time zone, similar to NaiveDateTime from chrono
  • ZonedDateTime: LocalDateTime + timezone info
  • (OffsetDateTime, LocalTime, LocalDate, MonthDay, YearMonth, Year, Month, DayOfWeek, and a couple of others, not interesting right now)

These types are convertible to string (override of Object.toString(), similar to Debug or Display in Rust):

LocalDateTime -> 2018-09-06T04:34:23
ZonedDate     -> 2018-09-06T04:34:23+0100[Europe/London]
Instant       -> 2018-09-06T03:34:23Z

There are conversions available between these three main types:

LocalDateTime + zone -> ZonedDateTime
ZonedDateTime -> Instant
ZonedDateTime -> LocalDateTime
Instant + zone -> ZonedDateTime

What I wanted to say is that: there's no conversion from Instant to LocalDateTime. Developer cannot directly obtain UTC time components from Instant. To get a year (for example) in UTC timezone, a developer must write either:

instant.atZone(ZoneId.of("UTC")).getYear(); // via ZonedDateTime
instant.atOffset(ZoneOffset.UTC).getYear(); // via OffsetDateTime

At that seems perfectly reasonable to me: if a developer needs a year, month and day, then very likely they do some calendaring, and that calendaring is done in a timezone of some user.

There are some practical applications when calendars need to be done in UTC, but they are very rare. The only possible practical use case I can think of is formatting a timestamp in UTC timezone but in slightly different format than the default (e. g. space-separated instead of T-separated).

(But if I miss some important use cases, please tell me.)

And that rare use case does not justify the existence of a shortcut operation which converts SystemTime to UTC NativeDateTime.

Even full-blown date-time API should not have a no-parameter operation which converts SystemTime to UTC time components, so our first step API should not have this operation either.

So, sorry, I cannot write an RFC which provides SystemTime::to_utc_date_time. What I could do instead:

Option 1: write an RFC which simply provides a display

Option 2: write a minimal TZ library:

struct TimeZone { ... }
impl TimeZone {
  fn forId(id: &str) -> Result<TimeZone> { ... }
  fn utc() -> TimeZone { ... }
  fn local() -> TimeZone { ... }
}

struct LocalDateTime { year, ..., nanoseconds }

impl SystemTime {
  fn to_local_date_time(&self, tz: &TimeZone) -> LocalDateTime { ... }
  fn from_local_date_time(&self, tz: &TimeZone) -> Result<SystemTime> { ... }
}

This library will be a subset of chrono and chrono-tz.

The library will contain a tz database, that tz database will be a 1Mb symbol in the library, and that symbol should be removed by a linker if TimeZone::forId is never called.

And then eventually write an RFC proposing to import that library into std::time.

@gnzlbg
Copy link
Contributor

gnzlbg commented Sep 6, 2018

@stepancheg could you comment about how does it support the different calendars ? e.g. C++ <date> supports gregorian, julian, maya, etc. calendars. IIRC @quodlibetor we had a repo were we were going to review the different time date and tz support of the mainstream languages but I don't know what happened with that :/

@SimonSapin
Copy link
Contributor

I’d be very hesitant to ship the tz database in the Rust distribution itself. That database is typically updated several times a year and sometimes on very short notice, so tying it to compiler updates doesn’t sound good. (We’re very careful about making compiler updates as painless as possible, but they can still be non-trivial at times. And even a project tracking Stable very closely gets changes 6~12 weeks after they land.)

What if TimeZone was a trait? With struct Utc; its only impl in the standard library, and a tz crate on crates.io.


  fn local() -> TimeZone { ... }

I don’t think there should be a no-argument inherent constructor for trying to guess the "local" timezone. (Local to who? Many applications are networked.) Instead the location of this API should more clearly indicate that it is not "pure" computation but might do things like a system call or looking up an environment variable, and behave differently on different machines/environments. In the standard library, this is std::env.

@gnzlbg
Copy link
Contributor

gnzlbg commented Sep 6, 2018

I’d be very hesitant to ship the tz database in the Rust distribution itself.

Does any library do this? C++'s <tz> library explicitly does not for the same reasons you mention, and I would be strongly against of including it. What <tz> does is provide functionality to parse, query, etc. the IANA database leaving it up to users how to obtain it.

@SimonSapin
Copy link
Contributor

What <tz> does is provide functionality to parse, query, etc. the IANA database leaving it up to users how to obtain it.

Ok that sounds more reasonable. But even so, I feel that timezone handling is complex enough that I’d rather not include it at least in the initial set of calendaring APIs we add to the standard library.

@gnzlbg
Copy link
Contributor

gnzlbg commented Sep 6, 2018

But even so, I feel that timezone handling is complex enough that I’d rather not include it at least in the initial set of calendaring APIs we add to the standard library.

Agreed, I actually would be more comfortable with not including anything in std at all beyond just exposing the system raw time APIs which std already does and to just recommend people to use chrono here :/

@SimonSapin
Copy link
Contributor

just exposing the system raw time APIs which std already does and to just recommend people to use chrono here

So, the status quo without this PR?

@gnzlbg
Copy link
Contributor

gnzlbg commented Sep 6, 2018

@SimonSapin pretty much, we tried to kickstart a chrono working group to at some point push chrono to 1.0 but we didn't manage to get enough manpower. The plan was to:

  • review widely used and peer-reviewed-ly designed time libraries from other languages from the point-of-view of how we would do that in Rust (Java, C#, C++, Swift, Kotlin, Dart, ...)
  • split chrono into chrono-time (no dates, no tzs) + rest, and come up with a time only library that stands on its own
  • split chrono into chrono-date that builds on top of chrono-time, and chrono-tz that builds on top of chrono date, and come up with a chrono-date library without time-zones that stands on its own (supports different calendars, provides access to low-level calendar algorithms, etc,)
  • finally shape a chrono-tz library that builds on the other two

and do all of this while simultaneously still re-exporting everything through the chrono crate to hopefully minimizing breakage.

In my opinion, the main problem that creating a good time-date-tz library is a huge amount of work that is hard to prioritize because chrono is actually already "good enough".

There are also potentially upcoming language features that would allow for potentially better designs of even chrono-time, e.g., if we had compile-time rational numbers like C++'s ratio, we might be able to build a better chrono-time library, so chrono-date and chrono-tz might end up being better too. These unknowns do not help.

All of these "unknowns" make me uncomfortable with trying to make the std time utilities do more than they do now. I'm even unsure if this change makes sense from a structural point-of-view. For example, if I had a crate that only wants to depend on chrono-time, I would expect that crate to print SystemTime without any tz information, and that if I wanted to print it with tz information I would have to depend on chrono-tz instead, so that only those who actually need tz information would pay for the compile-time, code-size, etc. of that functionality (at least this is how it works in C++ with <chrono>, <date>, and <tz>).

@quodlibetor
Copy link
Contributor

Yeah, I agree with gnzlbg, their description of chrono is still the plan, and they accurately describe my concerns with the idea of trying to make a minimal calendar API in the standard library. I'm slightly less concerned about adding a UTC display method to SystemTime, but that might just be because I want it all the time. Splitting chrono up so that there are tiny crates for these things should get us to a place where the current situation is less of a concern.

The issue is entirely one of working hours at this point -- the experience of Java and C++ over the last several decades mean that we have actual experience to go off of, and so it'd be somewhat reasonable to expect a rust library to get it right... given that there are humans able to put in the time. I do expect to actually have a couple months worth of free weekends for the first time in basically a year, during which one of my highest priorities will be trying to get more contributors to the chrono codebase so that I'm no longer the bottleneck.

@stepancheg
Copy link
Contributor Author

I'm closing this PR for now. Thanks for the feedback!

@stepancheg stepancheg closed this Sep 7, 2018
@rfcbot rfcbot removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-postpone This issue / PR is in PFCP or FCP with a disposition to postpone it. labels Sep 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.