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
相关产品推荐
相关产品推荐

