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