@discomedia/utils 1.0.18 → 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/README.md CHANGED
@@ -12,6 +12,11 @@ NPM repo: https://www.npmjs.com/package/@discomedia/utils
12
12
  npm install @discomedia/utils
13
13
  ```
14
14
 
15
+ > **Note:**
16
+ > The [`tslib`](https://www.npmjs.com/package/tslib) package is required for building this library on GitHub Actions and other CI environments.
17
+ > It is included as a dependency because Rollup and TypeScript emit helpers that require `tslib` at build time.
18
+ > Do not remove `tslib` from dependencies, or CI builds will fail.
19
+
15
20
  ## Usage
16
21
 
17
22
  This package provides two different entry points depending on your environment:
@@ -13,8 +13,6 @@ var require$$0$1 = require('buffer');
13
13
  var require$$0$4 = require('fs');
14
14
  var require$$1$1 = require('path');
15
15
  var require$$2$1 = require('os');
16
- var dateFns = require('date-fns');
17
- var dateFnsTz = require('date-fns-tz');
18
16
 
19
17
  var Types = /*#__PURE__*/Object.freeze({
20
18
  __proto__: null
@@ -251,7 +249,7 @@ const safeJSON = (text) => {
251
249
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
252
250
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
253
251
 
254
- const VERSION = '5.10.1'; // x-release-please-version
252
+ const VERSION = '5.10.2'; // x-release-please-version
255
253
 
256
254
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
257
255
  const isRunningInBrowser = () => {
@@ -12856,7 +12854,7 @@ var config = {};
12856
12854
 
12857
12855
  var main = {exports: {}};
12858
12856
 
12859
- var version = "17.2.0";
12857
+ var version = "17.2.1";
12860
12858
  var require$$4 = {
12861
12859
  version: version};
12862
12860
 
@@ -12875,9 +12873,12 @@ function requireMain () {
12875
12873
 
12876
12874
  // Array of tips to display randomly
12877
12875
  const TIPS = [
12878
- '🔐 encrypt with dotenvx: https://dotenvx.com',
12876
+ '🔐 encrypt with Dotenvx: https://dotenvx.com',
12879
12877
  '🔐 prevent committing .env to code: https://dotenvx.com/precommit',
12880
12878
  '🔐 prevent building .env in docker: https://dotenvx.com/prebuild',
12879
+ '📡 observe env with Radar: https://dotenvx.com/radar',
12880
+ '📡 auto-backup env with Radar: https://dotenvx.com/radar',
12881
+ '📡 version env with Radar: https://dotenvx.com/radar',
12881
12882
  '🛠️ run anywhere with `dotenvx run -- yourcommand`',
12882
12883
  '⚙️ specify custom .env file path with { path: \'/custom/path/.env\' }',
12883
12884
  '⚙️ enable debug logging with { debug: true }',
@@ -13178,7 +13179,7 @@ function requireMain () {
13178
13179
  }
13179
13180
  }
13180
13181
 
13181
- _log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`(tip: ${_getRandomTip()})`)}`);
13182
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`-- tip: ${_getRandomTip()}`)}`);
13182
13183
  }
13183
13184
 
13184
13185
  if (lastError) {
@@ -13481,64 +13482,136 @@ const marketEarlyCloses = {
13481
13482
  },
13482
13483
  };
13483
13484
 
13484
- // market-time.ts - Refactored for better organization and usability
13485
- // ===== CONFIGURATION =====
13486
- /**
13487
- * Market configuration constants
13488
- */
13485
+ // Constants for NY market times (Eastern Time)
13489
13486
  const MARKET_CONFIG = {
13490
- TIMEZONE: 'America/New_York',
13487
+ UTC_OFFSET_STANDARD: -5, // EST
13488
+ UTC_OFFSET_DST: -4, // EDT
13491
13489
  TIMES: {
13492
- EXTENDED_START: { hour: 4, minute: 0 },
13493
13490
  MARKET_OPEN: { hour: 9, minute: 30 },
13494
13491
  MARKET_CLOSE: { hour: 16, minute: 0 },
13495
- EARLY_CLOSE: { hour: 13, minute: 0 },
13496
- EXTENDED_END: { hour: 20, minute: 0 },
13497
- EARLY_EXTENDED_END: { hour: 17, minute: 0 },
13498
- },
13492
+ EARLY_CLOSE: { hour: 13, minute: 0 }},
13499
13493
  };
13500
- // ===== MARKET CALENDAR SERVICE =====
13494
+ // Helper: Get NY offset for a given UTC date (DST rules for US)
13501
13495
  /**
13502
- * Service for handling market calendar operations (holidays, early closes, market days)
13496
+ * Returns the NY timezone offset (in hours) for a given UTC date, accounting for US DST rules.
13497
+ * @param date - UTC date
13498
+ * @returns offset in hours (-5 for EST, -4 for EDT)
13503
13499
  */
13504
- class MarketCalendar {
13505
- timezone;
13506
- constructor(timezone = MARKET_CONFIG.TIMEZONE) {
13507
- this.timezone = timezone;
13500
+ function getNYOffset(date) {
13501
+ // US DST starts 2nd Sunday in March, ends 1st Sunday in November
13502
+ const year = date.getUTCFullYear();
13503
+ const dstStart = getNthWeekdayOfMonth(year, 3, 0, 2); // March, Sunday, 2nd
13504
+ const dstEnd = getNthWeekdayOfMonth(year, 11, 0, 1); // November, Sunday, 1st
13505
+ const utcTime = date.getTime();
13506
+ if (utcTime >= dstStart.getTime() && utcTime < dstEnd.getTime()) {
13507
+ return MARKET_CONFIG.UTC_OFFSET_DST;
13508
+ }
13509
+ return MARKET_CONFIG.UTC_OFFSET_STANDARD;
13510
+ }
13511
+ // Helper: Get nth weekday of month in UTC
13512
+ /**
13513
+ * Returns the nth weekday of a given month in UTC.
13514
+ * @param year - Year
13515
+ * @param month - 1-based month (e.g. March = 3)
13516
+ * @param weekday - 0=Sunday, 1=Monday, ...
13517
+ * @param n - nth occurrence
13518
+ * @returns Date object for the nth weekday
13519
+ */
13520
+ function getNthWeekdayOfMonth(year, month, weekday, n) {
13521
+ let count = 0;
13522
+ for (let d = 1; d <= 31; d++) {
13523
+ const date = new Date(Date.UTC(year, month - 1, d));
13524
+ if (date.getUTCMonth() !== month - 1)
13525
+ break;
13526
+ if (date.getUTCDay() === weekday) {
13527
+ count++;
13528
+ if (count === n)
13529
+ return date;
13530
+ }
13508
13531
  }
13532
+ // fallback: last day of month
13533
+ return new Date(Date.UTC(year, month - 1, 28));
13534
+ }
13535
+ // Helper: Convert UTC date to NY time (returns new Date object)
13536
+ /**
13537
+ * Converts a UTC date to NY time (returns a new Date object).
13538
+ * @param date - UTC date
13539
+ * @returns Date object in NY time
13540
+ */
13541
+ function toNYTime(date) {
13542
+ const offset = getNYOffset(date);
13543
+ // NY offset in hours
13544
+ const utcMillis = date.getTime();
13545
+ const nyMillis = utcMillis + offset * 60 * 60 * 1000;
13546
+ return new Date(nyMillis);
13547
+ }
13548
+ // Helper: Convert NY time to UTC (returns new Date object)
13549
+ /**
13550
+ * Converts a NY time date to UTC (returns a new Date object).
13551
+ * @param date - NY time date
13552
+ * @returns Date object in UTC
13553
+ */
13554
+ function fromNYTime(date) {
13555
+ const offset = getNYOffset(date);
13556
+ const nyMillis = date.getTime();
13557
+ const utcMillis = nyMillis - offset * 60 * 60 * 1000;
13558
+ return new Date(utcMillis);
13559
+ }
13560
+ // Market calendar logic
13561
+ /**
13562
+ * Market calendar logic for holidays, weekends, and market days.
13563
+ */
13564
+ class MarketCalendar {
13509
13565
  /**
13510
- * Check if a date is a weekend
13566
+ * Checks if a date is a weekend in NY time.
13567
+ * @param date - Date object
13568
+ * @returns true if weekend, false otherwise
13511
13569
  */
13512
13570
  isWeekend(date) {
13513
- const day = date.getDay();
13514
- return day === 0 || day === 6; // Sunday or Saturday
13571
+ const day = toNYTime(date).getUTCDay();
13572
+ return day === 0 || day === 6;
13515
13573
  }
13516
13574
  /**
13517
- * Check if a date is a market holiday
13575
+ * Checks if a date is a market holiday in NY time.
13576
+ * @param date - Date object
13577
+ * @returns true if holiday, false otherwise
13518
13578
  */
13519
13579
  isHoliday(date) {
13520
- const formattedDate = dateFnsTz.formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13521
- const year = dateFnsTz.toZonedTime(date, this.timezone).getFullYear();
13580
+ const nyDate = toNYTime(date);
13581
+ const year = nyDate.getUTCFullYear();
13582
+ const month = nyDate.getUTCMonth() + 1;
13583
+ const day = nyDate.getUTCDate();
13584
+ const formattedDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
13522
13585
  const yearHolidays = marketHolidays[year];
13523
13586
  if (!yearHolidays)
13524
13587
  return false;
13525
13588
  return Object.values(yearHolidays).some(holiday => holiday.date === formattedDate);
13526
13589
  }
13527
13590
  /**
13528
- * Check if a date is an early close day
13591
+ * Checks if a date is an early close day in NY time.
13592
+ * @param date - Date object
13593
+ * @returns true if early close, false otherwise
13529
13594
  */
13530
13595
  isEarlyCloseDay(date) {
13531
- const formattedDate = dateFnsTz.formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13532
- const year = dateFnsTz.toZonedTime(date, this.timezone).getFullYear();
13596
+ const nyDate = toNYTime(date);
13597
+ const year = nyDate.getUTCFullYear();
13598
+ const month = nyDate.getUTCMonth() + 1;
13599
+ const day = nyDate.getUTCDate();
13600
+ const formattedDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
13533
13601
  const yearEarlyCloses = marketEarlyCloses[year];
13534
13602
  return yearEarlyCloses && yearEarlyCloses[formattedDate] !== undefined;
13535
13603
  }
13536
13604
  /**
13537
- * Get the early close time for a date (in minutes from midnight)
13605
+ * Gets the early close time (in minutes from midnight) for a given date.
13606
+ * @param date - Date object
13607
+ * @returns minutes from midnight or null
13538
13608
  */
13539
13609
  getEarlyCloseTime(date) {
13540
- const formattedDate = dateFnsTz.formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13541
- const year = dateFnsTz.toZonedTime(date, this.timezone).getFullYear();
13610
+ const nyDate = toNYTime(date);
13611
+ const year = nyDate.getUTCFullYear();
13612
+ const month = nyDate.getUTCMonth() + 1;
13613
+ const day = nyDate.getUTCDate();
13614
+ const formattedDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
13542
13615
  const yearEarlyCloses = marketEarlyCloses[year];
13543
13616
  if (yearEarlyCloses && yearEarlyCloses[formattedDate]) {
13544
13617
  const [hours, minutes] = yearEarlyCloses[formattedDate].time.split(':').map(Number);
@@ -13547,284 +13620,85 @@ class MarketCalendar {
13547
13620
  return null;
13548
13621
  }
13549
13622
  /**
13550
- * Check if a date is a market day (not weekend or holiday)
13623
+ * Checks if a date is a market day (not weekend or holiday).
13624
+ * @param date - Date object
13625
+ * @returns true if market day, false otherwise
13551
13626
  */
13552
13627
  isMarketDay(date) {
13553
13628
  return !this.isWeekend(date) && !this.isHoliday(date);
13554
13629
  }
13555
13630
  /**
13556
- * Get the next market day from a given date
13631
+ * Gets the next market day after the given date.
13632
+ * @param date - Date object
13633
+ * @returns Date object for next market day
13557
13634
  */
13558
13635
  getNextMarketDay(date) {
13559
- let nextDay = dateFns.add(date, { days: 1 });
13636
+ let nextDay = new Date(date.getTime() + 24 * 60 * 60 * 1000);
13560
13637
  while (!this.isMarketDay(nextDay)) {
13561
- nextDay = dateFns.add(nextDay, { days: 1 });
13638
+ nextDay = new Date(nextDay.getTime() + 24 * 60 * 60 * 1000);
13562
13639
  }
13563
13640
  return nextDay;
13564
13641
  }
13565
13642
  /**
13566
- * Get the previous market day from a given date
13643
+ * Gets the previous market day before the given date.
13644
+ * @param date - Date object
13645
+ * @returns Date object for previous market day
13567
13646
  */
13568
13647
  getPreviousMarketDay(date) {
13569
- let prevDay = dateFns.sub(date, { days: 1 });
13648
+ let prevDay = new Date(date.getTime() - 24 * 60 * 60 * 1000);
13570
13649
  while (!this.isMarketDay(prevDay)) {
13571
- prevDay = dateFns.sub(prevDay, { days: 1 });
13650
+ prevDay = new Date(prevDay.getTime() - 24 * 60 * 60 * 1000);
13572
13651
  }
13573
13652
  return prevDay;
13574
13653
  }
13575
13654
  }
13576
- // ===== TIME FORMATTER SERVICE =====
13655
+ // Get last full trading date
13577
13656
  /**
13578
- * Service for formatting time outputs
13657
+ * Returns the last full trading date (market close) for a given date.
13658
+ * @param currentDate - Date object (default: now)
13659
+ * @returns Date object for last full trading date
13579
13660
  */
13580
- class TimeFormatter {
13581
- timezone;
13582
- constructor(timezone = MARKET_CONFIG.TIMEZONE) {
13583
- this.timezone = timezone;
13584
- }
13585
- /**
13586
- * Format a date based on the output format
13587
- */
13588
- formatDate(date, outputFormat = 'iso') {
13589
- switch (outputFormat) {
13590
- case 'unix-seconds':
13591
- return Math.floor(date.getTime() / 1000);
13592
- case 'unix-ms':
13593
- return date.getTime();
13594
- case 'iso':
13595
- default:
13596
- return dateFnsTz.formatInTimeZone(date, this.timezone, "yyyy-MM-dd'T'HH:mm:ssXXX");
13597
- }
13598
- }
13599
- /**
13600
- * Get New York timezone offset
13601
- */
13602
- getNYTimeZone(date = new Date()) {
13603
- const dtf = new Intl.DateTimeFormat('en-US', {
13604
- timeZone: this.timezone,
13605
- timeZoneName: 'shortOffset',
13606
- });
13607
- const parts = dtf.formatToParts(date);
13608
- const tz = parts.find(p => p.type === 'timeZoneName')?.value;
13609
- if (!tz) {
13610
- throw new Error('Could not determine New York offset');
13611
- }
13612
- const shortOffset = tz.replace('GMT', '');
13613
- if (shortOffset === '-4') {
13614
- return '-04:00';
13615
- }
13616
- else if (shortOffset === '-5') {
13617
- return '-05:00';
13618
- }
13619
- else {
13620
- throw new Error(`Unexpected timezone offset: ${shortOffset}`);
13621
- }
13622
- }
13623
- /**
13624
- * Get trading date in YYYY-MM-DD format
13625
- */
13626
- getTradingDate(time) {
13627
- let date;
13628
- if (typeof time === 'number') {
13629
- date = new Date(time);
13630
- }
13631
- else if (typeof time === 'string') {
13632
- date = new Date(time);
13633
- }
13634
- else {
13635
- date = time;
13636
- }
13637
- return dateFnsTz.formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13638
- }
13661
+ function getLastFullTradingDateImpl(currentDate = new Date()) {
13662
+ const calendar = new MarketCalendar();
13663
+ const nyDate = toNYTime(currentDate);
13664
+ const minutes = nyDate.getUTCHours() * 60 + nyDate.getUTCMinutes();
13665
+ const marketOpenMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
13666
+ let marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13667
+ if (calendar.isEarlyCloseDay(currentDate)) {
13668
+ marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13669
+ }
13670
+ // If not a market day, or before open, or during market hours, return previous market day's close
13671
+ if (!calendar.isMarketDay(currentDate) || minutes < marketOpenMinutes || (minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
13672
+ const prevMarketDay = calendar.getPreviousMarketDay(currentDate);
13673
+ let prevCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13674
+ if (calendar.isEarlyCloseDay(prevMarketDay)) {
13675
+ prevCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13676
+ }
13677
+ const year = prevMarketDay.getUTCFullYear();
13678
+ const month = prevMarketDay.getUTCMonth();
13679
+ const day = prevMarketDay.getUTCDate();
13680
+ const closeHour = Math.floor(prevCloseMinutes / 60);
13681
+ const closeMinute = prevCloseMinutes % 60;
13682
+ return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
13683
+ }
13684
+ // After market close or after extended hours, return today's close
13685
+ const year = nyDate.getUTCFullYear();
13686
+ const month = nyDate.getUTCMonth();
13687
+ const day = nyDate.getUTCDate();
13688
+ const closeHour = Math.floor(marketCloseMinutes / 60);
13689
+ const closeMinute = marketCloseMinutes % 60;
13690
+ return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
13639
13691
  }
13640
- // ===== MARKET TIME CALCULATOR =====
13641
13692
  /**
13642
- * Service for core market time calculations
13693
+ * Returns the last full trading date as a Date object.
13643
13694
  */
13644
- class MarketTimeCalculator {
13645
- calendar;
13646
- timezone;
13647
- constructor(timezone = MARKET_CONFIG.TIMEZONE) {
13648
- this.timezone = timezone;
13649
- this.calendar = new MarketCalendar(timezone);
13650
- }
13651
- /**
13652
- * Get market open/close times for a date
13653
- */
13654
- getMarketTimes(date) {
13655
- const zonedDate = dateFnsTz.toZonedTime(date, this.timezone);
13656
- // Market closed on weekends and holidays
13657
- if (!this.calendar.isMarketDay(zonedDate)) {
13658
- return {
13659
- marketOpen: false,
13660
- open: null,
13661
- close: null,
13662
- openExt: null,
13663
- closeExt: null,
13664
- };
13665
- }
13666
- const dayStart = dateFns.startOfDay(zonedDate);
13667
- // Regular market times
13668
- const open = dateFnsTz.fromZonedTime(dateFns.set(dayStart, { hours: MARKET_CONFIG.TIMES.MARKET_OPEN.hour, minutes: MARKET_CONFIG.TIMES.MARKET_OPEN.minute }), this.timezone);
13669
- let close = dateFnsTz.fromZonedTime(dateFns.set(dayStart, { hours: MARKET_CONFIG.TIMES.MARKET_CLOSE.hour, minutes: MARKET_CONFIG.TIMES.MARKET_CLOSE.minute }), this.timezone);
13670
- // Extended hours
13671
- const openExt = dateFnsTz.fromZonedTime(dateFns.set(dayStart, { hours: MARKET_CONFIG.TIMES.EXTENDED_START.hour, minutes: MARKET_CONFIG.TIMES.EXTENDED_START.minute }), this.timezone);
13672
- let closeExt = dateFnsTz.fromZonedTime(dateFns.set(dayStart, { hours: MARKET_CONFIG.TIMES.EXTENDED_END.hour, minutes: MARKET_CONFIG.TIMES.EXTENDED_END.minute }), this.timezone);
13673
- // Handle early close days
13674
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13675
- close = dateFnsTz.fromZonedTime(dateFns.set(dayStart, { hours: MARKET_CONFIG.TIMES.EARLY_CLOSE.hour, minutes: MARKET_CONFIG.TIMES.EARLY_CLOSE.minute }), this.timezone);
13676
- closeExt = dateFnsTz.fromZonedTime(dateFns.set(dayStart, { hours: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour, minutes: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute }), this.timezone);
13677
- }
13678
- return {
13679
- marketOpen: true,
13680
- open,
13681
- close,
13682
- openExt,
13683
- closeExt,
13684
- };
13685
- }
13686
- /**
13687
- * Check if a time is within market hours based on intraday reporting mode
13688
- */
13689
- isWithinMarketHours(date, intradayReporting = 'market_hours') {
13690
- const zonedDate = dateFnsTz.toZonedTime(date, this.timezone);
13691
- // Not a market day
13692
- if (!this.calendar.isMarketDay(zonedDate)) {
13693
- return false;
13694
- }
13695
- const timeInMinutes = zonedDate.getHours() * 60 + zonedDate.getMinutes();
13696
- switch (intradayReporting) {
13697
- case 'extended_hours': {
13698
- const startMinutes = MARKET_CONFIG.TIMES.EXTENDED_START.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_START.minute;
13699
- let endMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
13700
- // Handle early close
13701
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13702
- endMinutes = MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
13703
- }
13704
- return timeInMinutes >= startMinutes && timeInMinutes <= endMinutes;
13705
- }
13706
- case 'continuous':
13707
- return true;
13708
- default: {
13709
- // 'market_hours'
13710
- const startMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
13711
- let endMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13712
- // Handle early close
13713
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13714
- endMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13715
- }
13716
- return timeInMinutes >= startMinutes && timeInMinutes <= endMinutes;
13717
- }
13718
- }
13719
- }
13720
- /**
13721
- * Get the last full trading date
13722
- */
13723
- getLastFullTradingDate(currentDate = new Date(), extendedHours = true) {
13724
- const nowET = dateFnsTz.toZonedTime(currentDate, this.timezone);
13725
- if (this.calendar.isMarketDay(nowET)) {
13726
- const timeInMinutes = nowET.getHours() * 60 + nowET.getMinutes();
13727
- let endMinutes;
13728
- if (extendedHours) {
13729
- endMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
13730
- if (this.calendar.isEarlyCloseDay(nowET)) {
13731
- endMinutes = MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
13732
- }
13733
- }
13734
- else {
13735
- endMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13736
- if (this.calendar.isEarlyCloseDay(nowET)) {
13737
- endMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13738
- }
13739
- }
13740
- if (timeInMinutes >= endMinutes) {
13741
- return dateFnsTz.fromZonedTime(dateFns.set(nowET, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }), this.timezone);
13742
- }
13743
- }
13744
- // Return the last completed trading day
13745
- const lastMarketDay = this.calendar.getPreviousMarketDay(nowET);
13746
- return dateFnsTz.fromZonedTime(dateFns.set(lastMarketDay, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }), this.timezone);
13747
- }
13748
- /**
13749
- * Get day boundaries based on intraday reporting mode
13750
- */
13751
- getDayBoundaries(date, intradayReporting = 'market_hours') {
13752
- const zonedDate = dateFnsTz.toZonedTime(date, this.timezone);
13753
- let start;
13754
- let end;
13755
- switch (intradayReporting) {
13756
- case 'extended_hours': {
13757
- start = dateFns.set(zonedDate, {
13758
- hours: MARKET_CONFIG.TIMES.EXTENDED_START.hour,
13759
- minutes: MARKET_CONFIG.TIMES.EXTENDED_START.minute,
13760
- seconds: 0,
13761
- milliseconds: 0,
13762
- });
13763
- end = dateFns.set(zonedDate, {
13764
- hours: MARKET_CONFIG.TIMES.EXTENDED_END.hour,
13765
- minutes: MARKET_CONFIG.TIMES.EXTENDED_END.minute,
13766
- seconds: 59,
13767
- milliseconds: 999,
13768
- });
13769
- // Handle early close
13770
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13771
- end = dateFns.set(zonedDate, {
13772
- hours: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour,
13773
- minutes: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute,
13774
- seconds: 59,
13775
- milliseconds: 999,
13776
- });
13777
- }
13778
- break;
13779
- }
13780
- case 'continuous': {
13781
- start = dateFns.startOfDay(zonedDate);
13782
- end = dateFns.endOfDay(zonedDate);
13783
- break;
13784
- }
13785
- default: {
13786
- // 'market_hours'
13787
- start = dateFns.set(zonedDate, {
13788
- hours: MARKET_CONFIG.TIMES.MARKET_OPEN.hour,
13789
- minutes: MARKET_CONFIG.TIMES.MARKET_OPEN.minute,
13790
- seconds: 0,
13791
- milliseconds: 0,
13792
- });
13793
- end = dateFns.set(zonedDate, {
13794
- hours: MARKET_CONFIG.TIMES.MARKET_CLOSE.hour,
13795
- minutes: MARKET_CONFIG.TIMES.MARKET_CLOSE.minute,
13796
- seconds: 59,
13797
- milliseconds: 999,
13798
- });
13799
- // Handle early close
13800
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13801
- end = dateFns.set(zonedDate, {
13802
- hours: MARKET_CONFIG.TIMES.EARLY_CLOSE.hour,
13803
- minutes: MARKET_CONFIG.TIMES.EARLY_CLOSE.minute,
13804
- seconds: 59,
13805
- milliseconds: 999,
13806
- });
13807
- }
13808
- break;
13809
- }
13810
- }
13811
- return {
13812
- start: dateFnsTz.fromZonedTime(start, this.timezone),
13813
- end: dateFnsTz.fromZonedTime(end, this.timezone),
13814
- };
13815
- }
13816
- }
13817
- const marketTimeCalculator = new MarketTimeCalculator();
13818
- const timeFormatter = new TimeFormatter();
13819
13695
  /**
13820
- * Get the last full trading date
13696
+ * Returns the last full trading date as a Date object.
13697
+ * @param currentDate - Date object (default: now)
13698
+ * @returns Date object for last full trading date
13821
13699
  */
13822
13700
  function getLastFullTradingDate(currentDate = new Date()) {
13823
- const date = marketTimeCalculator.getLastFullTradingDate(currentDate);
13824
- return {
13825
- date,
13826
- YYYYMMDD: timeFormatter.getTradingDate(date),
13827
- };
13701
+ return getLastFullTradingDateImpl(currentDate);
13828
13702
  }
13829
13703
 
13830
13704
  const log = (message, options = { type: 'info' }) => {
@@ -14224,8 +14098,8 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
14224
14098
  const response = await this.getHistoricalBars({
14225
14099
  symbols: [symbol],
14226
14100
  timeframe: '1Day',
14227
- start: prevMarketDate.date.toISOString(),
14228
- end: prevMarketDate.date.toISOString(),
14101
+ start: prevMarketDate.toISOString(),
14102
+ end: prevMarketDate.toISOString(),
14229
14103
  limit: 1,
14230
14104
  });
14231
14105
  if (!response.bars[symbol] || response.bars[symbol].length === 0) {