local_time 0.3.0 → 1.0.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
  SHA1:
3
- metadata.gz: 84f0f9773b1e9bfac34c2e579af56d6c784a8d23
4
- data.tar.gz: a1606c79f356e6e09b67f5b2adca382e38366a9f
3
+ metadata.gz: 0d42dfef630d09374cd897d5a842566191ff07cd
4
+ data.tar.gz: 671f048d37ba09ade8df1e01dd8c4bb36f5ccfab
5
5
  SHA512:
6
- metadata.gz: 88db2bd3c1a77268ef0e8a59a18c81bed59c9f3b837bd3f9d2e555862228ded216dc7a2cf8e2d19a1c06fc262ada3ca319e4b2538eeae3ea826c3ecbe65c113f
7
- data.tar.gz: 71e7a980c10839bc5e81dda212a9a99f1dd9a5ec68cbd3a082e754126de0dfccad334e3efc28bb01a1c416b71c31bc7bdece4a9c848a2de60e7d771513de9121
6
+ metadata.gz: 6209af737d470ff029db6233ca5f30e6840e8e74c40db30aca1a8ad9a592dbd705ea2159ff16eb37118946dee8d6f6db1fbfd69d6f129d30b9aab1c3f3a93b18
7
+ data.tar.gz: 26786c11ec81af062927263f55e5f4103532dbdc871d535e0e272a5c95b8a94f6e8979d165154330c3bc2cc0b93ac202f4202546f04258012e75b82d4c1d8ce1
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2014 Javan Makhmali
1
+ Copyright 2014 Javan Makhmali, Basecamp
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,11 +1,16 @@
1
- Local Time is a Rails engine with helpers and JavaScript for displaying times and dates to users in their local time. The helpers render a `<time>` element in UTC and the JavaScript swoops in to convert and format. Because the `<time>` element is only rendered in one timezone, it is ideal for caching.
1
+ Local Time is a Rails engine with helpers and JavaScript for displaying times and dates to users in their local time. The helpers render a `<time>` element and the JavaScript swoops in to convert and format. The helper output is ideal for caching since it's always in UTC time.
2
2
 
3
3
  ---
4
4
 
5
5
  ####Example
6
6
 
7
+ ```ruby
8
+ > comment.created_at
9
+ "Wed, 27 Nov 2013 18:43:22 EST -0500"
10
+ ```
11
+
7
12
  ```erb
8
- <%= local_time(comment.created_at) # comment.created_at => Wed, 27 Nov 2013 18:43:22 EST -0500 %>
13
+ <%= local_time(comment.created_at) %>
9
14
  ```
10
15
 
11
16
  Renders:
@@ -22,6 +27,7 @@ When the DOM loads, the content is immediately replaced with a local, formatted
22
27
  <time data-format="%B %e, %Y %l:%M%P"
23
28
  data-local="time"
24
29
  datetime="2013-11-27T23:43:22Z"
30
+ title="November 27, 2013 6:43pm EDT"
25
31
  data-localized="true">November 27, 2013 6:43pm</time>
26
32
  ```
27
33
 
@@ -30,21 +36,36 @@ When the DOM loads, the content is immediately replaced with a local, formatted
30
36
  #### Time and date helpers
31
37
 
32
38
  ```erb
33
- Pass a time and an optional strftime format (default format shown here)
34
- <%= local_time(time, format: '%B %e, %Y %l:%M%P') %>
39
+ <%= local_time(time) %>
40
+ ```
41
+
42
+ Format with a strftime string (default format shown here)
43
+
44
+ ```erb
45
+ <%= local_time(time, '%B %e, %Y %l:%M%P') %>
46
+ ```
47
+
48
+ Alias for `local_time` with a month-formatted default
49
+
50
+ ```erb
51
+ <%= local_date(time, '%B %e, %Y') %>
52
+ ```
53
+
54
+ To set attributes on the time tag, pass a hash as the second argument with a `:format` key and your attributes.
35
55
 
36
- Alias for local_time with a month-formatted default
37
- <%= local_date(time, format: '%B %e, %Y') %>
56
+ ```erb
57
+ <%= local_time(time, format: '%B %e, %Y %l:%M%P', class: 'my-time') %>
38
58
  ```
39
59
 
40
60
  To use a strftime format already defined in your app, pass a symbol as the format.
61
+
41
62
  ```erb
42
- <%= local_time(date, format: :long) %>
63
+ <%= local_time(date, :long) %>
43
64
  ```
44
65
 
45
66
  `I18n.t("time.formats.#{format}")`, `I18n.t("date.formats.#{format}")`, `Time::DATE_FORMATS[format]`, and `Date::DATE_FORMATS[format]` will be scanned (in that order) for your format.
46
67
 
47
- Note: The included strftime JavaScript implementation is not 100% complete. It supports the following directives: `%a %A %b %B %c %d %e %H %I %l %m %M %p %P %S %w %y %Y`
68
+ Note: The included strftime JavaScript implementation is not 100% complete. It supports the following directives: `%a %A %b %B %c %d %e %H %I %l %m %M %p %P %S %w %y %Y %Z`
48
69
 
49
70
  #### Time ago helper
50
71
 
@@ -52,7 +73,9 @@ Note: The included strftime JavaScript implementation is not 100% complete. It s
52
73
  <%= local_time_ago(time) %>
53
74
  ```
54
75
 
55
- Displays the relative amount of time passed. With age, the descriptions transition from specific quantities to general dates. The `<time>` elements are updated every 60 seconds. Examples (in quotes):
76
+ Displays the relative amount of time passed. With age, the descriptions transition from {quantity of seconds, minutes, or hours} to {date + time} to {date}. The `<time>` elements are updated every 60 seconds.
77
+
78
+ Examples (in quotes):
56
79
 
57
80
  * Recent: "a second ago", "32 seconds ago", "an hour ago", "14 hours ago"
58
81
  * Yesterday: "yesterday at 5:22pm"
@@ -60,6 +83,22 @@ Displays the relative amount of time passed. With age, the descriptions transiti
60
83
  * This year: "on Nov 17"
61
84
  * Last year: "on Jan 31, 2012"
62
85
 
86
+ #### Relative time helper
87
+
88
+ Preset time and date formats that vary with age. The available types are `date`, `time-ago`, `time-or-date`, and `weekday`. Like the `local_time` helper, `:type` can be passed a string or in an options hash.
89
+
90
+ ```erb
91
+ <%= local_relative_time(time, 'weekday') %>
92
+ <%= local_relative_time(time, type: 'time-or-date') %>
93
+ ```
94
+
95
+ **Available `:type` options**
96
+
97
+ * `date` Inlcudes the year unless it's current. "Apr 11" or "Apr 11, 2013"
98
+ * `time-ago` See above. `local_time_ago` calls `local_relative_time` with this `:type` option.
99
+ * `time-or-date` Displays the time if it's todday. Displays time otherwise. "3:26pm" or "Apr 11"
100
+ * `weekday` Displays "Today", "Yesterday", or the weekday (e.g. Wednesday) if the time is within a week of today.
101
+
63
102
  #### Installation
64
103
 
65
104
  1. Add `gem 'local_time'` to your Gemfile.
@@ -72,18 +111,29 @@ The included JavaScript does not depend on any frameworks or libraries, and list
72
111
 
73
112
  #### JavaScript API
74
113
 
75
- `strftime` and `relativeTimeAgo` are available via the global `LocalTime` object.
114
+ `relativeDate`, `relativeTimeAgo`, `relativeTimeOrDate`, `relativeWeekday`, `run`, and `strftime` methods are available on the global `LocalTime` object.
76
115
 
77
116
  ```js
78
- > LocalTime.strftime(new Date, "%B %e, %Y %l:%M%P")
79
- "February 9, 2014 12:55pm"
80
-
81
117
  > LocalTime.relativeTimeAgo(new Date(new Date - 60 * 1000 * 5))
82
118
  "5 minutes ago"
119
+
120
+ // Process <time> tags. Equivalent to dispatching a "time:elapse" Event.
121
+ > LocalTime.run()
122
+
123
+ > LocalTime.strftime(new Date, "%B %e, %Y %l:%M%P")
124
+ "February 9, 2014 12:55pm"
83
125
  ```
84
126
 
85
127
  #### Version History
86
128
 
129
+ **1.0.0** (April 12, 2014)
130
+
131
+ * Added `local_relative_time` helper with several built in types
132
+ * Allow `:format` (and `:type`) option as a bare string or value in hash
133
+ * Added `relativeDate`, `relativeTimeOrDate`, `relativeWeekday` and `run` to the API
134
+ * Dropped ineffective `popstate` event listener
135
+ * Now in use at [Basecamp](https://basecamp.com/)
136
+
87
137
  **0.3.0** (February 9, 2014)
88
138
 
89
139
  * Allow :format option lookup in I18n or DATE_FORMATS hashes [Paul Dobbins]
@@ -30,7 +30,7 @@ strftime = (time, formatString) ->
30
30
  minute = time.getMinutes()
31
31
  second = time.getSeconds()
32
32
 
33
- formatString.replace /%([%aAbBcdeHIlmMpPSwyY])/g, ([match, modifier]) ->
33
+ formatString.replace /%([%aAbBcdeHIlmMpPSwyYZ])/g, ([match, modifier]) ->
34
34
  switch modifier
35
35
  when '%' then '%'
36
36
  when 'a' then weekdays[day].slice 0, 3
@@ -51,6 +51,7 @@ strftime = (time, formatString) ->
51
51
  when 'w' then day
52
52
  when 'y' then pad year % 100
53
53
  when 'Y' then year
54
+ when 'Z' then time.toString().match(/\((\w+)\)$/)?[1] ? ''
54
55
 
55
56
 
56
57
  class CalendarDate
@@ -67,6 +68,16 @@ class CalendarDate
67
68
  @year = @date.getUTCFullYear()
68
69
  @month = @date.getUTCMonth() + 1
69
70
  @day = @date.getUTCDate()
71
+ @value = @date.getTime()
72
+
73
+ equals: (calendarDate) ->
74
+ calendarDate?.value is @value
75
+
76
+ is: (calendarDate) ->
77
+ @equals calendarDate
78
+
79
+ isToday: ->
80
+ @is @constructor.today()
70
81
 
71
82
  occursOnSameYearAs: (date) ->
72
83
  @year is date?.year
@@ -82,7 +93,7 @@ class CalendarDate
82
93
  @constructor.today().daysSince @
83
94
 
84
95
 
85
- class RelativeTimeAgo
96
+ class RelativeTime
86
97
  constructor: (@date) ->
87
98
  @calendarDate = CalendarDate.fromDate @date
88
99
 
@@ -100,6 +111,12 @@ class RelativeTimeAgo
100
111
  else
101
112
  "on #{@formatDate()}"
102
113
 
114
+ toTimeOrDateString: ->
115
+ if @calendarDate.isToday()
116
+ @formatTime()
117
+ else
118
+ @formatDate()
119
+
103
120
  timeElapsed: ->
104
121
  ms = new Date().getTime() - @date.getTime()
105
122
  sec = Math.round ms / 1000
@@ -143,8 +160,19 @@ class RelativeTimeAgo
143
160
  formatTime: ->
144
161
  strftime @date, '%l:%M%P'
145
162
 
163
+ relativeDate = (date) ->
164
+ new RelativeTime(date).formatDate()
165
+
146
166
  relativeTimeAgo = (date) ->
147
- new RelativeTimeAgo(date).toString()
167
+ new RelativeTime(date).toString()
168
+
169
+ relativeTimeOrDate = (date) ->
170
+ new RelativeTime(date).toTimeOrDateString()
171
+
172
+ relativeWeekday = (date) ->
173
+ if day = new RelativeTime(date).relativeWeekday()
174
+ day.charAt(0).toUpperCase() + day.substring(1)
175
+
148
176
 
149
177
  domLoaded = false
150
178
 
@@ -156,10 +184,6 @@ update = (callback) ->
156
184
  if Turbolinks?.supported
157
185
  document.addEventListener "page:update", callback
158
186
  else
159
- setTimeout ->
160
- window.addEventListener "popstate", callback
161
- , 1
162
-
163
187
  jQuery?(document).on "ajaxSuccess", (event, xhr) ->
164
188
  callback() if jQuery.trim xhr.responseText
165
189
 
@@ -180,19 +204,30 @@ document.addEventListener "DOMContentLoaded", ->
180
204
  time = new Date Date.parse datetime
181
205
  return if isNaN time
182
206
 
207
+ unless element.hasAttribute("title")
208
+ element.setAttribute "title", strftime(time, "%B %e, %Y at %l:%M%P %Z")
209
+
183
210
  element[textProperty] =
184
211
  switch local
212
+ when "date"
213
+ element.setAttribute "data-localized", true
214
+ relativeDate time
185
215
  when "time"
186
216
  element.setAttribute "data-localized", true
187
217
  strftime time, format
188
218
  when "time-ago"
189
219
  relativeTimeAgo time
220
+ when "time-or-date"
221
+ relativeTimeOrDate time
222
+ when "weekday"
223
+ relativeWeekday(time) ? ""
224
+
225
+ run = ->
226
+ event = document.createEvent "Events"
227
+ event.initEvent "time:elapse", true, true
228
+ document.dispatchEvent event
190
229
 
191
- setInterval ->
192
- event = document.createEvent "Events"
193
- event.initEvent "time:elapse", true, true
194
- document.dispatchEvent event
195
- , 60 * 1000
230
+ setInterval run, 60 * 1000
196
231
 
197
232
  # Public API
198
- @LocalTime = {strftime, relativeTimeAgo}
233
+ @LocalTime = {relativeDate, relativeTimeAgo, relativeTimeOrDate, relativeWeekday, run, strftime}
@@ -1,9 +1,11 @@
1
1
  module LocalTimeHelper
2
2
  DEFAULT_FORMAT = '%B %e, %Y %l:%M%P'
3
3
 
4
- def local_time(time, options = {})
5
- time = utc_time(time)
6
- format = time_format(options.delete(:format))
4
+ def local_time(time, options = nil)
5
+ time = utc_time(time)
6
+
7
+ options, format = extract_options_and_value(options, :format)
8
+ format = find_time_format(format)
7
9
 
8
10
  options[:data] ||= {}
9
11
  options[:data].merge! local: :time, format: format
@@ -11,20 +13,28 @@ module LocalTimeHelper
11
13
  time_tag time, time.strftime(format), options
12
14
  end
13
15
 
14
- def local_date(time, options = {})
15
- options.reverse_merge! format: '%B %e, %Y'
16
+ def local_date(time, options = nil)
17
+ options, format = extract_options_and_value(options, :format)
18
+ options[:format] = format || '%B %e, %Y'
16
19
  local_time time, options
17
20
  end
18
21
 
19
- def local_time_ago(time, options = {})
22
+ def local_relative_time(time, options = nil)
20
23
  time = utc_time(time)
24
+ options, type = extract_options_and_value(options, :type)
21
25
 
22
26
  options[:data] ||= {}
23
- options[:data].merge! local: 'time-ago'
27
+ options[:data].merge! local: type
24
28
 
25
29
  time_tag time, time.strftime(DEFAULT_FORMAT), options
26
30
  end
27
31
 
32
+ def local_time_ago(time, options = nil)
33
+ options, type = extract_options_and_value(options, :type)
34
+ options[:type] = 'time-ago'
35
+ local_relative_time time, options
36
+ end
37
+
28
38
  def utc_time(time_or_date)
29
39
  if time_or_date.respond_to?(:in_time_zone)
30
40
  time_or_date.in_time_zone.utc
@@ -34,7 +44,7 @@ module LocalTimeHelper
34
44
  end
35
45
 
36
46
  private
37
- def time_format(format)
47
+ def find_time_format(format)
38
48
  if format.is_a?(Symbol)
39
49
  if (i18n_format = I18n.t("time.formats.#{format}", default: [:"date.formats.#{format}", ''])).present?
40
50
  i18n_format
@@ -47,4 +57,16 @@ module LocalTimeHelper
47
57
  format.presence || DEFAULT_FORMAT
48
58
  end
49
59
  end
60
+
61
+ def extract_options_and_value(options, value_key = nil)
62
+ case options
63
+ when Hash
64
+ value = options.delete(value_key)
65
+ [ options, value ]
66
+ when NilClass
67
+ [ {} ]
68
+ else
69
+ [ {}, options ]
70
+ end
71
+ end
50
72
  end
@@ -1,11 +1,19 @@
1
1
  require_relative '../../app/helpers/local_time_helper'
2
2
  require 'active_support/all'
3
3
  require 'action_view'
4
+
4
5
  require 'minitest/autorun'
6
+ begin
7
+ # 2.0.0
8
+ class TestCase < MiniTest::Test; end
9
+ rescue NameError
10
+ # 1.9.3
11
+ class TestCase < MiniTest::Unit::TestCase; end
12
+ end
5
13
 
6
14
  I18n.enforce_available_locales = false
7
15
 
8
- class LocalTimeHelperTest < MiniTest::Unit::TestCase
16
+ class LocalTimeHelperTest < TestCase
9
17
  include ActionView::Helpers::DateHelper, ActionView::Helpers::TagHelper
10
18
  include LocalTimeHelper
11
19
 
@@ -51,6 +59,11 @@ class LocalTimeHelperTest < MiniTest::Unit::TestCase
51
59
  assert_equal expected, local_time(@time, format: '%b %e')
52
60
  end
53
61
 
62
+ def test_local_time_with_format_as_string
63
+ expected = %Q(<time data-format="%b %e" data-local="time" datetime="#{@time_js}">Nov 21</time>)
64
+ assert_equal expected, local_time(@time, '%b %e')
65
+ end
66
+
54
67
  def test_local_time_with_i18n_format
55
68
  expected = %Q(<time data-format="%b %e" data-local="time" datetime="#{@time_js}">Nov 21</time>)
56
69
  assert_equal expected, local_time(@time, format: :simple_time)
@@ -88,6 +101,11 @@ class LocalTimeHelperTest < MiniTest::Unit::TestCase
88
101
  assert_equal expected, local_date(@time.to_date, format: '%b %e')
89
102
  end
90
103
 
104
+ def test_local_date_with_format_as_string
105
+ expected = %Q(<time data-format="%b %e" data-local="time" datetime="#{@time_js}">Nov 21</time>)
106
+ assert_equal expected, local_date(@time.to_date, '%b %e')
107
+ end
108
+
91
109
  def test_local_date_with_i18n_format
92
110
  expected = %Q(<time data-format="%b %e" data-local="time" datetime="#{@time_js}">Nov 21</time>)
93
111
  assert_equal expected, local_date(@time.to_date, format: :simple_date)
@@ -112,4 +130,14 @@ class LocalTimeHelperTest < MiniTest::Unit::TestCase
112
130
  expected = %Q(<time class="date-time" data-local="time-ago" datetime="#{@time_js}">November 21, 2013 6:00am</time>)
113
131
  assert_equal expected, local_time_ago(@time, class: "date-time")
114
132
  end
133
+
134
+ def test_relative_time
135
+ expected = %Q(<time data-local="time-or-date" datetime="#{@time_js}">November 21, 2013 6:00am</time>)
136
+ assert_equal expected, local_relative_time(@time, type: "time-or-date")
137
+ end
138
+
139
+ def test_local_time_ago_with_type_as_string
140
+ expected = %Q(<time data-local="time-or-date" datetime="#{@time_js}">November 21, 2013 6:00am</time>)
141
+ assert_equal expected, local_relative_time(@time, "time-or-date")
142
+ end
115
143
  end
@@ -1,13 +1,18 @@
1
- #= require qunit
2
- #= require moment
3
- #= require sinon-timers
1
+ #= require vendor/qunit
2
+ #= require vendor/moment
3
+ #= require vendor/sinon-timers
4
4
  #= require local_time
5
- #= require_directory ./unit
5
+
6
6
  #= require_self
7
+ #= require_directory .
8
+
9
+ @addTimeEl = ({format, type, datetime} = {}) ->
10
+ format ?= "%Y"
11
+ type ?= "time"
12
+ datetime ?= "2013-11-12T12:13:00Z"
7
13
 
8
- @addTimeEl = (format = "%Y", datetime = "2013-11-12T12:13:00Z") ->
9
14
  el = document.createElement "time"
10
- el.setAttribute "data-local", "time"
15
+ el.setAttribute "data-local", type
11
16
  el.setAttribute "data-format", format
12
17
  el.setAttribute "datetime", datetime
13
18
  document.body.appendChild el
@@ -8,7 +8,7 @@ test "date", ->
8
8
  assertLocalized "date", "date"
9
9
 
10
10
  test "unparseable time", ->
11
- el = addTimeEl "%Y", ":("
11
+ el = addTimeEl format: "%Y", datetime: ":("
12
12
  setText el, "2013"
13
13
  run()
14
14
  equal getText(el), "2013"
@@ -10,11 +10,6 @@ test "document time:elapse", ->
10
10
  triggerEvent "time:elapse"
11
11
  ok getText el
12
12
 
13
- test "window popstate", ->
14
- el = addTimeEl()
15
- triggerEvent "popstate", window
16
- ok getText el
17
-
18
13
  test "document page:update with Turbolinks on", ->
19
14
  el = addTimeEl()
20
15
  triggerEvent "page:update"