JavaScript 部署到 Heroku 后的日期时间不同

JavaScript Date time different once deployed to Heroku

在本地,每分钟都是准确的。一旦部署到 Heroku,时间之间的差异就会减少大约 6 个小时。我不想转换 Heroku 时区(希望它保持 UTC)。我已经尝试了从 getTimezoneOffset() 转换到不同日期格式的所有方法,但我仍然得到相同的结果。我怎样才能让这两个日期时间相互匹配并且在部署时不被小时数抵消?为什么它们的格式不同,但格式完全相同?

// Used to calculate current date time

const currentDate = new Date();
// ^ Production - (2021-10-12T19:12:41.081Z)
const time = `${currentDate.getHours()}:${currentDate.getMinutes()}`;
const fullDate = `${currentDate.getMonth()}/${currentDate.getDate()}/${currentDate.getFullYear()}`;
const currentDateFormatted = new Date(`${fullDate} ${time}`);
// ^ Production - (2021-10-12T19:12:00.000Z)

const currentParsedDateToUTC = Date.parse(currentDateFormatted.toUTCString());

// Used to calculate an event date time

const eventDate = new Date(`${event.date} ${event.endTime}`); // same exact format as above
// ^ Production - (2021-10-12T13:12:00.000Z)
const eventParsedDateToUTC = Date.parse(eventDate.toUTCString());

const isExpired = (currentParsedDateToUTC > eventParsedDateToUTC); // works locally, but not in production

在此示例中,事件日期和开始时间与当前日期时间相同。我怎样才能防止它们有很大的不同?

那是因为 Heroku 服务器与你的时区不同,你可以通过从前端转换时间格式来处理它,我建议你使用 moment.js 例如在你的前端你可以转换为这个:

npm install moment --save

然后您可以创建一个函数来更改要显示的格式:

const formatDatetime = (
  datetime = "N/A",
  format = 'LLL' // here is your format
) => {
  return moment(datetime).isValid()
    ? moment(datetime).format(format)
    : datetime;
};

所以 -- Heroku 返回正确的 UTC 本地时间,在撰写本文时它是 2021-10-12T19:36:00.000Z

您要求 Heroku 将 2012-10-12 13:12 解释为日期,但您没有指定它应该使用的时区,因此它默认为它自己的当地时间 UTC。

此处一切正常。

我认为你在隐含地问的是你希望它把 13:12 解释为你的当地时间。但是,Heroku 无法知道您的当地时间,因此您需要跟踪数据库中事件的时区。

这在本地工作的唯一原因是因为您的本地服务器恰好与您处于同一时区——如果我从我的时区连接到您的本地服务器,我会遇到同样的问题。

前四行代码似乎是试图创建一个日期并将秒和毫秒设置为零。可以这样做:

let d = new Date();
d.setSeconds(0,0);

这会将秒和毫秒设置为零。

我不知道你认为以下内容是什么:

const currentParsedDateToUTC = Date.parse(currentDateFormatted.toUTCString());

但给出了相同的结果:

d.getTime();

这实际上是之前调用 setSeconds 时返回的值。所以前 5 行代码减少为:

let currentParsedDateToUTC = new Date().setSeconds(0,0);

然后在:

const eventDate = new Date(`${event.date} ${event.endTime}`);

d/m/y H:m 格式的时间戳使用内置解析器进行解析,这是个坏主意,请参阅 Why does Date.parse give incorrect results?。您可以改用库或只编写一个 2 行函数来完成这项工作。

然后又是:

const eventParsedDateToUTC = Date.parse(eventDate.toUTCString());

这很简单:

const eventParsedDateToUTC = eventDate.getTime();

终于有:

const isExpired = (currentParsedDateToUTC > eventParsedDateToUTC);

比较运算符会强制 Dates 为您编号,因此您可以将值保留为 Dates。

完成这项工作的函数是:

// eventDate is UTC timestamp in m/d/y H:m format
function isExpired(eventDate) {
  // Parse eventDate as UTC
  let [M,D,Y,H,m] = eventDate.split(/\W/);
  let eventD = new Date(Date.UTC(Y, M-1, D, H, m));
  // return true if has passed (minute precision)
  return eventD < new Date().setSeconds(0,0);
}

// Event dates (UTC)
['10/12/2021 12:00', // 12 Oct 2021 12:00
 '10/13/2021 12:00', // 13 Oct 2021 12:00
 '10/13/2022 12:00', // 13 Oct 2022 12:00
 ].forEach(d => 
   console.log(d + ' has' + (isExpired(d)? '':' not') + ' Expired')   
);

您可以在不转换为 Date 的情况下使用 Date.UTC(Y, M-1, D, H, m) 返回的值,因此最后两行可以是:

  return Date.UTC(Y, M-1, D, H, m) < new Date().setSeconds(0,0);

但使用日期更符合语义(如果不需要)。 :-)