@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tim-code/my-util",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "",
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
- * @returns {Object} { timestamp, date, time, minute, datetime }
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({ floorMinute = false, timestamp = undefined } = {}) {
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: "America/New_York",
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
- const minute = parseInt(time.split(":")[1], 10)
29
- const datetime = `${date} ${time}`
30
- return { timestamp, date, time, minute, datetime }
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 { addDays, addTime, getEasternTime, isDate, isTime, isUnixTimestamp } from "./time.js"
3
-
4
- // getEasternTime changed: removed "days" param, added "timestamp" param
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
- expect(typeof result.minute).toBe("number")
12
- expect(typeof result.datetime).toBe("string")
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
- expect(def.datetime).toEqual(explicit.datetime)
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
+ })