JavaScript - converting Date to UTC and removing days from it - javascript

Trying to iterate backwards from today to August, and log out all the time stamps stored on our database between those periods.
The API only allows queries between a 24 hour window, hence my need for a for loop to call it multiple times. The query’s start and end times are in ISOstring format.
Problem is, when my loop reaches October 31st, it tries to query with startTime 23:00 endTime 00:00 and complains that this is beyond the 24 hour range limit. This makes sense, as I’m from UK and this is when the clocks go back.
Is there a way for me to set my date to UTC and continuously subtract days from it, hence ignoring timezone and any daylight saving?
Our team has consistently used date-fns, so I would love a solution using this library if a library is recommended/required.
This is what I'm currently working with:
export async function dataTrawl(): Promise<void> {
try {
for (let date = new Date(); date >= new Date('2022-10-01'); date.setDate(date.getDate() - 1)) {
const startTime = new Date(date.getTime());
startTime.setDate(date.getDate() - 1);
const response: AxiosResponse<DataExchangeResponse> = await axios.get(
'...api.../1/datapoints',
{
headers: {
accept: 'application/json',
'x-api-key': 'KEY'
},
params: {
start: startTime.toISOString(),
end: date.toISOString()
}
}
);
console.log(`dataTrawl: ${JSON.stringify(response.data)}`);
}
} catch (e) {
const error = e as AxiosError;
console.error(error);
}
}

You can work in UTC days, where (in ECMAScript) every day is exactly 8.64e7 ms long:
// Set start to end of current UTC date
let start = new Date();
start.setUTCHours(24,0,0,0);
// Loop over all previous UTC days until limit
for (let limit = new Date('2022-11-21'); start > limit; start.setTime(+start - 8.64e7)) {
let end = new Date(start - 8.64e7);
// stuff with start and end
console.log(`${start.toISOString()} -`,
`${end.toISOString()}`);
}

You can use the getUTCDate() and setUTCDate() functions to move a date backwards by one day in UTC.
This way we'll be dealing with 24 hour periods consistently with no changes due to DST rules.
export async function dataTrawl(): Promise<void> {
try {
const start = new Date();
const end = new Date('2022-10-01');
console.log(`dataTrawl: start: ${start.toISOString()} -> end: ${end.toISOString()}`);
for (let date = start; date >= end; date.setUTCDate(date.getUTCDate() - 1)) {
const startTime = new Date(date);
startTime.setDate(date.getUTCDate() - 1);
const response: AxiosResponse<DataExchangeResponse> = await axios.get(
'...api.../1/datapoints',
{
headers: {
accept: 'application/json',
'x-api-key': 'KEY'
},
params: {
start: startTime.toISOString(),
end: date.toISOString()
}
}
);
console.log(`dataTrawl: ${JSON.stringify(response.data)}`);
}
} catch (e) {
const error = e as AxiosError;
console.error(error);
}
}

Related

How to use intervalToDuration function from date-fns

I tried using the intervalToDuration function from date-fns but I keep getting an error that says End Date is invalid.
My code is as follows
import { intervalToDuration} from "date-fns";
remaining() {
const now = new Date();
const end = this.endDate;
return intervalToDuration({
start: now,
end: end,
});
},
this.endDate is dynamically populated but for this question is equal to 2021-02-26T00:00:00.000Z
Since your endDate variable is coming from an API, it is being stored as a string, not a date.
Calling intervalToDuration is expecting an interval object to be passed as the parameter. An interval consists of two Dates or Numbers (unix timestamp)
To correct this, you must convert endDate to a date object, below is an untested example;
const remaining = () => {
const endDate = "2021-02-26T00:00:00.000Z";
const now = new Date();
const end = new Date(endDate);
return intervalToDuration({
start: now,
end: end
});
};
const dur = remaining();
console.log("DURRATON ", JSON.stringify(dur));
//
// Response == DURRATON {"years":0,"months":1,"days":8,"hours":15,"minutes":42,"seconds":0}
//
Notice : This does not handle timezones correctly. new Date() will create a datetime in the client timezone where it appears that your response from the API is in GMT timezone

from `const now = new Date()`, how can I verify that I am before or equal a particular date at a given timezone and hour?

I have a timezone map with publishing hour in the local zone with news that must define when they should be published on a date using a date picker.
This is a new news article that is initialized with the following:
{ timeZoneId: 'Europe/Paris, releaseHour: 9, publishingDateTime: undefined } // 9 is the hour GMT+1
I want to know how can I from const now = new Date(), verify if this article should be
published today or the next day, the criteria are:
Is now before releaseHour? (is 9am GMT+1 in paris already passs or not)
If yes, then we should offer the next release slot at 9am GMT+1 + 1 day
If no, then we should use the release slot at 9am the same day
How is this possible?
This is how I have tried:
import { isBefore, isEqual } from 'date-fns';
import { utcToZonedTime } from 'date-fns-tz';
export const getNextPublishingDateTime = (now, timeZoneId, releaseHour) => {
const zoned = utcToZonedTime(now, timeZoneId);
const releaseTime = new Date(zoned.toISOString());
releaseTime.setHours(releaseHour, 0, 0, 0);
if (isBefore(zoned, releaseTime) || isEqual(zoned, releaseTime)) {
console.log('before');
return releaseTime;
}
releaseTime.setDate(releaseTime.getDate() + 1);
console.log('after');
return releaseTime;
};
But the hour returned by utcToZonedTime is not +01:00 offset, instead it is a date at my offset.
I have tried some other ideas, using moment-tz and vanilla Date, I found this task a bit complicated and hope to find help with the JS community as this look to be a normal date comparaison.
You can use the Intl object and formatToParts method to get the current time in any supported timezone using IANA representative locations. It's reasonably well supported.
E.g.
function getHour(date, loc) {
let d = new Intl.DateTimeFormat("en-EN", {
hour: 'numeric',
hour12: false,
timeZone: loc
});
return d.formatToParts(date)[0].value;
}
let loc = 'Europe/Paris';
let now = new Date();
console.log(`The current hour in ${loc} is ${getHour(now, loc)}.`);
The above is just for illustration, there should be validation of input and return values.

How can I update date every Sunday?

So I'm troubleshooting with some JavaScript dates. I'm working with NodeJS Mongoose and React. I'd like to update all dates in database, but I'd like to do that every weekend and keep hours, don't change them at all.
Let say I have day like 22 January 2020, and during weekend date will update itself to 29 of January and then 5 of February. Everything in database is save like ISODate("2020-01-16T16:27:15.003Z") and I have a code to update those dates whenever I want. I'm having trouble figure out how to body of setDate() should look like to automatically change months and days while keeping the same hour everytime - so 22/01/2020 4:00 PM during weekend will change to 29/01/2020 4:00PM.
I've already tried to use momentjs to handle dates but it doesn't work with my database.
cron.schedule("* * * * * *",async function() {
const courses = await Course.find({});
courses.forEach(course => {
const newDate = () => {
let date = new Date();
return date.toISOString();
};
Course.updateMany({
"nextClasses": course.nextClasses === course.startingDate ? course.startingDate :
course.nextClasses
},{$set: {"nextClasses": newDate()}},(err) => console.log(err))
});
}
That's the code responsible for changing dates, for now it changes everything to current date every second ( on purpose, for validation purposes )
This would add 7 days to all dates every Sunday.
const addDays = (date, days) => {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
};
cron.schedule("0 0 * * 0", async function() {
const courses = await Course.find({});
courses.forEach(async (course) => {
await course.update(
{
$set: {
nextClasses: addDays(course.nextClasses, 7)
}
},
(err) => console.log(err)
);
});
});
Alternate method
cron.schedule("0 0 * * 0", async function() {
const courses = await Course.find({});
courses.forEach(async (course) => {
course.nextClasses.setDate(course.nextClasses.getDate() + 7);
course.markModified("nextClasses"); /* Mongoose does not track changes made by built-in Date methods */
await course.save((err) => console.log(err));
});
});

Easiest way to convert UTC to New Zealand date (account for daylight savings)?

I have the following function which I've written to convert msSinceEpoch to the New Zealand Date (IE11 Compatible)...
const MAGICNUMBER = 13;
const toNewZealand = (msSinceEpoch) => {
const [day, month, year, time] = new Date(msSinceEpoch).toLocaleString("en-NZ", {
hour12: false, timeZone: "UTC"
}).split(/[/,]/); // timeZone UTC is the only format support on IE11
const [hours, minutes, seconds] = time.trim().split(":");
return new Date(~~year, ~~month - 1, ~~day, ~~hours + MAGICNUMBER, ~~minutes, ~~seconds)
};
// usage....
console.log(
toNewZealand(new Date().getTime())
)
However, this contains a magic number which is not relative to New Zealand's daylight savings time (+12 or +13).
So here it gets complicated, how do I get the right number relative to daylight savings in New Zealand (+12 or +13).
My initial attempt was just to calculate whether it was in between the last Sunday of September or first Sunday of April but then I realised that the second I use a new Date() constructor anywhere in the code it's going to create a date relative to their system time and break the math.
TL;DR Convert UTC Milliseconds since epoch to New Zealand Time that works with New Zealand's Daylight savings settings.
EDIT: Also not interested in using Moment or any other library to solve this problem due to bundle size costs.
TL;DR Convert UTC Milliseconds since epoch to New Zealand Time...
I think OP has a misunderstanding of what time conversion is. MS Since Epoch are always in UTC. Time conversation changes the display format of the time, and should not change msSinceEpoch
You can use toLocalString() and pass the desired timezone (Pacific/Auckland) to convert the display format of your DateTime:
const date = new Date();
console.log(
date.toLocaleString('en-NZ', {
timeZone: 'Pacific/Auckland',
}),
);
Solved it, tried to keep it as human readable as possible, basically tries to add 12 or 13 depending on where the current UTC DateTime lies, if by adding 12 or 13 we fall into the next daylight savings period we add the alternate instead....
IE if by adding 12 we fall into +13 territory.... add +13 instead.
IE if by adding +13 we fall into +12 territory.... add +12 instead.
New Zealand's daylight savings time changes on the last sunday of september and the first sunday of April.
This is the solution....
const UTCFromMS = (ms) => {
return new Date(new Date(ms).toUTCString().replace(" GMT", ""))
};
const addHours = (dte, hrs) => {
return new Date(
dte.getFullYear(),
dte.getMonth(),
dte.getDate(),
dte.getHours() + hrs,
dte.getMinutes(),
dte.getMilliseconds()
);
};
const toNewZealand = (ms) => {
return addNewZealandDaylightSavings(UTCFromMS(ms));
};
const getPreviousSunday = (dte) => {
return new Date(
dte.getFullYear(),
dte.getMonth(),
dte.getDate() - dte.getDay(),
1,
0,
0
);
};
const getNextSunday = (dte) => {
return new Date(
dte.getFullYear(),
dte.getMonth(),
dte.getDay() === 0 ? dte.getDate() : dte.getDate() + (7 - dte.getDay()),
1,
0,
0
)
};
const standardHours = 12;
const daylightHours = 13;
const addNewZealandDaylightSavings = (dte) => {
const lastSundaySeptember = getPreviousSunday(
new Date(dte.getFullYear(), 8, 30)
);
const firstSundayApril = getNextSunday(
new Date(dte.getFullYear(), 3, 1)
);
// If its before firstSundayApril, add 13, if we went over 1am, add 12.
if(dte <= firstSundayApril) {
const daylightNz = addHours(dte, daylightHours);
if(daylightNz >= firstSundayApril) {
return addHours(dte, standardHours);
}
return daylightNz
}
// if its before lastSundaySeptember, add 12 if we went over 1am add 13.
if(dte <= lastSundaySeptember) {
const standardNz = addHours(dte, standardHours);
if(standardNz >= lastSundaySeptember) {
return addHours(dte, daylightHours);
}
return standardNz;
}
return addHours(dte, daylightHours);
};
console.log(toNewZealand(new Date().getTime()).toString());
// the above line should always output the current DateTime in New Zealand, replace the argument with any epoch milliseconds and it should still always give you the correct time.
===EDIT====
The above answer has since become less relevant as now there's way to do this that are part of the web standard.
Date.prototype.toLocaleString AND timeZone: "Pacific/Auckland"
Is one of the simplest way's to convert dates today, you can read about it more on MDN here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString

how to get time zone of city or country in javascript

I use getTimezoneOffset() to get the offset in minutes for a given date object. Is is possible to use javascript to get timezone offset of a city or a country?
for example:
var offset = getCityOffset("Miami"); // returns -240
No, there is nothing built-in to javascript which allows you to get the number of minutes to offset for specific time zones/cities.
getTimeZoneOffset works for the current browser's settings
MomentJS Timezone extensions has some of this sort of functionality, which is of course reliant on the MomentJS library.
If you have access to Lat/Long values, then google provide a timezone API
I'm going to answer this question for Node.js using TypeScript (please remove the types if you want to use with plan JavaScript). For that, we will need 2 NPM packages.
moment-timezone
city-timezones
Disclaimer: Timezones are COMPLEX. Not all timezones are 1 hour apart and some daylight saving time settings are weird with, for example, 15 minutes difference instead of the standard 60 minutes. This is a naive implementation that suited my use case. Use with discretion.
Code:
import * as cityTimeZones from "city-timezones";
import * as moment from "moment-timezone";
/**
* Returns the UTC offset for the given timezone
* #param timezone Example: America/New_York
*/
export function getNormalizedUtcOffset(timezone: string): number | null {
const momentTimezone = moment.tz(timezone);
if (!momentTimezone) {
return null;
}
let offset = momentTimezone.utcOffset();
if (momentTimezone.isDST()) {
// utcOffset will return the offset normalized by DST. If the location
// is in daylight saving time now, it will be adjusted for that. This is
// a NAIVE attempt to normalize that by going back 1 hour
offset -= 60;
}
return offset/60;
}
/**
* Returns the offset range for the given city or region
* #param location
*/
export function getUtcOffsetForLocation(location: string): number[] | null {
const timezones = cityTimeZones.findFromCityStateProvince(location);
if (timezones && timezones.length) {
// timezones will contain an array of all timezones for all cities inside
// the given location. For example, if location is a country, this will contain
// all timezones of all cities inside the country.
// YOU SHOULD CACHE THE RESULT OF THIS FUNCTION.
const offsetSet = new Set<number>();
for (let timezone of timezones) {
const offset = getNormalizedUtcOffset(timezone.timezone);
if (offset !== null) {
offsetSet.add(offset);
}
}
return [...offsetSet].sort((a, b) => a - b);
}
return null;
}
Unit tests (with Jest)
import { getUtcOffsetForLocation } from "../timezone";
describe("timezone", () => {
describe("getUtcOffsetForLocation", () => {
it("should work for Lisbon", () => {
expect(getUtcOffsetForLocation("Lisbon")).toEqual([0]);
});
it("should work for Berlin", () => {
expect(getUtcOffsetForLocation("Berlin")).toEqual([1]);
});
it("should work for Germany", () => {
expect(getUtcOffsetForLocation("Germany")).toEqual([1]);
});
it("should work for the United States", () => {
// when the region has multiple timezones,
expect(getUtcOffsetForLocation("United States")).toEqual( [-10, -9, -8, -7, -6, -5, -4]);
});
it("should return null for a non-existing region", () => {
// when the region has multiple timezones,
expect(getUtcOffsetForLocation("Blablabla")).toEqual( null);
});
});
});
you can use toLocaleTimeString() to find out the time of a particular city of a aprticular country , For example i want to determine the current time in 24 hour of India , so run this script
`let indianTime = new Date().toLocaleTimeString("en-US",
{timeZone:'Asia/Kolkata',timestyle:'full',hourCycle:'h24'})
console.log(indianTime)`
similarly for time of Dhaka/Bangladesh we can do the same
`let bangladeshTime = new Date().toLocaleTimeString("en-US",
{timeZone:'Asia/Dhaka',timestyle:'full',hourCycle:'h24'})
console.log(bangladeshTime)`
here i used a parameter en-Us to get the standard time format
TimeZoneOffset :
var d = new Date()
alert(d.getTimezoneOffset());
toLocaleTimeString() : This converts time to the local.
var d = new Date();
alert(d.toLocaleTimeString());
Using a library: refer, Auto Time zone detection & momentjs
function getTimeOffset(country = 'America/New_York', summerTime = false) {
let date = new Date(new Date().getFullYear(), summerTime ? 6 : 11, 1);
let wordTime = new Date(date.toISOString().substr(0, 19)).getTime();
let localTime = new Date(date.toLocaleString('en', { timeZone: country })).getTime();
return (wordTime - localTime) / 1000 / 60;
}
There is no default method. Although, there are few ways you can do it easily using the same getTimeZoneOffSet method. One such tutorial is here. http://www.techrepublic.com/article/convert-the-local-time-to-another-time-zone-with-this-javascript/
function calcTime(city, offset) {
// create Date object for current location
d = new Date();
// convert to msec
// add local time zone offset
// get UTC time in msec
utc = d.getTime() + (d.getTimezoneOffset() * 60000);
// create new Date object for different city
// using supplied offset
nd = new Date(utc + (3600000*offset));
// return time as a string
return "The local time in " + city + " is " + nd.toLocaleString();
}
Note that this function requires you to pass the difference from GMT manually. You can use your code behind to get that parameter.

Categories