humane 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 433a88d823e187b4481fb3c2632ba816b487b3b8047f272b5c4b17a930b6227b
4
- data.tar.gz: 5c90d3a7ee778274382e8ca25d604d39e672333ddfb8985941b97e7ce345f335
3
+ metadata.gz: 94b64259f8b2dc0d78f3f6feb6b48b4d908f23bfa74f133d901c57f5232294bd
4
+ data.tar.gz: 65e61cee90061fb0eba0e08053c2d24104d7e7d8bc711b8789661a5b27dd9f52
5
5
  SHA512:
6
- metadata.gz: 42ceb33a1bd570aa85b8cc5a9417204fcd9d6041e79e6e44c3b7a65469f9139c2897019afef22aab9269bd3956599c2e1ed430021a64f399a2fc6fac0e996c30
7
- data.tar.gz: fc9eaa4b043bf6547444631fde9fa2bba69c493ecc07d5e43375ac65f6a20c9f607c73776c7f3e535080a4d2c682747cb8bff8e017e5ddfe67a0f79e3869a4ae
6
+ metadata.gz: a3320656c10207ff6e941325d26085bc16acb4a56807cf4daafd2bb7560396cd36a37afcc77b270114b330d83ae9e0a7b8b394444958b0a7f96207a775c9492a
7
+ data.tar.gz: 84d09dac947371900807718502d17055a914b91e58833a10fc2868c0179794fbf8ba80a42d14b359aef4fa4c32c2dbdcaa9265d737195f90c7978bc381a2dce2
data/README.md CHANGED
@@ -33,6 +33,11 @@ let formatter = RelativeDateTimeFormatter(); formatter.unitsStyle = .full
33
33
  formatter.localizedString(for: time, relativeTo: now) // "3 minutes ago"
34
34
  ```
35
35
 
36
+ If you're writing Swift directly rather than calling Foundation by hand,
37
+ [`humane-swift`](https://github.com/woodie/humane-swift) wraps these same two
38
+ formatters with the identical API shape -- including the
39
+ `includeSeconds`/`approximate` options below.
40
+
36
41
  ## Install
37
42
 
38
43
  ```
@@ -45,6 +50,26 @@ or in a `Gemfile`:
45
50
  gem "humane"
46
51
  ```
47
52
 
53
+ ## Beyond Foundation's defaults
54
+
55
+ Two options on `Humane::TimeFormatter`, both off by default so it matches
56
+ `RelativeDateTimeFormatter` exactly out of the box:
57
+
58
+ - `include_seconds` (default `false`): under 30 seconds, collapses to "less than a
59
+ minute ago"/"in less than a minute" instead of an exact second count. Named after
60
+ ActionView's `include_seconds`, which defaults the same way.
61
+ - `approximate` (default `false`): prefixes "about"/"in about" on the hour-scale
62
+ buckets (1 hour, and 2..24 hours), the way ActionView's `distance_of_time_in_words`
63
+ does for those same buckets -- for a render that can't refresh itself and shouldn't
64
+ overstate its own precision. Matches ActionView's own table exactly (down to its
65
+ 44:30/89:30 rounding cutoffs), through the "1 day" bucket; week/month/year buckets
66
+ are out of scope. See [issue #1](https://github.com/woodie/humane-ruby/issues/1).
67
+
68
+ ```ruby
69
+ Humane::TimeFormatter.new(approximate: true).string(at: t - 15 * 3600, relative_to: t)
70
+ # => "about 15 hours ago"
71
+ ```
72
+
48
73
  ## Scope
49
74
 
50
75
  Finder's `.file` byte-count style, and a numeric (non-calendar-aware)
@@ -4,8 +4,10 @@ module Humane
4
4
  # Formats one time relative to another the way Finder-adjacent tools do.
5
5
  class TimeFormatter
6
6
  # include_seconds shows exact seconds under a minute instead of collapsing to "less than a minute ago/in less than a minute". Defaults to false, matching ActionView's include_seconds.
7
- def initialize(include_seconds: false)
7
+ # approximate prefixes "about"/"in about" on the hour-scale buckets (1 hour, and 2..24 hours), matching ActionView's distance_of_time_in_words wording for those buckets. Defaults to false. See docs/COMMENTS.md and humane-ruby issue #1 for the full bucket table this ports.
8
+ def initialize(include_seconds: false, approximate: false)
8
9
  @include_seconds = include_seconds
10
+ @approximate = approximate
9
11
  end
10
12
 
11
13
  # Returns the time at `at` relative to `relative_to` as a human-readable string.
@@ -14,26 +16,37 @@ module Humane
14
16
  future = seconds.negative?
15
17
  seconds = seconds.abs
16
18
 
17
- if !@include_seconds && seconds < 60
19
+ if !@include_seconds && seconds < 30
18
20
  return future ? "in less than a minute" : "less than a minute ago"
19
21
  end
20
22
 
21
- text =
22
- if seconds < 60
23
- pluralize(seconds.to_i, "second")
24
- elsif seconds < 3600
25
- pluralize((seconds / 60.0).round, "minute")
26
- elsif seconds < 86_400
27
- pluralize((seconds / 3600.0).round, "hour")
28
- else
29
- pluralize((seconds / 86_400.0).round, "day")
23
+ if @include_seconds && seconds < 60
24
+ return wrap(pluralize(seconds.to_i, "second"), future: future)
25
+ end
26
+
27
+ # Buckets come from distance_in_minutes, not raw seconds re-divided per unit -- see docs/COMMENTS.md.
28
+ distance_in_minutes = (seconds / 60.0).round
29
+
30
+ text, approximable =
31
+ case distance_in_minutes
32
+ when 1 then ["1 minute", false]
33
+ when 2..44 then [pluralize(distance_in_minutes, "minute"), false]
34
+ when 45..89 then ["1 hour", true]
35
+ when 90..1439 then [pluralize((distance_in_minutes / 60.0).round, "hour"), true]
36
+ when 1440..2519 then ["1 day", false]
37
+ else [pluralize((distance_in_minutes / 1440.0).round, "day"), false]
30
38
  end
31
39
 
32
- future ? "in #{text}" : "#{text} ago"
40
+ text = "about #{text}" if @approximate && approximable
41
+ wrap(text, future: future)
33
42
  end
34
43
 
35
44
  private
36
45
 
46
+ def wrap(text, future:)
47
+ future ? "in #{text}" : "#{text} ago"
48
+ end
49
+
37
50
  def pluralize(count, unit)
38
51
  count == 1 ? "1 #{unit}" : "#{count} #{unit}s"
39
52
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Humane
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: humane
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Woodell