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

Improve #inspect format for Time types #5794

Merged
merged 3 commits into from
Apr 26, 2018

Conversation

straight-shoota
Copy link
Member

@straight-shoota straight-shoota commented Mar 9, 2018

I'm doing some housekeeping to normalize and improve the inspect (and to_s) output of time and location related types.

Time#inspect now includes nanoseconds unless it is 0. #to_s still omits them to avoid cluttering the output. When calling #inspect you typically expect a detailed output that allows to distinguish the difference between two instances. While nanoseconds are usually not relevant, it should still show up in case it is significant.

Time.now.inspect # => 2018-03-09 12:52:36.506736000 +01:00 Europe/Berlin
Time.now.to_s    # => 2018-03-09 12:52:36 +01:00 Europe/Berlin

Time::Zone now includes a #format method which is used to print the offset as +HH:mm:ss. This format is added to the #inspect output and also used by Time::Formatter#time_zone.

@RX14
Copy link
Contributor

RX14 commented Mar 9, 2018

I'd expect inspect to include nanoseconds even if nanoseconds was 0.

@straight-shoota
Copy link
Member Author

IMO it just adds unnecessary clutter. And nanoseconds == 0 is pretty common because it is usually not included when serializing time data.

But I see the point, that it adds uncertainty when a time instance is printed, because it can mean nanoseconds is just omitted (e.g. when calling to_s) or actually 0.

What do you think about printing only one decimal place (.0) if it is 0?

@RX14
Copy link
Contributor

RX14 commented Mar 9, 2018

@straight-shoota yes that's fine

@straight-shoota straight-shoota force-pushed the jm/fix/time-to_s branch 3 times, most recently from 83a1f92 to 762a467 Compare March 9, 2018 14:09
@straight-shoota
Copy link
Member Author

straight-shoota commented Mar 9, 2018

(this answers a deleted comment)

ISO 8601 or RFC 3339 only support fixed offsets. Time instances cannot be entirely expressed in those formats, because they are essentially an ISO 8601 date with an attached location which provides zone transition rules.

Two instances of Time having different locations but sharing the same current offset should be distinguishable. They behave differently for example when adding a time span. Thus location is significant for inspecting a Time instance.

If time zone rules don't matter, you should just use UTC and/or fixed offset locations if necessary. Such instances can be expressed as ISO 8601 or RFC 3339 and the location is omitted in #inspect.

@ysbaddaden
Copy link
Contributor

ysbaddaden commented Mar 12, 2018

My review got lost, but in essence:

  • #inspect printing a dangling .0 is useless noise;
  • #to_s has no reason to skip nanoseconds (unless zero);
  • #to_s should have a location argument that defaults to Location::Local, allowing useful constructs such as Time.with(user.location) { puts comment.created_at.to_s }; bonus: we don't have to print the location (it's localtime or the specified one).

Actually, I believe all Time instances should be shifted to localtime —I'm probably wrong, but I never got a use-case where keeping the original location was essential or even useful. The fact that international standards (ISO 8601) don't specify locations is a huge hint to me that what's important is the offset (structural deviation from UTC), not the location (context specific representational concept).

@straight-shoota
Copy link
Member Author

I now concur with @RX14 that nanoseconds should better be explicit. It's obviously very unlikely that this gets confused with a custom display of a non zero-nanosecond time (for example `time.to_s("%F %T %Z #{time.location}")), but I think we should better be safe than sorry. IMHO the additional noise is acceptable.

I'm not sure about to_s... could be with or without nanoseconds, I don't have a clear position if it should.

I like the idea of specifying a location for formatting, but I would just add it as an argument to #to_s:
comment.created_at.to_s(user.location). This would format the time in the given location without printing the actual location. It could probably even omit the offset because it is clearly a localized format.
It's essentially the same as comment.created_at.in(user.location).to_s but with a modified default format.

What do you mean with "all Time instances should be shifted to localtime"? Time#location should always be Location.local??

The location is usually not that important, that's true. But it depends on what you're doing with your Time instances. If it's just recording timed events and printing that in a localized format, then yes, you don't need the Time instance to have any location. It should just be UTC and it is only localized when printed.
But if you're doing calendar operations such as adding time spans, it is essential to have the Time instance in a specific location.

@straight-shoota
Copy link
Member Author

straight-shoota commented Mar 12, 2018

After thinking about this, I'm pretty sure to_s should not print nanoseconds by default. Many Time instances will have a non-zero nanoseconds because they were created as Time.now but for the great majority of format use cases sub-second precision is completely irrelevant. Let's keep the default format short and precise for ~90% of use cases. It is always possible to specify a custom format for to_s.

Following this reasoning, I would rather consider removing the location from the default format than adding nanoseconds.

@RX14
Copy link
Contributor

RX14 commented Mar 13, 2018

nah, we should keep every detail including nanoseconds and zone on inspect and have to_s just be date time +-offset without ns.

@straight-shoota
Copy link
Member Author

straight-shoota commented Mar 19, 2018

@RX14's suggestion seems reasonable, to_s gives the typically needed information and inspect shows everything. I've implemented it that way.

@RX14
Copy link
Contributor

RX14 commented Mar 19, 2018

needs rebase for CI fix

@straight-shoota
Copy link
Member Author

Rebased

Copy link
Contributor

@RX14 RX14 left a comment

Choose a reason for hiding this comment

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

Docs seem to be out of date.

src/time.cr Outdated
to_s "%F %T %:z", io
# Prints this `Time` to *io*.
#
# The local date-time is formatted as ISO 8601 date string `YYYY-MM-DD HH:mm:ss.nnnnnnnnn +ZZ:ZZ:ZZ`.
Copy link
Contributor

Choose a reason for hiding this comment

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

that string isn't iso 8601 and the example doesn't include the zone name.

Copy link
Member Author

Choose a reason for hiding this comment

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

Why isn't it ISO 8601?

Copy link
Member Author

Choose a reason for hiding this comment

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

Zone name is mentioned in the next paragraph.

Copy link
Contributor

@RX14 RX14 Mar 26, 2018

Choose a reason for hiding this comment

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

no T, and there's a space between the nanoseconds and the +, and iso 8601 only permits hh:mm offset

Copy link
Member Author

Choose a reason for hiding this comment

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

T designator can be omitted, but the additional white space is true. The question is: should it be removed?

Copy link
Contributor

@RX14 RX14 Mar 26, 2018

Choose a reason for hiding this comment

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

no, T can't be omitted in practice. We can talk about the standard details all we want but in practice iso 8601 means rfc3339. We should leave the format as-is but not advertise to_s as conforming to any spec.

src/time.cr Outdated
io
end

# Prints this `Time` to *io*.
#
# The local date-time is formatted as ISO 8601 date string `YYYY-MM-DD HH:mm:ss +ZZ:ZZ:ZZ`.
Copy link
Contributor

Choose a reason for hiding this comment

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

also not iso 8601.

src/time.cr Outdated
# The local date-time is formatted as ISO 8601 date string `YYYY-MM-DD HH:mm:ss +ZZ:ZZ:ZZ`.
# Nanoseconds are always omitted. Offset seconds are omitted if `0`.
#
# The name of the location is appended unless it is a fixed zone offset.
Copy link
Contributor

Choose a reason for hiding this comment

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

It shouldn't be.

@straight-shoota
Copy link
Member Author

GTG?

`#inspect` now prints `nanoseconds` unless it is `0` and uses `Time::Zone#format`.
Location name is no longer omitted if `"Local"`, otherwise there would be no distinction between a fixed offset time zone and local time zone because both would just show the offset and no name.
`#to_s` still omits nanoseconds and time zone name.
@straight-shoota
Copy link
Member Author

Merge and milestone?

@RX14 RX14 added this to the Next milestone Apr 26, 2018
@RX14 RX14 merged commit 3afc781 into crystal-lang:master Apr 26, 2018
@straight-shoota straight-shoota deleted the jm/fix/time-to_s branch April 26, 2018 13:21
chris-huxtable pushed a commit to chris-huxtable/crystal that referenced this pull request Jun 6, 2018
* Improve #inspect format for Time::Location types

* Add Time::Zone#format and use it in Time::Formatter

* Improve format of Time#inspect

`#inspect` now prints `nanoseconds` unless it is `0` and uses `Time::Zone#format`.
Location name is no longer omitted if `"Local"`, otherwise there would be no distinction between a fixed offset time zone and local time zone because both would just show the offset and no name.
`#to_s` still omits nanoseconds and time zone name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants