如何将 date-fns 中的 startOfDay 与时区一起使用?

And*_*lka 10 javascript date date-fns

我尝试用 date-fns 替换 momentjs

但我正在努力解决一件非常简单的事情:我需要根据给定的时区计算时间戳中获得的日期的 startOfDay、end of day 和 addDays。

date = 1492437600; // Monday April 17, 2017 12:00:00 (pm) in time zone America/Noronha
timeZone = 'America/Noronha';
_startOfDay = utcToZonedTime(startOfDay(date*1000), timeZone).getTime()/1000;
Run Code Online (Sandbox Code Playgroud)

我得到了 _startOfDay = 1492365600 的结果,即美国/诺罗尼亚时区 2017 年 4 月 16 日星期日 16:00:00(下午)

我做错了什么?提前致谢

D G*_*D G 11

注意:请参阅此答案的底部以获得最佳解决方案

旧答案:

我看到这个问题已经有 11 个月了(截至撰写本文时),但我想我会回答它,因为我遇到了同样的问题,其他人将来可能会带着同样的问题来到这里。

date -fns库没有时区支持,因此您还需要使用date-fns-tz库。从date-fns-tz导入getTimezoneOffset函数,并用于计算本地(浏览器)时区和您希望计算一天结束时间的时区(即 America/Noronha)之间的偏移量。请注意,date-fns “endOf”函数使用本地/浏览器时区。

例如

import { endOfDay } from 'date-fns';
import { getTimezoneOffset } from 'date-fns-tz/esm';

const MILLISECS_IN_DAY = 86400000;
            
const localTz = Intl.DateTimeFormat().resolvedOptions().timeZone; // Browser Timezone
const tzOffset = getTimezoneOffset('America/Noronha') - getTimezoneOffset(localTz);

// Have to cater for negative offsets
const tzOffsetEOD = (tzOffset < 0) ? (MILLISECS_IN_DAY + tzOffset) : tzOffset;
       
let testDate = new Date();     
let eodInTimezone = endOfDay(testDate).getTime() - tzOffsetEOD; // in millisecs

// Have to project forward a day if the result is in the past
if (eodInTimezone < testDate.getTime()) eodInTimezone += MILLISECS_IN_DAY;
Run Code Online (Sandbox Code Playgroud)

有人也许能够想出一个更优雅的解决方案来解决这个问题。如果我这样做,我会发回这里。


新答案:

此解决方案最适合所有“End Of” date-fns函数:

import { endOfDay, endOfWeek, endOfMonth, endOfYear } from 'date-fns';
import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz/esm';

const calcZonedDate = (date, tz, fn, options = null) => {
    const inputZoned = utcToZonedTime(date, tz);
    const fnZoned = (options) ? fn(inputZoned, options) : fn(inputZoned);
    return zonedTimeToUtc(fnZoned, tz);
}

const getZonedEndOfDay = (date, timeZone) => {
    return calcZonedDate(date, timeZone, endOfDay);
}

const getZonedEndOfWeek = (date, timeZone) => {
    return calcZonedDate(date, timeZone, endOfWeek, { weekStartsOn: 1 });
}

const getZonedEndOfMonth = (date, timeZone) => {
    return calcZonedDate(date, timeZone, endOfMonth);
}

const getZonedEndOfYear = (date, timeZone) => {
    return calcZonedDate(date, timeZone, endOfYear);
}

// Example Usage
let endOfDayZoned = getZonedEndOfDay(new Date(), 'America/Noronha');
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。