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

Support for relative/pretty date formatting #79

Closed
xiajinchun opened this issue Nov 20, 2015 · 22 comments
Closed

Support for relative/pretty date formatting #79

xiajinchun opened this issue Nov 20, 2015 · 22 comments
Assignees

Comments

@xiajinchun
Copy link

Is there the way to custom time unit when call the function like toRelativeString? , and then return the relative representation with custom time unit.
For example if the time unit is day, a relative representation of the date by comparing 2015-11-15 00:00 to the 2015-11-16 02:00 should be returned 1 day, and if the time unit is hour, should be returned 26 hours.

@Hout Hout added the question label Dec 7, 2015
@Hout
Copy link
Contributor

Hout commented Dec 13, 2015

This would be really nice and I think it is feasible. Needs further investigation.

@Hout Hout added enhancement and removed question labels Dec 13, 2015
@malcommac
Copy link
Owner

It could be a nice feature to replace the old relative formatting feature and make it more flexible.
We could add a mask of units (you can specify both days and hours for example and return values with that precision).
But, in fact, this option is very similar to func difference() already implemented in DateInRegion: then we can use it as base and print result in readable localize form.

@malcommac
Copy link
Owner

"Pretty" date formatting is an interesting feature to implement but I'm not sure the best way is to make some sort of resemble of difference() method we have already.
This method takes as argument comparison date, unit flag to compare and return an instance of datecomponents which allows you to return days,months (...) elapsed since the dates.

I would to take a look around to other frameworks+languages in order to get the most convenient way to implement it.

OPTION 1

[NSDate/DateInRegion].prettyDate(fromDate: NSDate/DateInRegion, style: PrettyDateStyle)

where PrettyDateStyle is an enum with the following types:

PrettyDateFormatWithTime

EXAMPLE:  if today is September 30, 2012 and the time is 15:30
Today:        12:58 PM
Tomorrow:     Tomorrow 15:30
Yesterday:    Yesterday 15:30
2 days ago:   Friday 15:30
1 week ago:   09/23/12 15:30
1 week later: 10/07/12 15:30

PrettyDateFormatNoTime

EXAMPLE:  if today is September 30, 2012 and the time is 15:30
Today:        Today
Tomorrow:     Tomorrow
Yesterday:    Yesterday
2 days ago:   Friday
1 week ago:   09/23/12
1 week later: 10/07/12

PrettyDateFormatTodayTimeOnly

EXAMPLE:  if today is September 30, 2012 and the time is 15:30
Today:        15:30
Tomorrow:     Tomorrow
Yesterday:    Yesterday
2 days ago:   Friday
1 week ago:   09/23/12
1 week later: 10/07/12

PrettyDateLongRelativeTime

EXAMPLES:
Now
15 minutes ago
59 minutes ago
1 hour ago
2 hours ago
Yesterday
30 days ago
90 days ago

PrettyDateShortRelativeTime

EXAMPLES:
Now
15m
59m
1h
23h
1d
30d
90d

@malcommac malcommac changed the title Relative representation with custom time unit like 'toRelativeString' Support for relative/pretty date formatting Dec 21, 2015
@Hout
Copy link
Contributor

Hout commented Dec 21, 2015

I presume the last two lines in PrettyDateShortRelativeTime should be prefixed with a minus?

@Hout
Copy link
Contributor

Hout commented Dec 21, 2015

Instead of having all kinds of different names, I prefer one general function with parameters for the units to format and the 'shortness' of the output string

@malcommac
Copy link
Owner

So something like the NSCalendarUnit (one or more to get granularity of the statement) and another param to get the short/long/medium style?

@malcommac
Copy link
Owner

However in order to get a reasonable behavior we should provide an easy to use formatting; while it great to have some sort of granularity of the interval (you would print 24 hours or 1 day) the most common way to format is 1 day. Customization is great but is also good to keep it simple.
So we could make a function which takes a list for time granularity (.Day,.Hour etc) and a style (short/normal/full) with default granularity set as auto (auto uses the highest time calculated granularity: 25 hours uses Day granularity and print 1 day; however if you specify [Day,Hour] it print 1 day and 1 hour; if you specify [Hour] you get 25 hours and if you specify [Year] you get nil.

@Hout
Copy link
Contributor

Hout commented Dec 22, 2015

To keep it simple we could also do some fuzzy parametering e.g.

units: NSCalendarUnit = nil // = auto by default
numberOfUnits: Int = 1
style: OurFantasticPrettyFormattingStyle = .Medium

Thus we can issue for 25 hours:

dateInterval.prettyFormat() // auto, 1 unit, : day (is largest), medium style --> "1dy"
dateInterval.prettyFormat(units: [.hour]) // manual, hour units only, medium style --> "25hr"
dateInterval.prettyFormat(units: [.day, .hour]) // manual, day and hour units, medium style --> "1dy 1hr"
dateInterval.prettyFormat(numberOfUnits: 2) // auto, 2 units (day and hour units), medium style --> "1dy 1hr"
dateInterval.prettyFormat(style: .Long) // auto, 1 unit, : day (is largest), long style --> "1 day"

@malcommac malcommac self-assigned this Dec 23, 2015
@malcommac
Copy link
Owner

Okay this is my proposal.
We can have a 'colloquial' style which can print dates in 4 styles (short,medium,long,full).
These are the conditions:

COLLOQUIAL STYLE

CONDITION SHORT NORMAL LONG FULL
x < 60m [x]m [x] mins [x] mins ago [x] minutes ago
x < 24h [x]h [x] hrs [x] hrs ago [x] hours ago
x < 48h yda y'day yesterday yesterday 15:30
x < 7days [x]d [x] days [x] days ago [x] days ago 15:30
x < 4weeks [x]w [x] weeks [x] weeks ago [x] weeks ago 15:30
x < 12 months [x]m [x] months 4/17 April 17
other [x]ys [x] years 2015/4/17 Friday, April 17 2015, 15:30

We also have another style (name???) which works by using parameters we have specified above:

  • units (NSCalendarUnit mask): the unit of time we want to use in order to compose the final string (an item is ignored when its value is 0).
  • style: SHORT,NORMAL,LONG,FULL (as above)
  • numberOfUnits: number of units to print when different from 0 (nil means 1 unit)

What do you think?

@Hout
Copy link
Contributor

Hout commented Dec 24, 2015

Looks good to me! One remark though: following the path from #123 NOT to put redundant parameters in a function I would suggest two functions:

// Takes the biggest unit of difference and the number of subsequent smaller units
func toRelativeString(fromDate: date, numberOfUnits: Int = 1, style: Y = .Medium)

// Takes the difference of the specified units
func toRelativeString(fromDate: date, units: NSCalendarUnits, style: Y = .Medium) 

I think more nuances are possible, but I suggest an agile approach: try fast and fail fast (if at all).

@malcommac
Copy link
Owner

I agree! However I don't understand params of these two functions:

  • A) "colloquial" output takes a reference date and the style.
  • B) "...." output takes reference date, units, number of units and style

So something like:

// Colloquial output
func toRelativeString(fromDate: date, style: Y = .Medium)

// Standard output 
func toRelativeString(fromDate: date, units: NSCalendarUnits, numberOfUnits: Int = 1 style: Y = .Medium) 

Am I missing something?

@malcommac
Copy link
Owner

Just added .toNaturalString() method to generate a colloquial based representation of the difference between two dates. Commit is 8f152ae.

This is the logical behind this method:

CONDITION SHORT S. DEFAULT S. LONG S. FULL S.
< 1 minute now 2 s 2 secs 2 seconds
< 1 hour 25m 25 mins 25 minutes 25 minutes, 3 secs
< 24 hours 4h 4 hrs 4 hours 4 hours (15:20)
< 48 hours yda yest yest. (15:20) yesterday (15:20)
< 7 days 6d 6 dys 6 days (15:20) 6 days ago (16:20)
< 4 weeks 3w 3 wks 3 weeks 3 weeks ago (Wed 17:20)
< 12 months 7m 7 mnths 7 months 7 months ago (March 15, 17:20)
> 1 year 2ys 2 years / 1 year 2 years / last year 2 years ago (April 15, 2016)

@malcommac
Copy link
Owner

I've also added .toComponentsStrings() method.
The signature is:

func toComponentsStrings(refDate :DateInRegion = DateInRegion(), units :[NSCalendarUnit]? = nil, maxUnits cUnits :Int = 0, style :DateFormatterStyle = .Default) -> [String]?```

This method get differences in terms of specified units from a refDate (or new standard DateInRegion() if not specified) and return a readable string for each components.
So, just for example, if two dates differ in term of 3 hours, 2 minutes, 5 seconds you will get a localized array with ["3hrs","2mins","5secs"] (style attribute specify the abbreviated form for each unit).
maxunits specify the max number of non null units to show in resulting array (0 means no limit).

Supported units are: Nanosecond, Second, Minute, Hour, Day, Month, Year.
Abbreviated forms are:

UNIT (SINGULAR/PLURAL FORM) SHORT DEFAULT LONG FULL
NANOSECOND n ns/ns ns nanosecond/nanoseconds
SECOND s s/s sec/secs second/seconds
MINUTE m m/ms min/mins minute/minutes
HOUR h h/hs hr/hrs hour/hours
DAY d d/ds dy/dys day/days
MONTH mn mn/mns mth/mths month/months
YEAR y y/ys yr/yrs year/years

@Hout
Copy link
Contributor

Hout commented Jan 13, 2016

Remarks:
Looking at this table, three types of abbreviations seems overkill.
Abbreviation for "month" is "mo", "mon".
Please ignore plurals with one letter abbreviations. It is rarely used, not recognised and it creates confusion (ms = millisecond).

@malcommac
Copy link
Owner

Okay; just a question: this is for English, are we sure we can remove safely singular form from types and make this assumption valid even for other languages? Otherwise we can simply make set the same value for both singular and plural and maintain the distinction inside the code.

@Hout
Copy link
Contributor

Hout commented Jan 13, 2016

Not sure, but still I think three type of abbreviations is overkill in English, Dutch, German, French and Indonesian. Not sure about Italian ;-)

@Hout
Copy link
Contributor

Hout commented Jan 13, 2016

BTW have you checked my suggestion in #65 with NSDateComponentFormatter? This seems to give you the names if the units automagically.

@malcommac
Copy link
Owner

Damn it seems the solution, I forgot that :|

@malcommac
Copy link
Owner

Have you looked at this?
https://github.com/algal/RelativeDatePlayground; NSDateComponentsFormatter does not handle correctly negative time intervals.

   SECONDS |  TTTTimeIntervalFormatter |  NSDateComponentsFormatter
-----------+---------------------------+---------------------------
  -1488010 |               2 weeks ago |          -1 week remaining
  -1468800 |               2 weeks ago |          -1 week remaining
   -864000 |                1 week ago |        0 seconds remaining
    -86400 |                 1 day ago |           -1 day remaining
    -36000 |              10 hours ago |        -10 hours remaining
     -3600 |                1 hour ago |          -1 hour remaining
      -600 |            10 minutes ago |      -10 minutes remaining
       -60 |              1 minute ago |        -1 minute remaining
       -10 |            10 seconds ago |      -10 seconds remaining
        -1 |              1 second ago |        -1 second remaining
        -0 |                  just now |        0 seconds remaining
         0 |                  just now |        0 seconds remaining
         1 |         1 second from now |         1 second remaining
        10 |       10 seconds from now |       10 seconds remaining
        60 |         1 minute from now |         1 minute remaining
       600 |       10 minutes from now |       10 minutes remaining
      3600 |           1 hour from now |           1 hour remaining
     36000 |         10 hours from now |         10 hours remaining
     86400 |            1 day from now |            1 day remaining
    864000 |           1 week from now |           1 week remaining
   1468800 |          2 weeks from now |          2 weeks remaining
   1488010 |          2 weeks from now |          2 weeks remaining

We can provide a port of TTTTimeIntervalFormatter right now and replace it with NSDateComponentsFormatter when it will be fixed. What do you think?

@Hout
Copy link
Contributor

Hout commented Jan 15, 2016

I am always in favour of using the native libraries. Can't we work around
the negative error by first making the time interval positive and creating
a string with ago?

Besides we could use this for more than just time intervals
Op do 14 jan. 2016 om 23:24 schreef Daniele Margutti <
[email protected]>

Have you looked at this?
https://github.com/algal/RelativeDatePlayground;
NSDateComponentsFormatter does not handle correctly negative time intervals.

SECONDS | TTTTimeIntervalFormatter | NSDateComponentsFormatter
-----------+---------------------------+---------------------------
-1488010 | 2 weeks ago | -1 week remaining
-1468800 | 2 weeks ago | -1 week remaining
-864000 | 1 week ago | 0 seconds remaining
-86400 | 1 day ago | -1 day remaining
-36000 | 10 hours ago | -10 hours remaining
-3600 | 1 hour ago | -1 hour remaining
-600 | 10 minutes ago | -10 minutes remaining
-60 | 1 minute ago | -1 minute remaining
-10 | 10 seconds ago | -10 seconds remaining
-1 | 1 second ago | -1 second remaining
-0 | just now | 0 seconds remaining
0 | just now | 0 seconds remaining
1 | 1 second from now | 1 second remaining
10 | 10 seconds from now | 10 seconds remaining
60 | 1 minute from now | 1 minute remaining
600 | 10 minutes from now | 10 minutes remaining
3600 | 1 hour from now | 1 hour remaining
36000 | 10 hours from now | 10 hours remaining
86400 | 1 day from now | 1 day remaining
864000 | 1 week from now | 1 week remaining
1468800 | 2 weeks from now | 2 weeks remaining
1488010 | 2 weeks from now | 2 weeks remaining

We can provide a port of TTTTimeIntervalFormatter right now and replace it
with NSDateComponentsFormatter when it will be fixed. What do you think?


Reply to this email directly or view it on GitHub
#79 (comment).

@malcommac
Copy link
Owner

Another referece:
http://www.openradar.appspot.com/17436956:

"The phrase "N minutes ago" does not actually specify the quantity of time, it specifies a moment in time described by an implicit reference to now and a quantity. Formatting moments in time, even if relative like this, would be more appropriate for NSDateFormatter. We can treat this as a request for improving relative date formatting on NSDateFormatter though, which would indeed be a great improvement."

[EDITED]
Okay I think NSDateComponentsFormatter is valid enough to be used without porting an external lib. We can define a set a postifx string to happend in various languages as you said.

@malcommac
Copy link
Owner

Added support for NSDateComponentsFormatter. It's a shared instance in each called thread and it's wrapped in FormatterStyle.
I've used it in toNaturalString(refDate:style:) method of DateInRegion/NSDate (to get a colloquial representation of the difference between two dates) and in toString(style:) of NSTimeInterval (to print time interval in a readable manner).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants