Moment.js Timezone转换异常:仅传入字符串格式日期才生效
Hey Brian, let's break down exactly what's happening here—this is a super common gotcha with how Moment.js parses dates and handles timezones, so you're not alone!
First, Let's Clarify What d Is
Since moment.isMoment(d) returns false, d is either a native JavaScript Date object or a plain date string. From the output you shared (Thu Apr 26 2018 21:09:11 GMT-0700 (Pacific Daylight Time)), it’s almost certainly a Date object (because that’s exactly what Date.prototype.toString() outputs in most browsers). That’s the first key to understanding the discrepancies.
Why the Two moment.tz Calls Return Different Results
Let’s break down each case:
1. moment.tz(d.toString(), this._timezone) → 04/26/2018 3 pm
When you convert d to a string with toString(), you’re creating a human-readable date string that includes explicit timezone info (GMT-0700 / PDT). Here’s what Moment does:
- It parses the string, honors the embedded PDT timezone, and converts that time to UTC (21:09 PDT = 04:09 UTC on April 27th).
- Then it takes that UTC time and converts it to whatever timezone
this._timezoneis set to, resulting in the 3 PM you see.
2. moment.tz(d, this._timezone) → 04/26/2018 10 pm
When you pass a native Date object directly to moment.tz, things work differently:
- Date objects store time as a UTC timestamp under the hood—their
toString()output (showing PDT) is just a localized display, not part of their internal value. - Moment reads that UTC timestamp directly, then converts it to
this._timezone’s time, which gives you the 10 PM result.
Why Converting to ISO String Doesn’t Change the Timezone
ISO strings (e.g., 2018-04-27T04:09:11Z) are always UTC-based (the trailing Z stands for Zulu time, which is UTC). When you convert d to an ISO string, you’re essentially stripping away the original PDT timezone info and locking it to UTC. Any subsequent timezone conversion will start from that UTC time, not the original PDT time—which is why you don’t see the "change" you expected.
Fixes to Get Consistent Results
Depending on what you’re trying to achieve, here are the right approaches:
If You Want to Preserve the Original PDT Time
If d represents a time in PDT and you need to convert it to your target timezone:
// First parse the date into its original timezone (PDT = America/Los_Angeles) const pdtTime = moment.tz(d.toString(), 'America/Los_Angeles'); // Then convert to your target timezone const targetTime = pdtTime.tz(this._timezone); console.log(targetTime.format('MM/DD/YYYY h a'));
If You Want to Use the Date Object’s UTC Timestamp
If you trust the Date object’s internal UTC timestamp (e.g., d was created correctly in UTC or your local timezone):
// Create a UTC Moment instance from the Date object const utcTime = moment.utc(d); // Convert to your target timezone const targetTime = utcTime.tz(this._timezone); console.log(targetTime.format('MM/DD/YYYY h a'));
If You Need to Force Parsing to Your Target Timezone
If the string’s embedded timezone is incorrect and you want to ignore it, use the third true parameter to force Moment to parse the string as if it’s in this._timezone:
const targetTime = moment.tz(d.toString(), this._timezone, true); console.log(targetTime.format('MM/DD/YYYY h a'));
内容的提问来源于stack exchange,提问作者Brian McCall

