@tim-code/my-util 0.0.7 → 0.0.9
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.
- package/package.json +1 -1
- package/src/time.js +40 -7
- package/src/time.test.js +152 -6
package/package.json
CHANGED
package/src/time.js
CHANGED
|
@@ -5,15 +5,20 @@ import { mod } from "./math.js"
|
|
|
5
5
|
* @param {Object} $1
|
|
6
6
|
* @param {boolean=} $1.floorMinute If true, floors to the nearest minute. If false, floors to the nearest second.
|
|
7
7
|
* @param {number=} $1.timestamp Unix timestamp to use instead of current time.
|
|
8
|
-
* @
|
|
8
|
+
* @param {string=} $1.timezone Timezone to use instead of Eastern time.
|
|
9
|
+
* @returns {Object} { timestamp, date, time, datetime }
|
|
9
10
|
*/
|
|
10
|
-
export function getEasternTime({
|
|
11
|
+
export function getEasternTime({
|
|
12
|
+
floorMinute = false,
|
|
13
|
+
timestamp = undefined,
|
|
14
|
+
timezone = "America/New_York",
|
|
15
|
+
} = {}) {
|
|
11
16
|
if (!timestamp) {
|
|
12
17
|
timestamp = new Date().getTime() / 1000
|
|
13
18
|
}
|
|
14
19
|
timestamp = floorMinute ? Math.floor(timestamp / 60) * 60 : Math.floor(timestamp)
|
|
15
20
|
const string = new Date(timestamp * 1000).toLocaleString("en-US", {
|
|
16
|
-
timeZone:
|
|
21
|
+
...(timezone ? { timeZone: timezone } : {}),
|
|
17
22
|
hour12: false,
|
|
18
23
|
year: "numeric",
|
|
19
24
|
month: "2-digit",
|
|
@@ -25,9 +30,19 @@ export function getEasternTime({ floorMinute = false, timestamp = undefined } =
|
|
|
25
30
|
const [americanDate, time] = string.split(", ")
|
|
26
31
|
const [month, day, year] = americanDate.split("/")
|
|
27
32
|
const date = [year, month, day].join("-")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
return { timestamp, date, time }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets various ways of representing the current time in local timezone. Floors to nearest second by default.
|
|
38
|
+
* @param {Object} $1
|
|
39
|
+
* @param {boolean=} $1.floorMinute If true, floors to the nearest minute. If false, floors to the nearest second.
|
|
40
|
+
* @param {number=} $1.timestamp Unix timestamp to use instead of current time.
|
|
41
|
+
* @param {string=} $1.timezone Timezone to use instead of local time.
|
|
42
|
+
* @returns {Object} { timestamp, date, time, minute, datetime }
|
|
43
|
+
*/
|
|
44
|
+
export function getTime({ floorMinute, timestamp, timezone = false }) {
|
|
45
|
+
return getEasternTime({ floorMinute, timestamp, timezone })
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
/**
|
|
@@ -79,7 +94,7 @@ export function isUnixTimestamp(ts, { max = 9999999999 } = {}) {
|
|
|
79
94
|
* @param {number} $1.minutes Minutes to add to time string
|
|
80
95
|
* @returns {string} HH:mm:ss
|
|
81
96
|
*/
|
|
82
|
-
export function addTime(timeString, { minutes = 0, hours = 0 }) {
|
|
97
|
+
export function addTime(timeString, { minutes = 0, hours = 0 } = {}) {
|
|
83
98
|
let [hour, minute, second = 0] = timeString.split(":").map(Number)
|
|
84
99
|
hour = mod(hour + hours, 24)
|
|
85
100
|
minute += minutes
|
|
@@ -109,3 +124,21 @@ export function addDays(dateString, days = 0) {
|
|
|
109
124
|
date.setUTCDate(date.getUTCDate() + days)
|
|
110
125
|
return date.toISOString().slice(0, 10)
|
|
111
126
|
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get all dates between two dates, with limit.
|
|
130
|
+
* @param {string} start
|
|
131
|
+
* @param {string} end
|
|
132
|
+
* @returns {Array}
|
|
133
|
+
*/
|
|
134
|
+
export function getDateRange(start, end, { limit = 1000 } = {}) {
|
|
135
|
+
const dates = []
|
|
136
|
+
while (start <= end) {
|
|
137
|
+
dates.push(start)
|
|
138
|
+
start = addDays(start, 1)
|
|
139
|
+
if (dates.length >= limit) {
|
|
140
|
+
break
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return dates
|
|
144
|
+
}
|
package/src/time.test.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import { describe, expect, test } from "@jest/globals"
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
addDays,
|
|
4
|
+
addTime,
|
|
5
|
+
getDateRange,
|
|
6
|
+
getEasternTime,
|
|
7
|
+
getTime,
|
|
8
|
+
isDate,
|
|
9
|
+
isTime,
|
|
10
|
+
isUnixTimestamp,
|
|
11
|
+
} from "./time.js"
|
|
12
|
+
|
|
13
|
+
// getEasternTime changed: now accepts a "timezone" param and no longer returns "minute" or "datetime"
|
|
5
14
|
describe("getEasternTime", () => {
|
|
6
15
|
test("returns correct structure and types", () => {
|
|
7
16
|
const result = getEasternTime()
|
|
8
17
|
expect(typeof result.timestamp).toBe("number")
|
|
9
18
|
expect(typeof result.date).toBe("string")
|
|
10
19
|
expect(typeof result.time).toBe("string")
|
|
11
|
-
|
|
12
|
-
expect(
|
|
20
|
+
// minute and datetime are no longer returned
|
|
21
|
+
expect(result).not.toHaveProperty("minute")
|
|
22
|
+
expect(result).not.toHaveProperty("datetime")
|
|
13
23
|
})
|
|
14
24
|
|
|
15
25
|
test("floors to minute if floorMinute is true", () => {
|
|
@@ -38,7 +48,46 @@ describe("getEasternTime", () => {
|
|
|
38
48
|
expect(def.date).toEqual(explicit.date)
|
|
39
49
|
expect(def.time).toEqual(explicit.time)
|
|
40
50
|
expect(def.timestamp).toEqual(explicit.timestamp)
|
|
41
|
-
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// New: test timezone override
|
|
54
|
+
test("respects timezone parameter", () => {
|
|
55
|
+
// 2024-06-01T12:34:56Z (UTC)
|
|
56
|
+
const ts = 1717245296
|
|
57
|
+
// New York (EDT, UTC-4)
|
|
58
|
+
const eastern = getEasternTime({ timestamp: ts, timezone: "America/New_York" })
|
|
59
|
+
// Los Angeles (PDT, UTC-7)
|
|
60
|
+
const pacific = getEasternTime({ timestamp: ts, timezone: "America/Los_Angeles" })
|
|
61
|
+
// UTC
|
|
62
|
+
const utc = getEasternTime({ timestamp: ts, timezone: "UTC" })
|
|
63
|
+
expect(eastern.time).not.toBe(pacific.time)
|
|
64
|
+
expect(eastern.time).not.toBe(utc.time)
|
|
65
|
+
expect(pacific.time).not.toBe(utc.time)
|
|
66
|
+
// Should match expected hour offset
|
|
67
|
+
expect(eastern.time.startsWith("08:34")).toBe(true) // EDT
|
|
68
|
+
expect(pacific.time.startsWith("05:34")).toBe(true) // PDT
|
|
69
|
+
expect(utc.time.startsWith("12:34")).toBe(true) // UTC
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test("returns local time if timezone is empty string", () => {
|
|
73
|
+
// If timezone is falsy (""), should use local time zone
|
|
74
|
+
// We'll compare to Date.toLocaleString with no timeZone option
|
|
75
|
+
const ts = 1717245296
|
|
76
|
+
const local = getEasternTime({ timestamp: ts, timezone: "" })
|
|
77
|
+
const expected = new Date(ts * 1000).toLocaleString("en-US", {
|
|
78
|
+
hour12: false,
|
|
79
|
+
year: "numeric",
|
|
80
|
+
month: "2-digit",
|
|
81
|
+
day: "2-digit",
|
|
82
|
+
hour: "2-digit",
|
|
83
|
+
minute: "2-digit",
|
|
84
|
+
second: "2-digit",
|
|
85
|
+
})
|
|
86
|
+
const [, time] = expected.split(", ")
|
|
87
|
+
const [month, day, year] = expected.split(", ")[0].split("/")
|
|
88
|
+
const date = [year, month, day].join("-")
|
|
89
|
+
expect(local.date).toBe(date)
|
|
90
|
+
expect(local.time).toBe(time)
|
|
42
91
|
})
|
|
43
92
|
|
|
44
93
|
// DST boundary tests
|
|
@@ -81,6 +130,53 @@ describe("getEasternTime", () => {
|
|
|
81
130
|
})
|
|
82
131
|
})
|
|
83
132
|
|
|
133
|
+
// New tests for getTime (newly exported function)
|
|
134
|
+
describe("getTime", () => {
|
|
135
|
+
test("returns same structure as getEasternTime", () => {
|
|
136
|
+
const result = getTime({})
|
|
137
|
+
expect(typeof result.timestamp).toBe("number")
|
|
138
|
+
expect(typeof result.date).toBe("string")
|
|
139
|
+
expect(typeof result.time).toBe("string")
|
|
140
|
+
expect(result).not.toHaveProperty("minute")
|
|
141
|
+
expect(result).not.toHaveProperty("datetime")
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test("defaults to local time if timezone is not provided", () => {
|
|
145
|
+
// getTime({}) should use timezone = false, which disables the timeZone option and uses local
|
|
146
|
+
const ts = 1717245296
|
|
147
|
+
const local = getTime({ timestamp: ts })
|
|
148
|
+
const expected = new Date(ts * 1000).toLocaleString("en-US", {
|
|
149
|
+
hour12: false,
|
|
150
|
+
year: "numeric",
|
|
151
|
+
month: "2-digit",
|
|
152
|
+
day: "2-digit",
|
|
153
|
+
hour: "2-digit",
|
|
154
|
+
minute: "2-digit",
|
|
155
|
+
second: "2-digit",
|
|
156
|
+
})
|
|
157
|
+
const [, time] = expected.split(", ")
|
|
158
|
+
const [month, day, year] = expected.split(", ")[0].split("/")
|
|
159
|
+
const date = [year, month, day].join("-")
|
|
160
|
+
expect(local.date).toBe(date)
|
|
161
|
+
expect(local.time).toBe(time)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test("passes timezone through to getEasternTime", () => {
|
|
165
|
+
// Should match getEasternTime with same timezone
|
|
166
|
+
const ts = 1717245296
|
|
167
|
+
const pacific = getTime({ timestamp: ts, timezone: "America/Los_Angeles" })
|
|
168
|
+
const ref = getEasternTime({ timestamp: ts, timezone: "America/Los_Angeles" })
|
|
169
|
+
expect(pacific).toEqual(ref)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test("floors to minute if floorMinute is true", () => {
|
|
173
|
+
const ts = 1717245296
|
|
174
|
+
const floored = getTime({ timestamp: ts, floorMinute: true })
|
|
175
|
+
expect(floored.timestamp % 60).toBe(0)
|
|
176
|
+
expect(floored.time.endsWith(":00")).toBe(true)
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
84
180
|
describe("isDate", () => {
|
|
85
181
|
test("returns true for valid YYYY-MM-DD dates", () => {
|
|
86
182
|
expect(isDate("2024-06-01")).toBe(true)
|
|
@@ -212,6 +308,12 @@ describe("addTime", () => {
|
|
|
212
308
|
test("handles midnight", () => {
|
|
213
309
|
expect(addTime("00:00:00", { hours: 0, minutes: 0 })).toBe("00:00:00")
|
|
214
310
|
})
|
|
311
|
+
|
|
312
|
+
// New: test default parameters (no options argument)
|
|
313
|
+
test("handles missing options argument (all defaults)", () => {
|
|
314
|
+
expect(addTime("12:34:56")).toBe("12:34:56")
|
|
315
|
+
expect(addTime("05:10")).toBe("05:10:00")
|
|
316
|
+
})
|
|
215
317
|
})
|
|
216
318
|
|
|
217
319
|
describe("addDays", () => {
|
|
@@ -264,3 +366,47 @@ describe("addDays", () => {
|
|
|
264
366
|
expect(addDays("2024-11-03", -1)).toBe("2024-11-02")
|
|
265
367
|
})
|
|
266
368
|
})
|
|
369
|
+
|
|
370
|
+
describe("getDateRange", () => {
|
|
371
|
+
test("returns all dates between start and end inclusive", () => {
|
|
372
|
+
expect(getDateRange("2024-06-01", "2024-06-03")).toEqual([
|
|
373
|
+
"2024-06-01",
|
|
374
|
+
"2024-06-02",
|
|
375
|
+
"2024-06-03",
|
|
376
|
+
])
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
test("returns just the start date if start equals end", () => {
|
|
380
|
+
expect(getDateRange("2024-06-01", "2024-06-01")).toEqual(["2024-06-01"])
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
test("returns empty array if start > end", () => {
|
|
384
|
+
expect(getDateRange("2024-06-03", "2024-06-01")).toEqual([])
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
test("respects limit option", () => {
|
|
388
|
+
expect(getDateRange("2024-06-01", "2024-06-10", { limit: 3 })).toEqual([
|
|
389
|
+
"2024-06-01",
|
|
390
|
+
"2024-06-02",
|
|
391
|
+
"2024-06-03",
|
|
392
|
+
])
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
test("returns at most 1000 dates by default", () => {
|
|
396
|
+
const dates = getDateRange("2020-01-01", "2025-01-01")
|
|
397
|
+
expect(dates.length).toBeLessThanOrEqual(1000)
|
|
398
|
+
expect(dates[0]).toBe("2020-01-01")
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
test("can return more than 1000 dates if limit is raised", () => {
|
|
402
|
+
const dates = getDateRange("2020-01-01", "2025-01-01", { limit: 2000 })
|
|
403
|
+
expect(dates.length).toBeGreaterThan(1000)
|
|
404
|
+
expect(dates[0]).toBe("2020-01-01")
|
|
405
|
+
expect(dates[dates.length - 1]).toBe("2025-01-01")
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
// ISSUE: getDateRange does not validate that start/end are valid dates, so invalid input may yield unexpected results.
|
|
409
|
+
test("handles invalid date input (returns empty array if start > end lexically)", () => {
|
|
410
|
+
expect(getDateRange("not-a-date", "2024-01-01")).toEqual([])
|
|
411
|
+
})
|
|
412
|
+
})
|