local_time 2.1.0 → 3.0.3
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 +50 -10
- 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 +60 -31
- data/test/javascripts/builds/index.js +26626 -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.js +109 -0
- data/test/javascripts/src/i18n_test.js +55 -0
- data/test/javascripts/src/index.js +10 -0
- data/test/javascripts/src/local_time_test.js +54 -0
- data/test/javascripts/src/relative_date_test.js +121 -0
- data/test/javascripts/src/strftime_test.js +151 -0
- data/test/javascripts/src/test_helpers.js +66 -0
- data/test/javascripts/src/time_ago_test.js +72 -0
- data/test/javascripts/vendor/moment.js +5684 -2
- data/test/javascripts/vendor/sinon.js +20485 -0
- metadata +81 -28
- data/app/assets/javascripts/local-time.js +0 -1
- data/test/javascripts/fixtures/body.html +0 -8
- data/test/javascripts/src/i18n_test.coffee +0 -47
- data/test/javascripts/src/local_time_test.coffee +0 -37
- data/test/javascripts/src/relative_date_test.coffee +0 -89
- data/test/javascripts/src/strftime_test.coffee +0 -54
- data/test/javascripts/src/test.coffee +0 -43
- data/test/javascripts/src/time_ago_test.coffee +0 -56
- 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,109 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
3
|
+
const { addTimeEl, assert, defer, getText, testAsync, testGroup, getTitle } = LocalTime.TestHelpers
|
4
|
+
const { config } = LocalTime
|
5
|
+
|
6
|
+
testGroup("format24 (24h time)", () => {
|
7
|
+
testAsync("default behavior", (done) => {
|
8
|
+
const now = moment()
|
9
|
+
|
10
|
+
const 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
|
+
})
|
16
|
+
|
17
|
+
testAsync("turned off", (done) => {
|
18
|
+
const now = moment()
|
19
|
+
const originalFormat24 = config.useFormat24
|
20
|
+
config.useFormat24 = false
|
21
|
+
|
22
|
+
const el = addTimeEl({ format: "%-l:%M%P", format24: "%H:%M", datetime: now.toISOString() })
|
23
|
+
defer(() => {
|
24
|
+
assert.equal(getText(el), now.format("h:mma"))
|
25
|
+
config.useFormat24 = originalFormat24
|
26
|
+
done()
|
27
|
+
})
|
28
|
+
})
|
29
|
+
|
30
|
+
testAsync("turned on", (done) => {
|
31
|
+
const now = moment()
|
32
|
+
const originalFormat24 = config.useFormat24
|
33
|
+
config.useFormat24 = true
|
34
|
+
|
35
|
+
const el = addTimeEl({ format: "%-l:%M%P", format24: "%H:%M", datetime: now.toISOString() })
|
36
|
+
defer(() => {
|
37
|
+
assert.equal(getText(el), now.format("HH:mm"))
|
38
|
+
config.useFormat24 = originalFormat24
|
39
|
+
done()
|
40
|
+
})
|
41
|
+
})
|
42
|
+
|
43
|
+
testAsync("fallback for missing data-format24 values", (done) => {
|
44
|
+
const now = moment()
|
45
|
+
const originalFormat24 = config.useFormat24
|
46
|
+
config.useFormat24 = true
|
47
|
+
|
48
|
+
const el = addTimeEl({ format: "%-l:%M%P", datetime: now.toISOString() })
|
49
|
+
defer(() => {
|
50
|
+
assert.equal(getText(el), now.format("h:mma"))
|
51
|
+
config.useFormat24 = originalFormat24
|
52
|
+
done()
|
53
|
+
})
|
54
|
+
})
|
55
|
+
|
56
|
+
testAsync("turned off for relative time elements", (done) => {
|
57
|
+
const ago = moment().subtract("days", 5)
|
58
|
+
const originalFormat24 = config.useFormat24
|
59
|
+
config.useFormat24 = false
|
60
|
+
|
61
|
+
const el = addTimeEl({ type: "time-ago", datetime: ago.toISOString() })
|
62
|
+
defer(() => {
|
63
|
+
assert.equal(getText(el), `${ago.format("dddd")} at ${ago.format("h:mma")}`)
|
64
|
+
config.useFormat24 = originalFormat24
|
65
|
+
done()
|
66
|
+
})
|
67
|
+
})
|
68
|
+
|
69
|
+
testAsync("turned on for relative time elements", (done) => {
|
70
|
+
const ago = moment().subtract("days", 5)
|
71
|
+
const originalFormat24 = config.useFormat24
|
72
|
+
config.useFormat24 = true
|
73
|
+
|
74
|
+
const el = addTimeEl({ type: "time-ago", datetime: ago.toISOString() })
|
75
|
+
defer(() => {
|
76
|
+
assert.equal(getText(el), `${ago.format("dddd")} at ${ago.format("HH:mm")}`)
|
77
|
+
config.useFormat24 = originalFormat24
|
78
|
+
done()
|
79
|
+
})
|
80
|
+
})
|
81
|
+
|
82
|
+
testAsync("element title when turned off", (done) => {
|
83
|
+
const ago = moment().subtract("days", 5)
|
84
|
+
const originalFormat24 = config.useFormat24
|
85
|
+
config.useFormat24 = false
|
86
|
+
|
87
|
+
const el = addTimeEl({ type: "time-ago", datetime: ago.toISOString() })
|
88
|
+
defer(() => {
|
89
|
+
const regex = new RegExp(ago.format('MMMM D, YYYY [at] h:mma') + " (\\w{3,4}|UTC[+-]d+)")
|
90
|
+
assert.ok(regex.test(getTitle(el)), `'${getTitle(el)}' doesn't look correct, it should match regex ${regex}`)
|
91
|
+
config.useFormat24 = originalFormat24
|
92
|
+
done()
|
93
|
+
})
|
94
|
+
})
|
95
|
+
|
96
|
+
testAsync("element title when turned on", (done) => {
|
97
|
+
const ago = moment().subtract("days", 5)
|
98
|
+
const originalFormat24 = config.useFormat24
|
99
|
+
config.useFormat24 = true
|
100
|
+
|
101
|
+
const el = addTimeEl({ type: "time-ago", datetime: ago.toISOString() })
|
102
|
+
defer(() => {
|
103
|
+
const regex = new RegExp(ago.format('MMMM D, YYYY [at] HH:mm') + " (\\w{3,4}|UTC[+-]d+)")
|
104
|
+
assert.ok(regex.test(getTitle(el)), `'${getTitle(el)}' doesn't look correct, it should match regex ${regex}`)
|
105
|
+
config.useFormat24 = originalFormat24
|
106
|
+
done()
|
107
|
+
})
|
108
|
+
})
|
109
|
+
})
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
3
|
+
const { addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent } = LocalTime.TestHelpers
|
4
|
+
const { config } = LocalTime
|
5
|
+
const { i18n } = config
|
6
|
+
|
7
|
+
testGroup("i18n", () => {
|
8
|
+
testAsync("updating a value", (done) => {
|
9
|
+
const now = moment()
|
10
|
+
const values = i18n[config.defaultLocale].date
|
11
|
+
|
12
|
+
const originalValue = values.today
|
13
|
+
values.today = "2day"
|
14
|
+
|
15
|
+
const el = addTimeEl({ type: "weekday", datetime: now.toISOString() })
|
16
|
+
defer(() => {
|
17
|
+
assert.equal(getText(el), "2day")
|
18
|
+
values.today = originalValue
|
19
|
+
done()
|
20
|
+
})
|
21
|
+
})
|
22
|
+
|
23
|
+
testAsync("adding a new locale", (done) => {
|
24
|
+
const now = moment()
|
25
|
+
|
26
|
+
const originalLocale = config.locale
|
27
|
+
config.locale = "es"
|
28
|
+
i18n.es = { date: { today: "hoy" } }
|
29
|
+
|
30
|
+
const el = addTimeEl({ type: "weekday", datetime: now.toISOString() })
|
31
|
+
defer(() => {
|
32
|
+
assert.equal(getText(el), "hoy")
|
33
|
+
config.locale = originalLocale
|
34
|
+
done()
|
35
|
+
})
|
36
|
+
})
|
37
|
+
|
38
|
+
testAsync("falling back to the default locale", (done) => {
|
39
|
+
const now = moment()
|
40
|
+
const yesterday = moment().subtract("days", 1)
|
41
|
+
|
42
|
+
const originalLocale = config.locale
|
43
|
+
config.locale = "es"
|
44
|
+
i18n.es = { date: { yesterday: "ayer" } }
|
45
|
+
|
46
|
+
const elWithTranslation = addTimeEl({ type: "weekday", datetime: yesterday.toISOString() })
|
47
|
+
const elWithoutTranslation = addTimeEl({ type: "weekday", datetime: now.toISOString() })
|
48
|
+
defer(() => {
|
49
|
+
assert.equal(getText(elWithTranslation), "ayer")
|
50
|
+
assert.equal(getText(elWithoutTranslation), "today")
|
51
|
+
config.locale = originalLocale
|
52
|
+
done()
|
53
|
+
})
|
54
|
+
})
|
55
|
+
})
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
3
|
+
const { addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent } = LocalTime.TestHelpers
|
4
|
+
|
5
|
+
testGroup("localized", () => {
|
6
|
+
for (var id of [ "one", "two", "past", "future" ]) {
|
7
|
+
test(id, () => {
|
8
|
+
assertLocalized(id)
|
9
|
+
})
|
10
|
+
}
|
11
|
+
|
12
|
+
test("date", () => {
|
13
|
+
assertLocalized("date", "date")
|
14
|
+
})
|
15
|
+
|
16
|
+
test("unparseable time", () => {
|
17
|
+
const el = addTimeEl({ format: "%Y", datetime: ":(" })
|
18
|
+
setText(el, "2013")
|
19
|
+
assert.equal(getText(el), "2013")
|
20
|
+
})
|
21
|
+
})
|
22
|
+
|
23
|
+
test("processed timestamp", () => {
|
24
|
+
const el = addTimeEl({ type: "time-or-date", datetime: moment().toISOString() })
|
25
|
+
assert.notOk(el.getAttribute("data-processed-at"))
|
26
|
+
LocalTime.run()
|
27
|
+
assert.ok(el.getAttribute("data-processed-at"))
|
28
|
+
})
|
29
|
+
|
30
|
+
function assertLocalized(id, type = "time") {
|
31
|
+
let compare, datetime, local, momentFormat
|
32
|
+
switch (type) {
|
33
|
+
case "time":
|
34
|
+
momentFormat = "MMMM D, YYYY h:mma"
|
35
|
+
compare = "toString"
|
36
|
+
break
|
37
|
+
case "date":
|
38
|
+
momentFormat = "MMMM D, YYYY"
|
39
|
+
compare = "dayOfYear"
|
40
|
+
break
|
41
|
+
}
|
42
|
+
|
43
|
+
const el = document.getElementById(id)
|
44
|
+
|
45
|
+
assert.ok(datetime = el.getAttribute("datetime"))
|
46
|
+
assert.ok(local = getText(el))
|
47
|
+
|
48
|
+
const datetimeParsed = moment(datetime)
|
49
|
+
const localParsed = moment(local, momentFormat)
|
50
|
+
|
51
|
+
assert.ok(datetimeParsed.isValid())
|
52
|
+
assert.ok(localParsed.isValid())
|
53
|
+
assert.equal(datetimeParsed[compare](), localParsed[compare]())
|
54
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
3
|
+
const { addTimeEl, assert, defer, getText, testAsync, testGroup, stubNow } = LocalTime.TestHelpers
|
4
|
+
|
5
|
+
testGroup("relative date", () => {
|
6
|
+
testAsync("this year", (done) => {
|
7
|
+
const now = moment()
|
8
|
+
const el = addTimeEl({ type: "date", datetime: now.toISOString() })
|
9
|
+
defer(() => {
|
10
|
+
assert.equal(getText(el), now.format("MMM D"))
|
11
|
+
done()
|
12
|
+
})
|
13
|
+
})
|
14
|
+
|
15
|
+
testAsync("last year", (done) => {
|
16
|
+
const before = moment().subtract("years", 1).subtract("days", 1)
|
17
|
+
const el = addTimeEl({ type: "date", datetime: before.toISOString() })
|
18
|
+
defer(() => {
|
19
|
+
assert.equal(getText(el), before.format("MMM D, YYYY"))
|
20
|
+
done()
|
21
|
+
})
|
22
|
+
})
|
23
|
+
})
|
24
|
+
|
25
|
+
testGroup("relative time or date", () => {
|
26
|
+
testAsync("today", (done) => {
|
27
|
+
const now = moment()
|
28
|
+
const el = addTimeEl({ type: "time-or-date", datetime: now.toISOString() })
|
29
|
+
defer(() => {
|
30
|
+
assert.equal(getText(el), now.format("h:mma"))
|
31
|
+
done()
|
32
|
+
})
|
33
|
+
})
|
34
|
+
|
35
|
+
testAsync("before today", (done) => {
|
36
|
+
const before = moment().subtract("days", 1)
|
37
|
+
const el = addTimeEl({ type: "time-or-date", datetime: before.toISOString() })
|
38
|
+
defer(() => {
|
39
|
+
assert.equal(getText(el), before.format("MMM D"))
|
40
|
+
done()
|
41
|
+
})
|
42
|
+
})
|
43
|
+
})
|
44
|
+
|
45
|
+
testGroup("relative weekday", () => {
|
46
|
+
testAsync("today", (done) => {
|
47
|
+
const now = moment()
|
48
|
+
const el = addTimeEl({ type: "weekday", datetime: now.toISOString() })
|
49
|
+
defer(() => {
|
50
|
+
assert.equal(getText(el), "today")
|
51
|
+
done()
|
52
|
+
})
|
53
|
+
})
|
54
|
+
|
55
|
+
testAsync("yesterday", (done) => {
|
56
|
+
const yesterday = moment().subtract("days", 1)
|
57
|
+
const el = addTimeEl({ type: "weekday", datetime: yesterday.toISOString() })
|
58
|
+
defer(() => {
|
59
|
+
assert.equal(getText(el), "yesterday")
|
60
|
+
done()
|
61
|
+
})
|
62
|
+
})
|
63
|
+
|
64
|
+
testAsync("this week", (done) => {
|
65
|
+
const recent = moment().subtract("days", 3)
|
66
|
+
const el = addTimeEl({ type: "weekday", datetime: recent.toISOString() })
|
67
|
+
defer(() => {
|
68
|
+
assert.equal(getText(el), recent.format("dddd"))
|
69
|
+
done()
|
70
|
+
})
|
71
|
+
})
|
72
|
+
|
73
|
+
testAsync("before this week", (done) => {
|
74
|
+
const before = moment().subtract("days", 8)
|
75
|
+
const el = addTimeEl({ type: "weekday", datetime: before.toISOString() })
|
76
|
+
defer(() => {
|
77
|
+
assert.equal(getText(el), "")
|
78
|
+
done()
|
79
|
+
})
|
80
|
+
})
|
81
|
+
})
|
82
|
+
|
83
|
+
testGroup("relative weekday or date", () => {
|
84
|
+
testAsync("today", (done) => {
|
85
|
+
const now = moment()
|
86
|
+
const el = addTimeEl({ type: "weekday-or-date", datetime: now.toISOString() })
|
87
|
+
defer(() => {
|
88
|
+
assert.equal(getText(el), "today")
|
89
|
+
done()
|
90
|
+
})
|
91
|
+
})
|
92
|
+
|
93
|
+
testAsync("yesterday", (done) => {
|
94
|
+
const yesterday = moment().subtract("days", 1)
|
95
|
+
const el = addTimeEl({ type: "weekday-or-date", datetime: yesterday.toISOString() })
|
96
|
+
defer(() => {
|
97
|
+
assert.equal(getText(el), "yesterday")
|
98
|
+
done()
|
99
|
+
})
|
100
|
+
})
|
101
|
+
|
102
|
+
testAsync("this week", (done) => {
|
103
|
+
const recent = moment().subtract("days", 3)
|
104
|
+
const el = addTimeEl({ type: "weekday-or-date", datetime: recent.toISOString() })
|
105
|
+
defer(() => {
|
106
|
+
assert.equal(getText(el), recent.format("dddd"))
|
107
|
+
done()
|
108
|
+
})
|
109
|
+
})
|
110
|
+
|
111
|
+
testAsync("before this week", (done) => {
|
112
|
+
stubNow(`${moment().year()}-03-20`, () => {
|
113
|
+
const before = moment().subtract("days", 8)
|
114
|
+
const el = addTimeEl({ type: "weekday-or-date", datetime: before.toISOString() })
|
115
|
+
defer(() => {
|
116
|
+
assert.equal(getText(el), before.format("MMM D"))
|
117
|
+
done()
|
118
|
+
})
|
119
|
+
})
|
120
|
+
})
|
121
|
+
})
|
@@ -0,0 +1,151 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
3
|
+
const { addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent } = LocalTime.TestHelpers
|
4
|
+
|
5
|
+
const momentMap = {
|
6
|
+
"%a": "ddd",
|
7
|
+
"%A": "dddd",
|
8
|
+
"%b": "MMM",
|
9
|
+
"%B": "MMMM",
|
10
|
+
"%c": "toString()",
|
11
|
+
"%d": "DD",
|
12
|
+
"%-d": "D",
|
13
|
+
"%e": "D",
|
14
|
+
"%H": "HH",
|
15
|
+
"%-H": "H",
|
16
|
+
"%I": "hh",
|
17
|
+
"%-I": "h",
|
18
|
+
"%l": "h",
|
19
|
+
"%m": "MM",
|
20
|
+
"%-m": "M",
|
21
|
+
"%M": "mm",
|
22
|
+
"%-M": "m",
|
23
|
+
"%p": "A",
|
24
|
+
"%P": "a",
|
25
|
+
"%S": "ss",
|
26
|
+
"%-S": "s",
|
27
|
+
"%w": "e",
|
28
|
+
"%y": "YY",
|
29
|
+
"%Y": "YYYY"
|
30
|
+
}
|
31
|
+
|
32
|
+
function stubDateToLocaleString(stubImplementation, callback) {
|
33
|
+
const original = Date.prototype.toLocaleString
|
34
|
+
Date.prototype.toLocaleString = stubImplementation
|
35
|
+
try {
|
36
|
+
return callback()
|
37
|
+
} finally {
|
38
|
+
Date.prototype.toLocaleString = original
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
function stubDateToString(stubImplementation, callback) {
|
43
|
+
const original = Date.prototype.toString
|
44
|
+
Date.prototype.toString = stubImplementation
|
45
|
+
try {
|
46
|
+
return callback()
|
47
|
+
} finally {
|
48
|
+
Date.prototype.toString = original
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
testGroup("strftime", () => {
|
53
|
+
for (let day = 0; day <= 30; day += 6) {
|
54
|
+
for (let hour = 0; hour <= 24; hour += 6) {
|
55
|
+
for (const format in momentMap) {
|
56
|
+
if (Object.prototype.hasOwnProperty.call(momentMap, format)) {
|
57
|
+
const momentFormat = momentMap[ format ]
|
58
|
+
|
59
|
+
test(`${format} (+${day} days, ${hour} hours)`, () => {
|
60
|
+
const now = moment().add("days", day).add("hours", hour)
|
61
|
+
const el = addTimeEl({ format, datetime: now.toISOString() })
|
62
|
+
LocalTime.process(el)
|
63
|
+
|
64
|
+
if (momentFormat.includes("toString()")) {
|
65
|
+
assert.equal(getText(el), now.toDate().toString())
|
66
|
+
} else {
|
67
|
+
assert.equal(getText(el), now.format(momentFormat))
|
68
|
+
}
|
69
|
+
})
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
test(`%Z Timezone (+${day} days, ${hour} hours)`, () => {
|
74
|
+
const now = moment().add("days", day).add("hours", hour)
|
75
|
+
const el = addTimeEl({ format: "%Z", datetime: now.toISOString() })
|
76
|
+
LocalTime.process(el)
|
77
|
+
|
78
|
+
const text = getText(el)
|
79
|
+
assert.ok(/^(\w{3,4}|UTC[\+\-]\d+)$/.test(text), `'${text}' doesn't look like a timezone. System date: '${new Date}'`)
|
80
|
+
})
|
81
|
+
}
|
82
|
+
}
|
83
|
+
})
|
84
|
+
|
85
|
+
testGroup("strftime time zones", () => {
|
86
|
+
for (var timeZone of Array.from(Object.keys(LocalTime.knownEdgeCaseTimeZones))) {
|
87
|
+
((timeZone => test(`edge-case time zone ${timeZone}`, () => {
|
88
|
+
const stub = () => `Thu Nov 30 2023 14:22:57 GMT-0000 (${timeZone})`
|
89
|
+
|
90
|
+
stubDateToLocaleString(stub, () => {
|
91
|
+
const el = addTimeEl({ format: "%Z", datetime: "2023-11-30T14:22:57Z" })
|
92
|
+
LocalTime.process(el)
|
93
|
+
|
94
|
+
assert.equal(getText(el), LocalTime.knownEdgeCaseTimeZones[timeZone])
|
95
|
+
})
|
96
|
+
})))(timeZone)
|
97
|
+
}
|
98
|
+
|
99
|
+
test("time zones Intl can abbreviate are parsed correctly", () => {
|
100
|
+
const stub = (_, options) => {
|
101
|
+
if (options.timeZoneName === "long") {
|
102
|
+
return "Thu Nov 30 2023 14:22:57 GMT-0800 (Alaska Daylight Time)" // not a known edge-case
|
103
|
+
} else if (options.timeZoneName === "short") {
|
104
|
+
return "11/30/2023, 2:22:57 PM AKDT" // possible to abbreviate
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
stubDateToLocaleString(stub, () => {
|
109
|
+
const el = addTimeEl({ format: "%Z", datetime: "2023-11-30T14:22:57Z" })
|
110
|
+
LocalTime.process(el)
|
111
|
+
|
112
|
+
assert.equal(getText(el), "AKDT")
|
113
|
+
})
|
114
|
+
})
|
115
|
+
|
116
|
+
test("time zones Intl can't abbreviate are parsed by our heuristic", () => {
|
117
|
+
const dateToStringStub = () => "Sat Dec 02 2023 17:20:26 GMT-0600 (Central Standard Time)"
|
118
|
+
const dateToLocaleStringStub = (_, options) => {
|
119
|
+
if (options.timeZoneName === "long") {
|
120
|
+
return "Thu Nov 30 2023 14:22:57 GMT+0700 (Central Twilight Time)" // not a known edge-case
|
121
|
+
} else if (options.timeZoneName === "short") {
|
122
|
+
return "11/30/2023, 2:22:57 PM GMT+7" // not possible to abbreviate
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
stubDateToString(dateToStringStub, () => stubDateToLocaleString(dateToLocaleStringStub, () => {
|
127
|
+
const el = addTimeEl({ format: "%Z", datetime: "2023-11-30T14:22:57Z" })
|
128
|
+
LocalTime.process(el)
|
129
|
+
|
130
|
+
assert.equal(getText(el), "CST")
|
131
|
+
}))
|
132
|
+
})
|
133
|
+
|
134
|
+
test("time zones Intl can't abbreviate and our heuristic can't parse display GMT offset", () => {
|
135
|
+
const dateToStringStub = () => ""
|
136
|
+
const dateToLocaleStringStub = (_, options) => {
|
137
|
+
if (options.timeZoneName === "long") {
|
138
|
+
return "Thu Nov 30 2023 14:22:57 GMT+0700 (Central Twilight Time)" // not a known edge-case
|
139
|
+
} else if (options.timeZoneName === "short") {
|
140
|
+
return "11/30/2023, 2:22:57 PM GMT+7" // not possible to abbreviate
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
stubDateToString(dateToStringStub, () => stubDateToLocaleString(dateToLocaleStringStub, () => {
|
145
|
+
const el = addTimeEl({ format: "%Z", datetime: "2023-11-30T14:22:57Z" })
|
146
|
+
LocalTime.process(el)
|
147
|
+
|
148
|
+
assert.equal(getText(el), "GMT+7")
|
149
|
+
}))
|
150
|
+
})
|
151
|
+
})
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import LocalTime from "local_time"
|
2
|
+
|
3
|
+
LocalTime.start()
|
4
|
+
|
5
|
+
LocalTime.TestHelpers = {
|
6
|
+
assert: QUnit.assert,
|
7
|
+
testGroup: QUnit.module,
|
8
|
+
test: QUnit.test,
|
9
|
+
|
10
|
+
testAsync(name, callback) {
|
11
|
+
QUnit.test(name, (assert) => {
|
12
|
+
const done = assert.async()
|
13
|
+
callback(done)
|
14
|
+
})
|
15
|
+
},
|
16
|
+
|
17
|
+
addTimeEl(param = {}) {
|
18
|
+
let { format, type, datetime, format24 } = param
|
19
|
+
if (!format) format = "%Y"
|
20
|
+
if (!type) type = "time"
|
21
|
+
if (!datetime) datetime = "2013-11-12T12:13:00Z"
|
22
|
+
|
23
|
+
const el = document.createElement("time")
|
24
|
+
el.setAttribute("data-local", type)
|
25
|
+
el.setAttribute("data-format", format)
|
26
|
+
el.setAttribute("datetime", datetime)
|
27
|
+
if (format24) el.setAttribute("data-format24", format24)
|
28
|
+
|
29
|
+
document.body.appendChild(el)
|
30
|
+
return el
|
31
|
+
},
|
32
|
+
|
33
|
+
setText(el, text) {
|
34
|
+
el.textContent = text
|
35
|
+
},
|
36
|
+
|
37
|
+
getText(el) {
|
38
|
+
// innerHTML works in all browsers so using it ensures we're
|
39
|
+
// reading the text content, not a potentially arbitrary property.
|
40
|
+
return el.innerHTML
|
41
|
+
},
|
42
|
+
|
43
|
+
getTitle(el) {
|
44
|
+
return el.getAttribute("title")
|
45
|
+
},
|
46
|
+
|
47
|
+
triggerEvent(name, el = document) {
|
48
|
+
const event = document.createEvent("Events")
|
49
|
+
event.initEvent(name, true, true)
|
50
|
+
el.dispatchEvent(event)
|
51
|
+
},
|
52
|
+
|
53
|
+
defer(callback) {
|
54
|
+
setTimeout(callback, 1)
|
55
|
+
},
|
56
|
+
|
57
|
+
stubNow(dateString, callback) {
|
58
|
+
const originalNow = moment.now
|
59
|
+
try {
|
60
|
+
moment.now = () => new Date(dateString).getTime()
|
61
|
+
callback()
|
62
|
+
} finally {
|
63
|
+
moment.now = originalNow
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|