最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use startOfDay from date-fns with timezones? - Stack Overflow

programmeradmin0浏览0评论

I try to replace momentjs with date-fns

but I am struggling with a very simple thing: I need to calculate startOfDay, end of day and addDays for the dates that I have got in timestamps and according to the giving timezone.

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;

and I have got the result for the _startOfDay = 1492365600 that is Sunday April 16, 2017 16:00:00 (pm) in time zone America/Noronha

What I am doing wrong? thanks in advance

I try to replace momentjs with date-fns

but I am struggling with a very simple thing: I need to calculate startOfDay, end of day and addDays for the dates that I have got in timestamps and according to the giving timezone.

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;

and I have got the result for the _startOfDay = 1492365600 that is Sunday April 16, 2017 16:00:00 (pm) in time zone America/Noronha

What I am doing wrong? thanks in advance

Share Improve this question asked Jun 3, 2021 at 10:15 Andrey SylkaAndrey Sylka 1711 gold badge2 silver badges6 bronze badges 1
  • when you call your method startOfDay(..), you do not specify a timezone so the return date can be the start of Day in any timezone. My guess would be UTC but it could be another timezone. – Godev Commented Jun 3, 2021 at 10:30
Add a comment  | 

4 Answers 4

Reset to default 14

Note: See the bottom of this answer for the best solution

Old Answer:

I see this question is 11 months old (as of writing) but thought I'd answer it as I hit the same problem, and others may come here in the future with the same question.

The date-fns library doesn't have timezone support, so you need to also use the date-fns-tz library. Import the getTimezoneOffset function from date-fns-tz and use to calculate the offset between the local (browser) timezone and the timezone you wish to calculate end of day for (i.e. America/Noronha). Note that the date-fns "endOf" functions use the local/browser timezone.

E.g.

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;

Someone may be able to come up with a more elegant solution to this problem. If I do I'll post back here.


New Answer:

This solution works best for all "End Of" date-fns functions:

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');

Hope this helps.

I would suggest to take a look at another date management library. date-fns seems to be designed to work in current timezone and does not provide right tools for timezone calculations.

The same thing in Luxon for example:

const startOfDay = DateTime
    .fromMillis(1492437600000)
    .setZone('America/Noronha')
    .startOf('day')

Then you can use startOfDay.toJSDate() to get Date object with right moment in time, or you could use startOfDay.toFormat('dd-MM-yyyy HH:mm:ss') for text output.

Based on the great answer by @D G, here's a typed version:

type DateType = Date | number | string

function calcZonedDate<
    T extends (date: DateType, options?: any) => Date,
    O = T extends (date: DateType, options?: infer U) => Date ? U : undefined
>(date: DateType, tz: string, fn: T, options?: O): Date {
    const inputZoned = toZonedTime(date, tz)
    const fnZoned = fn(inputZoned, options)
    return fromZonedTime(fnZoned, tz)
}

function zonedEndOfDay(date: DateType, tz: string) {
    return calcZonedDate(date, tz, endOfDay)
}

function zonedEndOfWeek(date: DateType, tz: string, options?: EndOfWeekOptions) {
    return calcZonedDate(date, tz, endOfWeek, options)
}

function zonedEndOfMonth(date: DateType, tz: string) {
    return calcZonedDate(date, tz, endOfMonth)
}

function zonedEndOfYear(date: DateType, tz: string) {
    return calcZonedDate(date, tz, endOfYear)
}

startOfDay is now a thing in DateFns. Example from https://date-fns.org/v2.16.1/docs/startOfDay

The start of a day for 2 September 2014 11:55:00:

var result = startOfDay(new Date(2014, 8, 2, 11, 55, 0))
//=> Tue Sep 02 2014 00:00:00
发布评论

评论列表(0)

  1. 暂无评论