You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular RxJS倒计时组件在iOS设备显示NaN问题求助

Angular倒计时组件iOS设备显示NaN问题排查与修复

问题现象

实现的Angular倒计时组件在桌面、Android设备各类浏览器中可正常显示倒计时(如30d 14h 45m 35s),但在iOS设备(测试机型含iPhone 6/6S/13、iPad,覆盖Safari/Chrome/Edge/Firefox等浏览器)中,所有计时值(天、时、分、秒)均显示为NaNd NaNh NaNm NaNs。

组件代码

TypeScript逻辑

export class CountdownTimerComponent implements OnInit, OnDestroy {  
  targetDate: string;
  subscription: Subscription;
  milliSecondsInASecond = 1000;
  hoursInADay = 24;
  minutesInAnHour = 60;
  SecondsInAMinute = 60;

  timeDifference: any;
  secondsToDday: any;
  minutesToDday: any;
  hoursToDday: any;
  daysToDday: any; 

  constructor(private adminService: AdminService) { }        

  ngOnInit() {
    this.getTargetDate();
    this.subscription = interval(1000)
        .subscribe(x => { this.getTimeDifference(); });
  }

  getTargetDate() {
    this.adminService.getSysConfigs().subscribe((resp: any[]) => {
      this.targetDate = resp.find(s => s.name === 'SingleCardItem').client;
    // targetDate格式为 '2022-10-9'
    })
  }

  private getTimeDifference () {
    let dDay = new Date(this.targetDate);
    this.timeDifference = dDay.getTime() - new Date().getTime();
    this.allocateTimeUnits(this.timeDifference);
  }

  private allocateTimeUnits (timeDifference) {
    this.secondsToDday = Math.floor((timeDifference) / (this.milliSecondsInASecond) % this.SecondsInAMinute);
    this.minutesToDday = Math.floor((timeDifference) / (this.milliSecondsInASecond * this.minutesInAnHour) % this.SecondsInAMinute);
    this.hoursToDday = Math.floor((timeDifference) / (this.milliSecondsInASecond * this.minutesInAnHour * this.SecondsInAMinute) % this.hoursInADay);
    this.daysToDday = Math.floor((timeDifference) / (this.milliSecondsInASecond * this.minutesInAnHour * this.SecondsInAMinute * this.hoursInADay));
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

模板代码

<div class="timer">
    <span id="days"> {{daysToDday}}d </span>
    <span id="hours"> {{hoursToDday}}h </span> 
    <span id="minutes"> {{minutesToDday}}m </span>
    <span id="seconds"> {{secondsToDday}}s </span>
</div>

根因分析

iOS端WebKit内核对日期字符串解析严格遵循ECMAScript规范,仅支持两位数日期/月份的YYYY-MM-DD格式。当前获取的targetDate是2022-10-9这类单位数日期格式,导致new Date(this.targetDate)在iOS环境下返回Invalid Date,调用getTime()会得到NaN,后续所有时间计算结果自然均为NaN。

修复方案

方案1:标准化日期字符串格式

在获取targetDate后,将单位数的月、日补零,转换为标准YYYY-MM-DD格式:

getTargetDate() {
  this.adminService.getSysConfigs().subscribe((resp: any[]) => {
    const rawDate = resp.find(s => s.name === 'SingleCardItem').client;
    // 补零处理单位数的月和日
    this.targetDate = rawDate.replace(/(\d+)-(\d+)-(\d+)/, (_, year, month, day) => {
      return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
    });
  })
}

方案2:手动拆分日期构造Date实例

跳过字符串解析环节,直接拆分日期的年、月、日构造Date对象(注意Date的月份为0基,需减1):

private getTimeDifference () {
  // 拆分日期字符串为数字类型的年、月、日
  const [year, month, day] = this.targetDate.split('-').map(Number);
  // 月份减1,适配Date的0基月份规则
  let dDay = new Date(year, month - 1, day);
  this.timeDifference = dDay.getTime() - new Date().getTime();
  this.allocateTimeUnits(this.timeDifference);
}

额外优化:避免初始空值计算

当前ngOnInit中立即订阅interval,但此时targetDate尚未从接口获取,会导致前几次计算出现NaN。可结合RxJS操作符优化:

ngOnInit() {
  this.subscription = this.adminService.getSysConfigs().pipe(
    map(resp => resp.find(s => s.name === 'SingleCardItem').client),
    // 同步补零处理日期
    map(rawDate => rawDate.replace(/(\d+)-(\d+)-(\d+)/, (_, y, m, d) => `${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`)),
    switchMap(targetDate => interval(1000).pipe(map(() => targetDate))),
    filter(targetDate => !!targetDate)
  ).subscribe(targetDate => {
    this.targetDate = targetDate;
    this.getTimeDifference();
  });
}

内容的提问来源于stack exchange,提问作者canbrian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:37:06