简体   繁体   English

如何编写一个 function 可以使用 date-fns 正确地将小时和天添加到目标时区?

[英]How to write a function that can add hours and days correctly to the target time zone with date-fns?

Lets say I have a timestamp with the IANA time zone identifier:假设我有一个带有 IANA 时区标识符的时间戳:

2021-11-07T00:00:00-04:00[America/New_York] 2021-11-07T00:00:00-04:00[美国/纽约]

Note : This timestamp is 2 hours away from transitioning from DST to standard time.注意:此时间戳是从 DST 转换到标准时间的2 小时

I want to write a function that can take the timestamp, time zone identifier and a Duration , and return a new "future" timestamp in the target timezone.我想编写一个 function 可以获取时间戳、时区标识符和Duration ,并在目标时区返回一个新的“未来”时间戳。

For instance:例如:

zonedAddDuration("2021-11-07T00:00:00-04:00", "America/New_York", {
  days: 1
})
// => "2021-11-08T00:00:00-05:00"

zonedAddDuration("2021-11-07T00:00:00-04:00", "America/New_York", {
  hours: 5
})
// "2021-11-07T04:00:00-05:00"

I seem to have found a way to make zonedAddDuration handle both these scenarios, but not at the same time :我似乎找到了一种让zonedAddDuration处理这两种情况的方法,但不能同时处理

import { add, Duration, parseISO } from "date-fns";
import {
  utcToZonedTime,
  format as formatTz
} from "date-fns-tz";

// Handles "hours" scenario (also minutes and seconds)
export const zonedAddDuration1 = (
  isoDateStr: string,
  zoneIana: string,
  duration: Duration
) => {
  const startDate = parseISO(isoDateStr);

  const futureDate = add(startDate, duration);
  const zonedTime = utcToZonedTime(futureDate, zoneIana);

  return formatTz(zonedTime, "yyyy-MM-dd'T'HH:mm:ssXXXzzz", {
    timeZone: zoneIana
  });
};

// handles "days" scenario (also months, weeks, years)
export const zonedAddDuration2 = (
  isoDateStr: string,
  zoneIana: string,
  duration: Duration
) => {
  const startDate = parseISO(isoDateStr);

  const zonedTime = utcToZonedTime(startDate, zoneIana);
  const futureDate = add(zonedTime, duration);

  return formatTz(futureDate, "yyyy-MM-dd'T'HH:mm:ssXXXzzz", {
    timeZone: zoneIana
  });
};

As you might notice, the only difference between zonedAddDuration1 and zonedAddDuration2 is the order in which I use utcToZonedTime .您可能会注意到, zonedAddDuration1zonedAddDuration2之间的唯一区别是我使用utcToZonedTime的顺序。 I need this function to be general purpose, and should handle all kinds of durations correctly, including DST transitions.我需要这个 function 是通用的,并且应该正确处理各种持续时间,包括 DST 转换。 I should add that the local timezone of the system should not matter, I want the same results regardless of where I run this code .我应该补充一点,系统的本地时区应该无关紧要,无论我在哪里运行此代码,我都想要相同的结果

I think my understanding of date-fns-tz might be lacking, I've read the documentation many times and still not sure I'm grasping it correctly.我认为我可能缺乏对 date-fns-tz 的理解,我已经阅读了很多次文档,但仍然不确定我是否正确掌握了它。

If it is not possible to write such a function, then any help understanding why it behaves as it does would be appreciated (why the ordering matters).如果不可能写出这样的 function,那么任何帮助理解它为什么会这样表现将不胜感激(为什么排序很重要)。

I've been experimenting in this Codesandbox (which includes the 2 test scenarios): https://codesandbox.io/s/exciting-fog-tke86?file=/src/dateUtil.ts我一直在这个 Codesandbox(包括 2 个测试场景)中进行试验: https://codesandbox.io/s/exciting-fog-tke86?file=/src/dateUtil.ts

I found a way that will add durations correctly for both dates and times.我找到了一种可以为日期和时间正确添加持续时间的方法。 What I did was to split the input duration in to a time duration and a date duration , and add those to the input date separately.我所做的是将输入持续时间拆分为持续时间日期持续时间,并将它们分别添加到输入日期。 This way I can apply the utcToZonedTime in the correct order for both scenarios.这样,我可以在两种情况下以正确的顺序应用utcToZonedTime

All this feels like a hack though, and I'm still not sure why it works (or even if it works as expected for all concievable scenarios).尽管如此,所有这一切都感觉像是一种黑客行为,我仍然不确定它为什么会起作用(或者即使它在所有可能的场景中都能按预期工作)。

import { add, Duration, parseISO } from "date-fns";
import {
  utcToZonedTime,
  format as formatTz
} from "date-fns-tz";

export const zonedAddDuration = (
  isoDateStr: string,
  zoneIana: string,
  duration: Duration
) => {
  const startDate = parseISO(isoDateStr);
  const dateDuration: Duration = {
    years: duration.years ?? 0,
    months: duration.months ?? 0,
    weeks: duration.weeks ?? 0,
    days: duration.days ?? 0
  };
  const timeDuration: Duration = {
    hours: duration.hours ?? 0,
    minutes: duration.minutes ?? 0,
    seconds: duration.seconds ?? 0
  };
  const futureDateTime = add(startDate, timeDuration);
  const zonedTime = utcToZonedTime(futureDateTime, zoneIana);
  const futureDate = add(zonedTime, dateDuration);

  return formatTz(futureDate, "yyyy-MM-dd'T'HH:mm:ssXXXzzz", {
    timeZone: zoneIana
  });
};

Here is a Codesandbox with the working version plus tests. 这是一个带有工作版本和测试的 Codesandbox

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM