local_time 2.1.0 → 3.0.2
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 +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +43 -9
- data/app/assets/javascripts/local-time.es2017-esm.js +1 -0
- data/app/assets/javascripts/local-time.es2017-umd.js +1 -0
- data/app/helpers/local_time_helper.rb +72 -21
- data/lib/local_time/version.rb +3 -0
- data/test/helpers/local_time_helper_test.rb +57 -22
- data/test/javascripts/builds/index.js +24028 -0
- data/test/javascripts/fixtures/index.html +20 -0
- data/test/javascripts/fixtures/time_zone_check.html +20 -0
- data/test/javascripts/server.mjs +41 -0
- data/test/javascripts/src/format24_test.coffee +94 -0
- data/test/javascripts/src/i18n_test.coffee +2 -0
- data/test/javascripts/src/index.coffee +10 -0
- data/test/javascripts/src/local_time_test.coffee +7 -1
- data/test/javascripts/src/relative_date_test.coffee +3 -1
- data/test/javascripts/src/strftime_test.coffee +73 -0
- data/test/javascripts/src/{test.coffee → test_helpers.coffee} +8 -5
- data/test/javascripts/src/time_ago_test.coffee +2 -0
- data/test/javascripts/vendor/moment.js +5681 -2
- data/test/javascripts/vendor/sinon.js +17742 -0
- metadata +74 -21
- data/app/assets/javascripts/local-time.js +0 -1
- data/test/javascripts/fixtures/body.html +0 -8
- data/test/javascripts/vendor/sinon-timers.js +0 -385
@@ -0,0 +1,20 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<title>Test Suite</title>
|
5
|
+
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.19.4.css">
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<div id="qunit"></div>
|
9
|
+
<div id="qunit-fixture"></div>
|
10
|
+
|
11
|
+
<script src="https://code.jquery.com/qunit/qunit-2.19.4.js"></script>
|
12
|
+
<script src="/test-build"></script>
|
13
|
+
|
14
|
+
<time id="one" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="2013-11-12T12:13:00Z"></time>
|
15
|
+
<time id="two" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="2013-01-02T02:04:00Z"></time>
|
16
|
+
<time id="past" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="1805-05-15T01:01:00Z"></time>
|
17
|
+
<time id="future" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="3333-02-14T23:55:00Z"></time>
|
18
|
+
<time id="date" data-format="%B %e, %Y" data-local="time" datetime="2013-11-12T12:13:00Z"></time>
|
19
|
+
<time id="ago"></time>
|
20
|
+
</body>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<title>Time Zone Check</title>
|
5
|
+
<script src="/current-build"></script>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<script>
|
9
|
+
LocalTime.start()
|
10
|
+
</script>
|
11
|
+
|
12
|
+
<p><strong>Standard Time (Northern Hemisphere) | DST Time (Southern Hemisphere)</strong></p>
|
13
|
+
<time data-format="%B %e, %Y %l:%M%P (%Z)" data-local="time" datetime="2023-12-12T12:13:00Z"></time>
|
14
|
+
|
15
|
+
<br>
|
16
|
+
<br>
|
17
|
+
|
18
|
+
<p><strong>DST Time (Northern Hemisphere) | Standard Time (Southern Hemisphere)</strong></p>
|
19
|
+
<time data-format="%B %e, %Y %l:%M%P (%Z)" data-local="time" datetime="2023-07-12T12:13:00Z"></time>
|
20
|
+
</body>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { Router } from "express"
|
2
|
+
import express from "express"
|
3
|
+
import path from "path"
|
4
|
+
import * as url from 'url'
|
5
|
+
|
6
|
+
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
7
|
+
const router = Router()
|
8
|
+
|
9
|
+
router.get("/", (_request, response) => {
|
10
|
+
const fixture = path.join(__dirname, "./fixtures/index.html")
|
11
|
+
response.status(200).sendFile(fixture)
|
12
|
+
})
|
13
|
+
|
14
|
+
router.get("/time-zone-check", (_request, response) => {
|
15
|
+
const fixture = path.join(__dirname, "./fixtures/time_zone_check.html")
|
16
|
+
response.status(200).sendFile(fixture)
|
17
|
+
})
|
18
|
+
|
19
|
+
router.get("/test-build", (_request, response) => {
|
20
|
+
const file = path.join(__dirname, "./builds/index.js")
|
21
|
+
response.status(200).sendFile(file)
|
22
|
+
})
|
23
|
+
|
24
|
+
router.get("/current-build", (_request, response) => {
|
25
|
+
const file = path.join(__dirname, "./../../app/assets/javascripts/local-time.es2017-umd.js")
|
26
|
+
response.status(200).sendFile(file)
|
27
|
+
})
|
28
|
+
|
29
|
+
const app = express()
|
30
|
+
app.use(express.static("."))
|
31
|
+
app.use(router)
|
32
|
+
|
33
|
+
const port = parseInt(process.env.PORT || "9000")
|
34
|
+
app.listen(port, () => {
|
35
|
+
console.log(`
|
36
|
+
/*
|
37
|
+
Please go to http://localhost:${port}/ to run JS tests now.
|
38
|
+
Or go to http://localhost:${port}/time-zone-check to manually test a time zone.
|
39
|
+
*/
|
40
|
+
`)
|
41
|
+
})
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
3
|
+
{addTimeEl, assert, defer, getText, testAsync, testGroup, getTitle} = LocalTime.TestHelpers
|
4
|
+
{config} = LocalTime
|
5
|
+
|
6
|
+
testGroup "format24 (24h time)", ->
|
7
|
+
testAsync "default behavior", (done) ->
|
8
|
+
now = moment()
|
9
|
+
|
10
|
+
el = addTimeEl format: "%-l:%M%P", format24: "%H:%M", datetime: now.toISOString()
|
11
|
+
defer ->
|
12
|
+
assert.equal getText(el), now.format("h:mma")
|
13
|
+
done()
|
14
|
+
|
15
|
+
testAsync "turned off", (done) ->
|
16
|
+
now = moment()
|
17
|
+
originalFormat24 = config.useFormat24
|
18
|
+
config.useFormat24 = false
|
19
|
+
|
20
|
+
el = addTimeEl format: "%-l:%M%P", format24: "%H:%M", datetime: now.toISOString()
|
21
|
+
defer ->
|
22
|
+
assert.equal getText(el), now.format("h:mma")
|
23
|
+
config.useFormat24 = originalFormat24
|
24
|
+
done()
|
25
|
+
|
26
|
+
testAsync "turned on", (done) ->
|
27
|
+
now = moment()
|
28
|
+
originalFormat24 = config.useFormat24
|
29
|
+
config.useFormat24 = true
|
30
|
+
|
31
|
+
el = addTimeEl format: "%-l:%M%P", format24: "%H:%M", datetime: now.toISOString()
|
32
|
+
defer ->
|
33
|
+
assert.equal getText(el), now.format("HH:mm")
|
34
|
+
config.useFormat24 = originalFormat24
|
35
|
+
done()
|
36
|
+
|
37
|
+
testAsync "fallback for missing data-format24 values", (done) ->
|
38
|
+
now = moment()
|
39
|
+
originalFormat24 = config.useFormat24
|
40
|
+
config.useFormat24 = true
|
41
|
+
|
42
|
+
el = addTimeEl format: "%-l:%M%P", datetime: now.toISOString()
|
43
|
+
defer ->
|
44
|
+
assert.equal getText(el), now.format("h:mma")
|
45
|
+
config.useFormat24 = originalFormat24
|
46
|
+
done()
|
47
|
+
|
48
|
+
testAsync "turned off for relative time elements", (done) ->
|
49
|
+
ago = moment().subtract("days", 5)
|
50
|
+
originalFormat24 = config.useFormat24
|
51
|
+
config.useFormat24 = false
|
52
|
+
|
53
|
+
el = addTimeEl type: "time-ago", datetime: ago.toISOString()
|
54
|
+
defer ->
|
55
|
+
assert.equal getText(el), "#{ago.format("dddd")} at #{ago.format("h:mma")}"
|
56
|
+
config.useFormat24 = originalFormat24
|
57
|
+
done()
|
58
|
+
|
59
|
+
testAsync "turned on for relative time elements", (done) ->
|
60
|
+
ago = moment().subtract("days", 5)
|
61
|
+
originalFormat24 = config.useFormat24
|
62
|
+
config.useFormat24 = true
|
63
|
+
|
64
|
+
el = addTimeEl type: "time-ago", datetime: ago.toISOString()
|
65
|
+
defer ->
|
66
|
+
assert.equal getText(el), "#{ago.format("dddd")} at #{ago.format("HH:mm")}"
|
67
|
+
config.useFormat24 = originalFormat24
|
68
|
+
done()
|
69
|
+
|
70
|
+
testAsync "element title when turned off", (done) ->
|
71
|
+
ago = moment().subtract("days", 5)
|
72
|
+
originalFormat24 = config.useFormat24
|
73
|
+
config.useFormat24 = false
|
74
|
+
|
75
|
+
el = addTimeEl type: "time-ago", datetime: ago.toISOString()
|
76
|
+
defer ->
|
77
|
+
regex = new RegExp(ago.format('MMMM D, YYYY [at] h:mma') + " (\\w{3,4}|UTC[+-]d+)")
|
78
|
+
assert.ok regex.test(getTitle(el)), "'#{getTitle(el)}' doesn't look correct, it should match regex #{regex}"
|
79
|
+
config.useFormat24 = originalFormat24
|
80
|
+
done()
|
81
|
+
|
82
|
+
|
83
|
+
testAsync "element title when turned on", (done) ->
|
84
|
+
ago = moment().subtract("days", 5)
|
85
|
+
originalFormat24 = config.useFormat24
|
86
|
+
config.useFormat24 = true
|
87
|
+
|
88
|
+
el = addTimeEl type: "time-ago", datetime: ago.toISOString()
|
89
|
+
defer ->
|
90
|
+
regex = new RegExp(ago.format('MMMM D, YYYY [at] HH:mm') + " (\\w{3,4}|UTC[+-]d+)")
|
91
|
+
assert.ok regex.test(getTitle(el)), "'#{getTitle(el)}' doesn't look correct, it should match regex #{regex}"
|
92
|
+
config.useFormat24 = originalFormat24
|
93
|
+
done()
|
94
|
+
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
1
3
|
{addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
|
2
4
|
|
3
5
|
testGroup "localized", ->
|
@@ -13,6 +15,11 @@ testGroup "localized", ->
|
|
13
15
|
setText el, "2013"
|
14
16
|
assert.equal getText(el), "2013"
|
15
17
|
|
18
|
+
test "processed timestamp", ->
|
19
|
+
el = addTimeEl type: "time-or-date", datetime: moment().toISOString()
|
20
|
+
assert.notOk el.getAttribute("data-processed-at")
|
21
|
+
LocalTime.run()
|
22
|
+
assert.ok el.getAttribute("data-processed-at")
|
16
23
|
|
17
24
|
assertLocalized = (id, type = "time") ->
|
18
25
|
switch type
|
@@ -27,7 +34,6 @@ assertLocalized = (id, type = "time") ->
|
|
27
34
|
|
28
35
|
assert.ok datetime = el.getAttribute "datetime"
|
29
36
|
assert.ok local = getText el
|
30
|
-
assert.equal el.getAttribute("aria-label"), local
|
31
37
|
|
32
38
|
datetimeParsed = moment datetime
|
33
39
|
localParsed = moment local, momentFormat
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
1
3
|
{addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
|
2
4
|
|
3
5
|
testGroup "relative date", ->
|
@@ -82,7 +84,7 @@ testGroup "relative weekday or date", ->
|
|
82
84
|
done()
|
83
85
|
|
84
86
|
testAsync "before this week", (done) ->
|
85
|
-
before = moment().subtract("days", 8)
|
87
|
+
before = moment("#{new Date().getFullYear()}1031", "YYYYMMDD").subtract("days", 8)
|
86
88
|
el = addTimeEl type: "weekday-or-date", datetime: before.toISOString()
|
87
89
|
defer ->
|
88
90
|
assert.equal getText(el), before.format("MMM D")
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
1
3
|
{addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
|
2
4
|
|
3
5
|
momentMap =
|
@@ -26,6 +28,22 @@ momentMap =
|
|
26
28
|
"%y": "YY"
|
27
29
|
"%Y": "YYYY"
|
28
30
|
|
31
|
+
stubDateToLocaleString = (stubImplementation, callback) ->
|
32
|
+
original = Date.prototype.toLocaleString
|
33
|
+
Date.prototype.toLocaleString = stubImplementation
|
34
|
+
try
|
35
|
+
callback()
|
36
|
+
finally
|
37
|
+
Date.prototype.toLocaleString = original
|
38
|
+
|
39
|
+
stubDateToString = (stubImplementation, callback) ->
|
40
|
+
original = Date.prototype.toString
|
41
|
+
Date.prototype.toString = stubImplementation
|
42
|
+
try
|
43
|
+
callback()
|
44
|
+
finally
|
45
|
+
Date.prototype.toString = original
|
46
|
+
|
29
47
|
testGroup "strftime", ->
|
30
48
|
for day in [0..30] by 6
|
31
49
|
do (day) ->
|
@@ -52,3 +70,58 @@ testGroup "strftime", ->
|
|
52
70
|
|
53
71
|
text = getText el
|
54
72
|
assert.ok /^(\w{3,4}|UTC[\+\-]\d+)$/.test(text), "'#{text}' doesn't look like a timezone. System date: '#{new Date}'"
|
73
|
+
|
74
|
+
testGroup "strftime time zones", ->
|
75
|
+
for timeZone in Object.keys(LocalTime.knownEdgeCaseTimeZones)
|
76
|
+
do (timeZone) ->
|
77
|
+
test "edge-case time zone #{timeZone}", ->
|
78
|
+
stub = -> "Thu Nov 30 2023 14:22:57 GMT-0000 (#{timeZone})"
|
79
|
+
|
80
|
+
stubDateToLocaleString stub, ->
|
81
|
+
el = addTimeEl format: "%Z", datetime: "2023-11-30T14:22:57Z"
|
82
|
+
LocalTime.process(el)
|
83
|
+
|
84
|
+
assert.equal getText(el), LocalTime.knownEdgeCaseTimeZones[timeZone]
|
85
|
+
|
86
|
+
test "time zones Intl can abbreviate are parsed correctly", ->
|
87
|
+
stub = (_, options) ->
|
88
|
+
if options.timeZoneName == "long"
|
89
|
+
"Thu Nov 30 2023 14:22:57 GMT-0800 (Alaska Daylight Time)" # not a known edge-case
|
90
|
+
else if options.timeZoneName == "short"
|
91
|
+
"11/30/2023, 2:22:57 PM AKDT" # possible to abbreviate
|
92
|
+
|
93
|
+
stubDateToLocaleString stub, ->
|
94
|
+
el = addTimeEl format: "%Z", datetime: "2023-11-30T14:22:57Z"
|
95
|
+
LocalTime.process(el)
|
96
|
+
|
97
|
+
assert.equal getText(el), "AKDT"
|
98
|
+
|
99
|
+
test "time zones Intl can't abbreviate are parsed by our heuristic", ->
|
100
|
+
dateToStringStub = -> "Sat Dec 02 2023 17:20:26 GMT-0600 (Central Standard Time)"
|
101
|
+
dateToLocaleStringStub = (_, options) ->
|
102
|
+
if options.timeZoneName == "long"
|
103
|
+
"Thu Nov 30 2023 14:22:57 GMT+0700 (Central Twilight Time)" # not a known edge-case
|
104
|
+
else if options.timeZoneName == "short"
|
105
|
+
"11/30/2023, 2:22:57 PM GMT+7" # not possible to abbreviate
|
106
|
+
|
107
|
+
stubDateToString dateToStringStub, ->
|
108
|
+
stubDateToLocaleString dateToLocaleStringStub, ->
|
109
|
+
el = addTimeEl format: "%Z", datetime: "2023-11-30T14:22:57Z"
|
110
|
+
LocalTime.process(el)
|
111
|
+
|
112
|
+
assert.equal getText(el), "CST"
|
113
|
+
|
114
|
+
test "time zones Intl can't abbreviate and our heuristic can't parse display GMT offset", ->
|
115
|
+
dateToStringStub = -> ""
|
116
|
+
dateToLocaleStringStub = (_, options) ->
|
117
|
+
if options.timeZoneName == "long"
|
118
|
+
"Thu Nov 30 2023 14:22:57 GMT+0700 (Central Twilight Time)" # not a known edge-case
|
119
|
+
else if options.timeZoneName == "short"
|
120
|
+
"11/30/2023, 2:22:57 PM GMT+7" # not possible to abbreviate
|
121
|
+
|
122
|
+
stubDateToString dateToStringStub, ->
|
123
|
+
stubDateToLocaleString dateToLocaleStringStub, ->
|
124
|
+
el = addTimeEl format: "%Z", datetime: "2023-11-30T14:22:57Z"
|
125
|
+
LocalTime.process(el)
|
126
|
+
|
127
|
+
assert.equal getText(el), "GMT+7"
|
@@ -1,8 +1,6 @@
|
|
1
|
-
|
2
|
-
#= require sinon-timers
|
1
|
+
import LocalTime from "local_time"
|
3
2
|
|
4
|
-
|
5
|
-
#= require_directory .
|
3
|
+
LocalTime.start()
|
6
4
|
|
7
5
|
LocalTime.TestHelpers =
|
8
6
|
assert: QUnit.assert
|
@@ -14,7 +12,7 @@ LocalTime.TestHelpers =
|
|
14
12
|
done = assert.async()
|
15
13
|
callback(done)
|
16
14
|
|
17
|
-
addTimeEl: ({format, type, datetime} = {}) ->
|
15
|
+
addTimeEl: ({format, type, datetime, format24} = {}) ->
|
18
16
|
format ?= "%Y"
|
19
17
|
type ?= "time"
|
20
18
|
datetime ?= "2013-11-12T12:13:00Z"
|
@@ -23,6 +21,8 @@ LocalTime.TestHelpers =
|
|
23
21
|
el.setAttribute "data-local", type
|
24
22
|
el.setAttribute "data-format", format
|
25
23
|
el.setAttribute "datetime", datetime
|
24
|
+
if format24 then el.setAttribute "data-format24", format24
|
25
|
+
|
26
26
|
document.body.appendChild el
|
27
27
|
el
|
28
28
|
|
@@ -34,6 +34,9 @@ LocalTime.TestHelpers =
|
|
34
34
|
# reading the text content, not a potentially arbitrary property.
|
35
35
|
el.innerHTML
|
36
36
|
|
37
|
+
getTitle: (el) ->
|
38
|
+
el.getAttribute("title")
|
39
|
+
|
37
40
|
triggerEvent: (name, el = document) ->
|
38
41
|
event = document.createEvent "Events"
|
39
42
|
event.initEvent name, true, true
|