@tim-code/my-util 0.7.3 → 0.8.0

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.7.3",
3
+ "version": "0.8.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "Tim Sprowl",
package/src/time.js CHANGED
@@ -1,24 +1,34 @@
1
1
  import { mod } from "./math.js"
2
2
 
3
+ // `floorMinute` at first glance seems ugly and something like `floor: "minute"` seems better.
4
+ // However, practically and realistically, it doesn't seem like we need other values for floor besides "second" and "minute".
5
+ // So, then it seems more problematic to rely on an exact string to be passed... probably would need to throw on some other value.
6
+ // We don't really ever want to disable flooring because we want timestamp, date, and time returned to represent the same thing.
3
7
  /**
4
8
  * Gets various ways of representing the current time in EDT. Floors to nearest second by default.
5
9
  * @param {Object} $1
6
- * @param {boolean=} $1.floorMinute If true, floors to the nearest minute. If false, floors to the nearest second.
7
- * @param {number=} $1.timestamp Unix timestamp to use instead of current time.
8
- * @param {string=} $1.timeZone Time zone to use instead of Eastern time. A falsy value corresponds to local time.
10
+ * @param {number=} $1.timestamp Unix timestamp to use instead of now (in seconds).
11
+ * @param {Date=} $1.dateInstance Alternative to specifying timestamp that is a Date instance.
12
+ * This can be used to specify UTC: `getEasternTime({ dateInstance: new Date(utc) })`.
13
+ * @param {boolean=} $1.floorMinute If true, floors to the nearest minute. If false, floors to the nearest second (default).
14
+ * @param {string=} $1.timeZone Time zone to use instead of Eastern time. A falsy, not-undefined value corresponds to local time.
9
15
  * @returns {Object} { timestamp, date: YYYY-MM-DD, time: HH:mm:ss }
16
+ * If invalid timestamp or dateInstance specified: { timestamp: NaN|Infinity, date: "Invalid date", time: undefined }
10
17
  */
11
18
  export function getEasternTime({
19
+ dateInstance = undefined,
20
+ timestamp = (dateInstance ?? new Date()).getTime() / 1000,
12
21
  floorMinute = false,
13
- timestamp = undefined,
14
22
  timeZone = "America/New_York",
15
23
  } = {}) {
16
- if (!timestamp) {
17
- timestamp = new Date().getTime() / 1000
24
+ if (floorMinute) {
25
+ timestamp = Math.floor(timestamp / 60) * 60
26
+ } else {
27
+ timestamp = Math.floor(timestamp)
18
28
  }
19
- timestamp = floorMinute ? Math.floor(timestamp / 60) * 60 : Math.floor(timestamp)
29
+ const flooredDateInstance = new Date(timestamp * 1000)
20
30
  // 'en-CA' (English - Canada) formats dates as YYYY-MM-DD and times in 24-hour format by default
21
- const string = new Date(timestamp * 1000).toLocaleString("en-CA", {
31
+ const string = flooredDateInstance.toLocaleString("en-CA", {
22
32
  ...(timeZone ? { timeZone } : {}),
23
33
  hour12: false,
24
34
  year: "numeric",
@@ -35,13 +45,16 @@ export function getEasternTime({
35
45
  /**
36
46
  * Gets various ways of representing the current time in local timezone. Floors to nearest second by default.
37
47
  * @param {Object} $1
48
+ * @param {number=} $1.timestamp Unix timestamp to use instead of now (in seconds).
49
+ * @param {Date=} $1.dateInstance Alternative to specifying timestamp that is a Date instance.
50
+ * This can be used to specify UTC: `getEasternTime({ dateInstance: new Date(utc) })`.
38
51
  * @param {boolean=} $1.floorMinute If true, floors to the nearest minute. If false, floors to the nearest second.
39
- * @param {number=} $1.timestamp Unix timestamp to use instead of current time.
40
- * @param {string=} $1.timeZone Time zone to use instead of local time. A falsy value (default) corresponds to local time.
41
- * @returns {Object} { timestamp, date, time, minute, datetime }
52
+ * @param {string=} $1.timeZone Time zone to use instead of local time. A falsy value corresponds to local time (default) .
53
+ * @returns {Object} { timestamp, date: YYYY-MM-DD, time: HH:mm:ss }
54
+ * If invalid timestamp or dateInstance specified: { timestamp: NaN|Infinity, date: "Invalid date", time: undefined }
42
55
  */
43
- export function getTime({ floorMinute, timestamp, timeZone = false } = {}) {
44
- return getEasternTime({ floorMinute, timestamp, timeZone })
56
+ export function getTime({ timestamp, dateInstance, floorMinute, timeZone = false } = {}) {
57
+ return getEasternTime({ timestamp, dateInstance, floorMinute, timeZone })
45
58
  }
46
59
 
47
60
  /**
@@ -107,7 +120,9 @@ export function isDateString(string) {
107
120
  }
108
121
 
109
122
  /**
110
- * Checks if the string represent a valid HH:mm:ss time.
123
+ * Checks if the string represents a valid HH:mm:ss time.
124
+ * This will return false for times like "24:00:00".
125
+ * Does not handle milliseconds.
111
126
  * @param {string} string
112
127
  * @returns {boolean}
113
128
  */
@@ -115,6 +130,33 @@ export function isTimeString(string) {
115
130
  return /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$/u.test(string)
116
131
  }
117
132
 
133
+ /**
134
+ * Checks if the string represents a valid date time string similar to YYYY-MM-DDTHH:mm:ss.
135
+ * Does not handle milliseconds.
136
+ * @param {string} string
137
+ * @param {Object} $1
138
+ * @param {string=} $1.separator Can specify a different string separator between date and time. Defaults to "T".
139
+ * @returns {boolean}
140
+ */
141
+ export function isDateTimeString(string, { separator = "T" } = {}) {
142
+ const [date, time] = string.split(separator)
143
+ return isDateString(date) && isTimeString(time)
144
+ }
145
+
146
+ /**
147
+ * Checks if the string represents a valid UTC date time similar to YYYY-MM-DDTHH:mm:ssZ.
148
+ * Does not handle milliseconds or UTC offsets.
149
+ * @param {string} string
150
+ * @returns {boolean}
151
+ */
152
+ export function isUTCString(string) {
153
+ if (!string.endsWith("Z")) {
154
+ return false
155
+ }
156
+ const datetime = string.slice(0, string.length - 1)
157
+ return isDateTimeString(datetime)
158
+ }
159
+
118
160
  /**
119
161
  * Checks if a number is a Unix timestamp (i.e. in seconds).
120
162
  * Would not validate timestamps set very far into the future.
package/src/time.test.js CHANGED
@@ -11,11 +11,18 @@ import {
11
11
  getTimeRange,
12
12
  getUnixTimestamp,
13
13
  isDateString,
14
+ isDateTimeString,
14
15
  isTimeString,
16
+ isUTCString,
15
17
  isUnixTimestamp,
16
18
  today,
17
19
  } from "./time.js"
18
20
 
21
+ // Exported functions:
22
+ // getEasternTime, getTime, getUnixTimestamp, today, getDayIndexInWeek, getMinute,
23
+ // isDateString, isTimeString, isDateTimeString, isUTCString, isUnixTimestamp,
24
+ // addTime, getTimeRange, addDays, getDateRange, getStartOfWeek
25
+
19
26
  describe("getEasternTime", () => {
20
27
  test("returns correct structure and types", () => {
21
28
  const result = getEasternTime()
@@ -46,6 +53,22 @@ describe("getEasternTime", () => {
46
53
  expect(floored.time.endsWith(":00")).toBe(true)
47
54
  })
48
55
 
56
+ test("accepts dateInstance and matches equivalent timestamp", () => {
57
+ const ts = 1717245296 // 2024-06-01T12:34:56Z
58
+ const di = new Date(ts * 1000)
59
+ const a = getEasternTime({ dateInstance: di, timeZone: "UTC" })
60
+ const b = getEasternTime({ timestamp: ts, timeZone: "UTC" })
61
+ expect(a).toEqual(b)
62
+ })
63
+
64
+ test("dateInstance path respects floorMinute", () => {
65
+ const ts = 1717245296 // ...:56
66
+ const di = new Date(ts * 1000)
67
+ const floored = getEasternTime({ dateInstance: di, floorMinute: true, timeZone: "UTC" })
68
+ expect(floored.timestamp % 60).toBe(0)
69
+ expect(floored.time).toBe("12:34:00")
70
+ })
71
+
49
72
  test("default parameters yield consistent output", () => {
50
73
  const def = getEasternTime()
51
74
  const explicit = getEasternTime({ floorMinute: false })
@@ -169,6 +192,14 @@ describe("getTime", () => {
169
192
  expect(pacific).toEqual(ref)
170
193
  })
171
194
 
195
+ test("accepts dateInstance and matches equivalent timestamp", () => {
196
+ const ts = 1717245296
197
+ const di = new Date(ts * 1000)
198
+ const a = getTime({ dateInstance: di, timeZone: "UTC" })
199
+ const b = getTime({ timestamp: ts, timeZone: "UTC" })
200
+ expect(a).toEqual(b)
201
+ })
202
+
172
203
  test("floors to minute if floorMinute is true", () => {
173
204
  const ts = 1717245296
174
205
  const floored = getTime({ timestamp: ts, floorMinute: true })
@@ -233,7 +264,6 @@ describe("getDayIndexInWeek", () => {
233
264
  })
234
265
  })
235
266
 
236
- // New tests for getMinute
237
267
  describe("getMinute", () => {
238
268
  test("extracts minute from HH:mm:ss", () => {
239
269
  expect(getMinute("12:34:56")).toBe(34)
@@ -278,6 +308,42 @@ describe("isTimeString", () => {
278
308
  })
279
309
  })
280
310
 
311
+ describe("isDateTimeString", () => {
312
+ test("valid with default T separator", () => {
313
+ expect(isDateTimeString("2024-06-01T12:34:56")).toBe(true)
314
+ })
315
+
316
+ test("rejects invalid combinations", () => {
317
+ expect(isDateTimeString("2024-06-01T12:34")).toBe(false) // missing seconds
318
+ expect(isDateTimeString("2024-06-01T24:00:00")).toBe(false) // invalid hour
319
+ expect(isDateTimeString("2024-02-31T12:00:00")).toBe(false) // invalid date
320
+ expect(isDateTimeString("2024-06-01 12:34:56")).toBe(false) // wrong separator
321
+ })
322
+
323
+ test("supports custom separator", () => {
324
+ expect(isDateTimeString("2024-06-01 12:34:56", { separator: " " })).toBe(true)
325
+ expect(isDateTimeString("2024-06-01T12:34:56", { separator: " " })).toBe(false)
326
+ })
327
+ })
328
+
329
+ describe("isUTCString", () => {
330
+ test("validates proper Zulu UTC format", () => {
331
+ expect(isUTCString("2024-06-01T12:34:56Z")).toBe(true)
332
+ })
333
+
334
+ test("rejects strings without trailing Z", () => {
335
+ expect(isUTCString("2024-06-01T12:34:56")).toBe(false)
336
+ })
337
+
338
+ test("rejects invalid time/date and unsupported variants", () => {
339
+ expect(isUTCString("2024-06-01T24:00:00Z")).toBe(false) // invalid hour
340
+ expect(isUTCString("2024-02-31T12:00:00Z")).toBe(false) // invalid date
341
+ expect(isUTCString("2024-06-01T12:34:56+00:00")).toBe(false) // offset not supported
342
+ expect(isUTCString("2024-06-01T12:34:56.123Z")).toBe(false) // milliseconds not supported
343
+ expect(isUTCString("2024-06-01 12:34:56Z")).toBe(false) // wrong separator
344
+ })
345
+ })
346
+
281
347
  describe("isUnixTimestamp", () => {
282
348
  test("returns true for valid unix timestamps in seconds", () => {
283
349
  expect(isUnixTimestamp(0)).toBe(true)
@@ -494,7 +560,6 @@ describe("getDateRange", () => {
494
560
  })
495
561
  })
496
562
 
497
- // Tests for getStartOfWeek (newly exported function)
498
563
  describe("getStartOfWeek", () => {
499
564
  test("returns same date if input is Sunday", () => {
500
565
  expect(getStartOfWeek("2024-06-02")).toBe("2024-06-02") // Sunday