local_time 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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"