@discomedia/utils 1.0.19 → 1.0.20

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/dist/index.cjs CHANGED
@@ -162,6 +162,11 @@ const MARKET_CONFIG = {
162
162
  },
163
163
  };
164
164
  // Helper: Get NY offset for a given UTC date (DST rules for US)
165
+ /**
166
+ * Returns the NY timezone offset (in hours) for a given UTC date, accounting for US DST rules.
167
+ * @param date - UTC date
168
+ * @returns offset in hours (-5 for EST, -4 for EDT)
169
+ */
165
170
  function getNYOffset(date) {
166
171
  // US DST starts 2nd Sunday in March, ends 1st Sunday in November
167
172
  const year = date.getUTCFullYear();
@@ -174,6 +179,14 @@ function getNYOffset(date) {
174
179
  return MARKET_CONFIG.UTC_OFFSET_STANDARD;
175
180
  }
176
181
  // Helper: Get nth weekday of month in UTC
182
+ /**
183
+ * Returns the nth weekday of a given month in UTC.
184
+ * @param year - Year
185
+ * @param month - 1-based month (e.g. March = 3)
186
+ * @param weekday - 0=Sunday, 1=Monday, ...
187
+ * @param n - nth occurrence
188
+ * @returns Date object for the nth weekday
189
+ */
177
190
  function getNthWeekdayOfMonth(year, month, weekday, n) {
178
191
  let count = 0;
179
192
  for (let d = 1; d <= 31; d++) {
@@ -190,6 +203,11 @@ function getNthWeekdayOfMonth(year, month, weekday, n) {
190
203
  return new Date(Date.UTC(year, month - 1, 28));
191
204
  }
192
205
  // Helper: Convert UTC date to NY time (returns new Date object)
206
+ /**
207
+ * Converts a UTC date to NY time (returns a new Date object).
208
+ * @param date - UTC date
209
+ * @returns Date object in NY time
210
+ */
193
211
  function toNYTime(date) {
194
212
  const offset = getNYOffset(date);
195
213
  // NY offset in hours
@@ -198,6 +216,11 @@ function toNYTime(date) {
198
216
  return new Date(nyMillis);
199
217
  }
200
218
  // Helper: Convert NY time to UTC (returns new Date object)
219
+ /**
220
+ * Converts a NY time date to UTC (returns a new Date object).
221
+ * @param date - NY time date
222
+ * @returns Date object in UTC
223
+ */
201
224
  function fromNYTime(date) {
202
225
  const offset = getNYOffset(date);
203
226
  const nyMillis = date.getTime();
@@ -205,6 +228,12 @@ function fromNYTime(date) {
205
228
  return new Date(utcMillis);
206
229
  }
207
230
  // Helper: Format date in ISO, unix, etc.
231
+ /**
232
+ * Formats a date in ISO, unix-seconds, or unix-ms format.
233
+ * @param date - Date object
234
+ * @param outputFormat - Output format ('iso', 'unix-seconds', 'unix-ms')
235
+ * @returns Formatted date string or number
236
+ */
208
237
  function formatDate(date, outputFormat = 'iso') {
209
238
  switch (outputFormat) {
210
239
  case 'unix-seconds':
@@ -217,15 +246,33 @@ function formatDate(date, outputFormat = 'iso') {
217
246
  }
218
247
  }
219
248
  // Helper: Format date in NY locale string
249
+ /**
250
+ * Formats a date in NY locale string.
251
+ * @param date - Date object
252
+ * @returns NY locale string
253
+ */
220
254
  function formatNYLocale(date) {
221
255
  return date.toLocaleString('en-US', { timeZone: 'America/New_York' });
222
256
  }
223
257
  // Market calendar logic
258
+ /**
259
+ * Market calendar logic for holidays, weekends, and market days.
260
+ */
224
261
  class MarketCalendar {
262
+ /**
263
+ * Checks if a date is a weekend in NY time.
264
+ * @param date - Date object
265
+ * @returns true if weekend, false otherwise
266
+ */
225
267
  isWeekend(date) {
226
268
  const day = toNYTime(date).getUTCDay();
227
269
  return day === 0 || day === 6;
228
270
  }
271
+ /**
272
+ * Checks if a date is a market holiday in NY time.
273
+ * @param date - Date object
274
+ * @returns true if holiday, false otherwise
275
+ */
229
276
  isHoliday(date) {
230
277
  const nyDate = toNYTime(date);
231
278
  const year = nyDate.getUTCFullYear();
@@ -237,6 +284,11 @@ class MarketCalendar {
237
284
  return false;
238
285
  return Object.values(yearHolidays).some(holiday => holiday.date === formattedDate);
239
286
  }
287
+ /**
288
+ * Checks if a date is an early close day in NY time.
289
+ * @param date - Date object
290
+ * @returns true if early close, false otherwise
291
+ */
240
292
  isEarlyCloseDay(date) {
241
293
  const nyDate = toNYTime(date);
242
294
  const year = nyDate.getUTCFullYear();
@@ -246,6 +298,11 @@ class MarketCalendar {
246
298
  const yearEarlyCloses = marketEarlyCloses[year];
247
299
  return yearEarlyCloses && yearEarlyCloses[formattedDate] !== undefined;
248
300
  }
301
+ /**
302
+ * Gets the early close time (in minutes from midnight) for a given date.
303
+ * @param date - Date object
304
+ * @returns minutes from midnight or null
305
+ */
249
306
  getEarlyCloseTime(date) {
250
307
  const nyDate = toNYTime(date);
251
308
  const year = nyDate.getUTCFullYear();
@@ -259,9 +316,19 @@ class MarketCalendar {
259
316
  }
260
317
  return null;
261
318
  }
319
+ /**
320
+ * Checks if a date is a market day (not weekend or holiday).
321
+ * @param date - Date object
322
+ * @returns true if market day, false otherwise
323
+ */
262
324
  isMarketDay(date) {
263
325
  return !this.isWeekend(date) && !this.isHoliday(date);
264
326
  }
327
+ /**
328
+ * Gets the next market day after the given date.
329
+ * @param date - Date object
330
+ * @returns Date object for next market day
331
+ */
265
332
  getNextMarketDay(date) {
266
333
  let nextDay = new Date(date.getTime() + 24 * 60 * 60 * 1000);
267
334
  while (!this.isMarketDay(nextDay)) {
@@ -269,6 +336,11 @@ class MarketCalendar {
269
336
  }
270
337
  return nextDay;
271
338
  }
339
+ /**
340
+ * Gets the previous market day before the given date.
341
+ * @param date - Date object
342
+ * @returns Date object for previous market day
343
+ */
272
344
  getPreviousMarketDay(date) {
273
345
  let prevDay = new Date(date.getTime() - 24 * 60 * 60 * 1000);
274
346
  while (!this.isMarketDay(prevDay)) {
@@ -278,6 +350,11 @@ class MarketCalendar {
278
350
  }
279
351
  }
280
352
  // Market open/close times
353
+ /**
354
+ * Returns market open/close times for a given date, including extended and early closes.
355
+ * @param date - Date object
356
+ * @returns MarketOpenCloseResult
357
+ */
281
358
  function getMarketTimes(date) {
282
359
  const calendar = new MarketCalendar();
283
360
  const nyDate = toNYTime(date);
@@ -315,6 +392,12 @@ function getMarketTimes(date) {
315
392
  };
316
393
  }
317
394
  // Is within market hours
395
+ /**
396
+ * Checks if a date/time is within market hours, extended hours, or continuous.
397
+ * @param date - Date object
398
+ * @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
399
+ * @returns true if within hours, false otherwise
400
+ */
318
401
  function isWithinMarketHoursImpl(date, intradayReporting = 'market_hours') {
319
402
  const calendar = new MarketCalendar();
320
403
  if (!calendar.isMarketDay(date))
@@ -343,6 +426,11 @@ function isWithinMarketHoursImpl(date, intradayReporting = 'market_hours') {
343
426
  }
344
427
  }
345
428
  // Get last full trading date
429
+ /**
430
+ * Returns the last full trading date (market close) for a given date.
431
+ * @param currentDate - Date object (default: now)
432
+ * @returns Date object for last full trading date
433
+ */
346
434
  function getLastFullTradingDateImpl(currentDate = new Date()) {
347
435
  const calendar = new MarketCalendar();
348
436
  const nyDate = toNYTime(currentDate);
@@ -375,6 +463,12 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
375
463
  return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
376
464
  }
377
465
  // Get day boundaries
466
+ /**
467
+ * Returns the start and end boundaries for a market day, extended hours, or continuous.
468
+ * @param date - Date object
469
+ * @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
470
+ * @returns Object with start and end Date
471
+ */
378
472
  function getDayBoundaries(date, intradayReporting = 'market_hours') {
379
473
  const calendar = new MarketCalendar();
380
474
  const nyDate = toNYTime(date);
@@ -410,6 +504,12 @@ function getDayBoundaries(date, intradayReporting = 'market_hours') {
410
504
  return { start, end };
411
505
  }
412
506
  // Period calculator
507
+ /**
508
+ * Calculates the start date for a given period ending at endDate.
509
+ * @param endDate - Date object
510
+ * @param period - Period string
511
+ * @returns Date object for period start
512
+ */
413
513
  function calculatePeriodStartDate(endDate, period) {
414
514
  const calendar = new MarketCalendar();
415
515
  let startDate;
@@ -451,6 +551,11 @@ function calculatePeriodStartDate(endDate, period) {
451
551
  return startDate;
452
552
  }
453
553
  // Get market time period
554
+ /**
555
+ * Returns the start and end dates for a market time period.
556
+ * @param params - MarketTimeParams
557
+ * @returns PeriodDates object
558
+ */
454
559
  function getMarketTimePeriod(params) {
455
560
  const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso', } = params;
456
561
  if (!period)
@@ -497,6 +602,11 @@ function getMarketTimePeriod(params) {
497
602
  };
498
603
  }
499
604
  // Market status
605
+ /**
606
+ * Returns the current market status for a given date.
607
+ * @param date - Date object (default: now)
608
+ * @returns MarketStatus object
609
+ */
500
610
  function getMarketStatusImpl(date = new Date()) {
501
611
  const calendar = new MarketCalendar();
502
612
  const nyDate = toNYTime(date);
@@ -567,10 +677,20 @@ function getMarketStatusImpl(date = new Date()) {
567
677
  };
568
678
  }
569
679
  // API exports
680
+ /**
681
+ * Returns market open/close times for a given date.
682
+ * @param options - { date?: Date }
683
+ * @returns MarketOpenCloseResult
684
+ */
570
685
  function getMarketOpenClose(options = {}) {
571
686
  const { date = new Date() } = options;
572
687
  return getMarketTimes(date);
573
688
  }
689
+ /**
690
+ * Returns the start and end dates for a market time period as Date objects.
691
+ * @param params - MarketTimeParams
692
+ * @returns Object with start and end Date
693
+ */
574
694
  function getStartAndEndDates(params = {}) {
575
695
  const { start, end } = getMarketTimePeriod(params);
576
696
  return {
@@ -581,9 +701,19 @@ function getStartAndEndDates(params = {}) {
581
701
  /**
582
702
  * Returns the last full trading date as a Date object.
583
703
  */
704
+ /**
705
+ * Returns the last full trading date as a Date object.
706
+ * @param currentDate - Date object (default: now)
707
+ * @returns Date object for last full trading date
708
+ */
584
709
  function getLastFullTradingDate(currentDate = new Date()) {
585
710
  return getLastFullTradingDateImpl(currentDate);
586
711
  }
712
+ /**
713
+ * Returns the next market day after the reference date.
714
+ * @param referenceDate - Date object (default: now)
715
+ * @returns Object with date, yyyymmdd string, and ISO string
716
+ */
587
717
  function getNextMarketDay({ referenceDate } = {}) {
588
718
  const calendar = new MarketCalendar();
589
719
  const startDate = referenceDate || new Date();
@@ -600,19 +730,40 @@ function getNextMarketDay({ referenceDate } = {}) {
600
730
  * @param time - a string, number (unix timestamp), or Date object representing the time
601
731
  * @returns the trading date as a string in YYYY-MM-DD format
602
732
  */
733
+ /**
734
+ * Returns the trading date for a given time in YYYY-MM-DD format (NY time).
735
+ * @param time - string, number, or Date
736
+ * @returns trading date string
737
+ */
603
738
  function getTradingDate(time) {
604
739
  const date = typeof time === 'number' ? new Date(time) : typeof time === 'string' ? new Date(time) : time;
605
740
  const nyDate = toNYTime(date);
606
741
  return `${nyDate.getUTCFullYear()}-${String(nyDate.getUTCMonth() + 1).padStart(2, '0')}-${String(nyDate.getUTCDate()).padStart(2, '0')}`;
607
742
  }
743
+ /**
744
+ * Returns the NY timezone offset string for a given date.
745
+ * @param date - Date object (default: now)
746
+ * @returns '-04:00' for EDT, '-05:00' for EST
747
+ */
608
748
  function getNYTimeZone(date) {
609
749
  const offset = getNYOffset(date || new Date());
610
750
  return offset === -4 ? '-04:00' : '-05:00';
611
751
  }
752
+ /**
753
+ * Returns the current market status for a given date.
754
+ * @param options - { date?: Date }
755
+ * @returns MarketStatus object
756
+ */
612
757
  function getMarketStatus(options = {}) {
613
758
  const { date = new Date() } = options;
614
759
  return getMarketStatusImpl(date);
615
760
  }
761
+ /**
762
+ * Checks if a date/time is within market hours, extended hours, or continuous.
763
+ * @param date - Date object
764
+ * @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
765
+ * @returns true if within hours, false otherwise
766
+ */
616
767
  function isWithinMarketHours(date, intradayReporting = 'market_hours') {
617
768
  return isWithinMarketHoursImpl(date, intradayReporting);
618
769
  }
@@ -621,6 +772,11 @@ function isWithinMarketHours(date, intradayReporting = 'market_hours') {
621
772
  * endDate is always the most recent market close (previous day's close if before open, today's close if after open).
622
773
  * days: 1 or not specified = that day's open; 2 = previous market day's open, etc.
623
774
  */
775
+ /**
776
+ * Returns full trading days from market open to market close.
777
+ * @param options - { endDate?: Date, days?: number }
778
+ * @returns Object with startDate and endDate
779
+ */
624
780
  function getTradingStartAndEndDates(options = {}) {
625
781
  const { endDate = new Date(), days = 1 } = options;
626
782
  const calendar = new MarketCalendar();
@@ -656,6 +812,76 @@ function getTradingStartAndEndDates(options = {}) {
656
812
  const startOpen = getMarketOpenClose({ date: startMarketDay }).open;
657
813
  return { startDate: startOpen, endDate: endClose };
658
814
  }
815
+ /**
816
+ * Counts trading time between two dates (passed as standard Date objects), excluding weekends and holidays, and closed market hours, using other functions in this library.
817
+ *
818
+ * This function calculates the actual trading time between two dates by:
819
+ * 1. Iterating through each calendar day between startDate and endDate (inclusive)
820
+ * 2. For each day that is a market day (not weekend/holiday), getting market open/close times
821
+ * 3. Calculating the overlap between the time range and market hours for that day
822
+ * 4. Summing up all the trading minutes across all days
823
+ *
824
+ * The function automatically handles:
825
+ * - Weekends (Saturday/Sunday) - skipped entirely
826
+ * - Market holidays - skipped entirely
827
+ * - Early close days (e.g. day before holidays) - uses early close time
828
+ * - Times outside market hours - only counts time within 9:30am-4pm ET (or early close)
829
+ *
830
+ * Examples:
831
+ * - 12pm to 3:30pm same day = 3.5 hours = 210 minutes = 0.54 days
832
+ * - 9:30am to 4pm same day = 6.5 hours = 390 minutes = 1 day
833
+ * - Friday 2pm to Monday 2pm = 6.5 hours (Friday 2pm-4pm + Monday 9:30am-2pm)
834
+ *
835
+ * @param startDate - Start date/time
836
+ * @param endDate - End date/time (default: now)
837
+ * @returns Object containing:
838
+ * - days: Trading time as fraction of full trading days (6.5 hours = 1 day)
839
+ * - hours: Trading time in hours
840
+ * - minutes: Trading time in minutes
841
+ */
842
+ function countTradingDays(startDate, endDate = new Date()) {
843
+ const calendar = new MarketCalendar();
844
+ // Ensure start is before end
845
+ if (startDate.getTime() > endDate.getTime()) {
846
+ throw new Error('Start date must be before end date');
847
+ }
848
+ let totalMinutes = 0;
849
+ // Get the NY dates for iteration
850
+ const startNY = toNYTime(startDate);
851
+ const endNY = toNYTime(endDate);
852
+ // Create date at start of first day (in NY time)
853
+ const currentNY = new Date(Date.UTC(startNY.getUTCFullYear(), startNY.getUTCMonth(), startNY.getUTCDate(), 0, 0, 0, 0));
854
+ // Iterate through each calendar day
855
+ while (currentNY.getTime() <= endNY.getTime()) {
856
+ const currentUTC = fromNYTime(currentNY);
857
+ // Check if this is a market day
858
+ if (calendar.isMarketDay(currentUTC)) {
859
+ // Get market hours for this day
860
+ const marketTimes = getMarketTimes(currentUTC);
861
+ if (marketTimes.marketOpen && marketTimes.open && marketTimes.close) {
862
+ // Calculate the overlap between our time range and market hours
863
+ const dayStart = Math.max(startDate.getTime(), marketTimes.open.getTime());
864
+ const dayEnd = Math.min(endDate.getTime(), marketTimes.close.getTime());
865
+ // Only count if there's actual overlap
866
+ if (dayStart < dayEnd) {
867
+ totalMinutes += (dayEnd - dayStart) / (1000 * 60);
868
+ }
869
+ }
870
+ }
871
+ // Move to next day
872
+ currentNY.setUTCDate(currentNY.getUTCDate() + 1);
873
+ }
874
+ // Convert to days, hours, minutes
875
+ const MINUTES_PER_TRADING_DAY = 390; // 6.5 hours
876
+ const days = totalMinutes / MINUTES_PER_TRADING_DAY;
877
+ const hours = totalMinutes / 60;
878
+ const minutes = totalMinutes;
879
+ return {
880
+ days: Math.round(days * 1000) / 1000, // Round to 3 decimal places
881
+ hours: Math.round(hours * 100) / 100, // Round to 2 decimal places
882
+ minutes: Math.round(minutes)
883
+ };
884
+ }
659
885
  // Export MARKET_TIMES for compatibility
660
886
  const MARKET_TIMES = {
661
887
  TIMEZONE: MARKET_CONFIG.TIMEZONE,
@@ -1083,6 +1309,12 @@ class Queue {
1083
1309
  current = current.next;
1084
1310
  }
1085
1311
  }
1312
+
1313
+ * drain() {
1314
+ while (this.#head) {
1315
+ yield this.dequeue();
1316
+ }
1317
+ }
1086
1318
  }
1087
1319
 
1088
1320
  function pLimit(concurrency) {
@@ -2112,7 +2344,7 @@ const safeJSON = (text) => {
2112
2344
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2113
2345
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2114
2346
 
2115
- const VERSION = '5.10.1'; // x-release-please-version
2347
+ const VERSION = '5.10.2'; // x-release-please-version
2116
2348
 
2117
2349
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2118
2350
  const isRunningInBrowser = () => {
@@ -15232,7 +15464,7 @@ var config = {};
15232
15464
 
15233
15465
  var main = {exports: {}};
15234
15466
 
15235
- var version = "17.2.0";
15467
+ var version = "17.2.1";
15236
15468
  var require$$4 = {
15237
15469
  version: version};
15238
15470
 
@@ -15251,9 +15483,12 @@ function requireMain () {
15251
15483
 
15252
15484
  // Array of tips to display randomly
15253
15485
  const TIPS = [
15254
- '🔐 encrypt with dotenvx: https://dotenvx.com',
15486
+ '🔐 encrypt with Dotenvx: https://dotenvx.com',
15255
15487
  '🔐 prevent committing .env to code: https://dotenvx.com/precommit',
15256
15488
  '🔐 prevent building .env in docker: https://dotenvx.com/prebuild',
15489
+ '📡 observe env with Radar: https://dotenvx.com/radar',
15490
+ '📡 auto-backup env with Radar: https://dotenvx.com/radar',
15491
+ '📡 version env with Radar: https://dotenvx.com/radar',
15257
15492
  '🛠️ run anywhere with `dotenvx run -- yourcommand`',
15258
15493
  '⚙️ specify custom .env file path with { path: \'/custom/path/.env\' }',
15259
15494
  '⚙️ enable debug logging with { debug: true }',
@@ -15554,7 +15789,7 @@ function requireMain () {
15554
15789
  }
15555
15790
  }
15556
15791
 
15557
- _log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`(tip: ${_getRandomTip()})`)}`);
15792
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`-- tip: ${_getRandomTip()}`)}`);
15558
15793
  }
15559
15794
 
15560
15795
  if (lastError) {
@@ -18039,6 +18274,7 @@ const disco = {
18039
18274
  getNYTimeZone: getNYTimeZone,
18040
18275
  getTradingDate: getTradingDate,
18041
18276
  getTradingStartAndEndDates: getTradingStartAndEndDates,
18277
+ countTradingDays: countTradingDays,
18042
18278
  MARKET_TIMES: MARKET_TIMES,
18043
18279
  },
18044
18280
  utils: {