【问题标题】:Getting the start of the day in a different timezone在不同的时区开始一天
【发布时间】:2020-06-03 20:03:05
【问题描述】:

我有一个Date。它在当地时区。我想要一个新的Date,它位于不同时区的一天的开始。以下是一些我不想要的东西:

  • UTC 格式的Date 相当于转换为 UTC 的第一个日期
  • 一个字符串

具体来说,UTC 不起作用,因为在 UTC 中获取一天的开始与在时区获取一天的开始是不同的。

因此,如果我在加尔各答有一个约会,并且想在旧金山开始这一天,那么加尔各答的日期和格林威治的日期可能不是同一个日期。可能是 6 月 15 日在加尔各答,6 月 15 日在格林威治,但 6 月 2 日在旧金山。因此,在设置为 UTC 的日期调用 setMinutes(0) 等将不起作用。

如果有帮助,我也会使用date-fns(不是moment),但这似乎不是因为所有日期(包括date-fns-tz 中的日期)都是在本地时间或UTC 时间返回的。)

这在 Javascript 中是可能的还是我疯了?

注意:

这和Convert date to another timezone in JavaScript不一样

那是关于转换为字符串。我不想要字符串。

【问题讨论】:

  • 您可以尝试将 UTC 日期转换为时间戳,减去或添加以毫秒为单位的时区差异,然后从更正的时间戳中创建一个新日期。
  • 这能回答你的问题吗? Convert date to another timezone in JavaScript
  • @EmielZuurbier 没有。这是关于转换为字符串,这不是我想要的。
  • 看这个方法,getTimezoneOffset可能有帮助

标签: javascript datetime timezone


【解决方案1】:

一种方法是:

  1. 获取所需位置的当前时区偏移量
  2. 为所需的 UTC 日期创建一个日期
  3. 从 #1 应用偏移量

例如使用Get Offset of the other Location in Javascript的答案:

function getTimezoneOffset(date, loc) {
  let offset;
  ['en','fr'].some(lang => {
    let parts = new Intl.DateTimeFormat(lang, {
      minute: 'numeric',
      timeZone: loc,
      timeZoneName:'short'
    }).formatToParts(date);
    let tzName = parts.filter(part => part.type == 'timeZoneName' && part.value);
    if (/^(GMT|UTC)/.test(tzName[0].value)) {
      offset = tzName[0].value.replace(/GMT|UTC/,'') || '+0';
      return true;
    }
  });
  let sign = offset[0] == '\x2b'? '\x2b' : '\x2d';
  let [h, m] = offset.substring(1).split(':');
  return sign + h.padStart(2, '0') + ':' + (m || '00');
}

// Convert offset string in ±HH:mm to minutes
function offsetToMins(offset) {
  let sign = /^-/.test(offset)? -1 : 1;
  let [h, m] = offset.match(/\d\d/g);
  return sign * (h * 60 + Number(m));
}

// Format date as YYYY-MM-DD at loc
function formatYMD(loc, date) {
  return date.toLocaleDateString('en-CA',{timeZone:loc});
}

// Return stat of day for date at loc
function startOfDayAtLoc(loc, date = new Date()) {
  let offset = getTimezoneOffset(date, loc);
  let offMins  = offsetToMins(offset);
  let d = new Date(+date);
  d.setUTCHours(0, -offMins, 0, 0);
  // If date is + or - original date, adjust
  let oDateTS = formatYMD(loc, date);
  let sodDateTS = formatYMD(loc, d);
  if (sodDateTS > oDateTS) {
    d.setUTCDate(d.getUTCDate() - 1);
  } else if (sodDateTS < oDateTS) {
    d.setUTCDate(d.getUTCDate() + 1);
  }
  return d;
}

// Examples
// 1 June 2020 00:00:00 Z
let d = new Date(Date.UTC(2020, 5, 1));
['America/New_York',
 'Asia/Tokyo',
 'Pacific/Tongatapu',
 'Pacific/Rarotonga'
].forEach(loc => {
  let locD = startOfDayAtLoc(loc, d);
  console.log(loc + ' ' + getTimezoneOffset(d, loc) + 
  '\nZulu : ' + locD.toISOString() +
  '\nLocal: ' + locD.toLocaleString('en-GB',{
    timeZone: loc, timeZoneName:'long'
  }));
});
// QnD formatter
let f = (loc, d) => d.toLocaleString('en-CA', {
  timeZone: loc, 
  hour12:false
});
// Dates on different date to UTC date
let laDate = new Date('2022-04-30T18:00:00-07:00');
let la = 'America/Los_Angeles';
console.log(`${la} - ${f(la, laDate)}` +
`\nStart of day: ${f(la, startOfDayAtLoc(la, laDate))}`
);
let bneDate = new Date('2022-05-01T03:00:00+10:00');
let bne = 'Australia/Brisbane';
console.log(`${bne} - ${f(bne, bneDate)}` +
`\nStart of day: ${f(bne, startOfDayAtLoc(bne, bneDate))}`
);

但是,我建议您使用支持时区的库,因为 Date 对象有很多怪癖,并且正在开发一个新的 Temporal object

【讨论】:

  • 这不起作用。运行startOfDayAtLoc('America/Los_Angeles', new Date('Apr 24 2022 18:00:00 pdt')) 返回Mon Apr 25 2022 00:00:00 GMT-0700 (Pacific Daylight Time)。这是错误的,因为太平洋夏令时间 4 月 24 日下午 6 点这一天的开始不是 4 月 25 日。
  • @MichaelMatthewToomim — 'Apr 24 2022 18:00:00 pdt' 不是 ECMAScript 支持的格式,因此解析依赖于实现,并且您的起点不可靠。如果解析正确,该函数将返回 2022-04-25T07:00:00.000Z(即洛杉矶 4 月 25 日的开始日期),因为它使用的是 UTC 日期而不是本地日期。
  • 它不应返回 4 月 25 日当天的开始。它应该返回 4 月 24 日的开始。解析不是问题。无论您如何表达时间,它都会做同样的事情。例如,这也失败了:startOfDayAtLoc('America/Los_Angeles', new Date('Sun Apr 24 2022 18:00:00 GMT-0700 (Pacific Daylight Time)')) 也失败了:startOfDayAtLoc('America/Los_Angeles', new Date(1650848400000))。他们都返回 25 号,而不是 24 号。
  • @MichaelMatthewToomim——我已经解释了为什么会这样。现在已修复。
  • 我确认修复。谢谢@RobG!
猜你喜欢
  • 1970-01-01
  • 2011-12-25
  • 2011-12-20
  • 2015-12-12
  • 2011-11-14
  • 2014-10-22
  • 2012-05-09
  • 2022-12-03
  • 1970-01-01
相关资源
最近更新 更多