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