Here is cutoffDate is passed as an argument to this method.
Sample value for cutoffDate = "2020-04-19 23:59:59"
If cutoffDate is past, as per the current time in Denver, return true
If cutoffDate is future, as per the current time in Denver, return false
let cutoffDate = "2020-04-19 23:59:59";
const currentDatetime = new Date(
new Date().toLocaleString("en-US", {
timeZone: "America/Denver",
})
);
const cutoffDateTime = new Date(cutoffDate);
console.log(currentDatetime + '\n' + cutoffDateTime);
console.log(currentDatetime < cutoffDateTime);
This method returns incorrect result, in some cases. This method is working correctly as expected in some cases and not working as expected in some cases.
Observation: As per logs, it tends to fail in IOS (iPhone). (in some cases)
As per my understanding, above code should function correctly always. But, I am unable to determine why it is failing in some cases.
What you're trying to do seems valid, however the way you're doing it is prone to failure.
"2020-04-19 23:59:59" is not a format supported by ECMA-262 so parsing is implementation dependent. Apple devices until very recently treated it as an invalid date, so this method will fail for any device running an earlier OS.
A better way is to manually parse the string to remove issues associated with the built–in parser. Similarly for the return value from toLocaleString.
The following manually parses the timestamp, then uses Intl.DateTimeFormat with formatToParts to get the date and time values for any IANA representative location. It uses Date.UTC to generate time values to compare and avoid local DST issues.
Note that if the input timestamp represents a date and time that doesn't exist in the location (i.e. it's in the period when clocks are advanced going into DST) or exists twice (i.e. it's in the period when clocks are wound back going out of DST) then results may or may not be "correct". However, this is an issue for any system comparing such dates and times.
// Return true if current time is before timestamp at loc
function nowIsBeforeDateAtLoc(ts, loc = 'UTC') {
// Get time value for timestamp parsed as UTC
let [Y,M,D,H,m,s] = ts.split(/\D/);
let tsTV = Date.UTC(Y,M-1,D,H,m,s);
// Get current date and time values for loc
let {year, month, day, hour, minute, second} = new Intl.DateTimeFormat('en', {
year:'numeric',
month:'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: false,
timeZone: loc
}).formatToParts(new Date()).reduce((acc, part) => {
acc[part.type] = part.value;
return acc;
}, Object.create(null));
// Get UTC time value for loc
let locTV = Date.UTC(year, month-1, day, hour, minute, second);
// Debug
// Parsed timestamp
console.log('Cutoff: ' + new Date(tsTV).toISOString().slice(0,-1));
// Current date and time at loc
console.log('Now ' + loc + ' : ' + new Date(locTV).toISOString().slice(0,-1));
return locTV < tsTV;
}
// Example
let cutoffTime = "2020-04-19 23:59:59";
//
console.log('Now is before cutoff at loc? ' + nowIsBeforeDateAtLoc(cutoffTime, 'America/Denver'));
You could also use a library to parse the timestamp for a particular location then compare it to "now", which might be simpler. See this answer to How to initialize a JavaScript Date to a particular time zone.
Related
I'm obtaining data.value which is a time in the format: hh:mm a - for example 12:30 am.
I also know:
the local timezone of the user (userTimeZone)
the timezone of the venue (venueTimeZone)
I need to convert the time selected by the user (data.value) to the correct date in the venueTimeZone. For example, if the user is in Americas/New York and they selected 1:30PM on the 20/05/2022, and the venue is in Americas/Los Angeles - the value I am interested in obtaining is 20/05/2022 10:30AM.
This is my attempt, however the timezone itself doesn't change - I think this is because when I create the userDateTime with moment I don't specify a time offset, but I'm not sure how to obtain the offset from userTimeZone, whilst accounting for DST.
const userTimeZone = _.get(
Intl.DateTimeFormat().resolvedOptions(),
['timeZone']
);
const venueDateStr = new Date().toLocaleString('en-US', {
timeZone: venueTimeZone,
});
const Date = new Date(restaurantDateStr);
const venueYear = venueDate.getFullYear();
const venueMonth = `0${venueDate.getMonth() + 1}`.slice(-2);
const venueDateOfMonth = `0${venueDate.getDate()}`.slice(-2);
const userDateTime = createDateAsUTC(
moment(
`${venueDateOfMonth}/${venueMonth}/${venueYear} ${data.value}`,
'DD/MM/YYYY hh:mm a'
).toDate()
).toLocaleString('en-US', { timeZone: venueTimeZone });
EDIT - I do not have the city offset, I have the timezone name, therefore I cannot use any suggested answer which relies on city offset.
Consider using Luxon - the successor to Moment. (See Moment's project status.)
// Parse your input string using the user's local time zone
// (this assumes the current local date)
const local = luxon.DateTime.fromFormat('1:30 pm', 'h:mm a');
// Convert to the desired time zone
const converted = local.setZone('America/Los_Angeles');
// Format the output as desired
const formatted = converted.toFormat('dd/MM/yyyy h:mm a').toLowerCase();
console.log(formatted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.4.0/luxon.min.js"></script>
You could also do this without a library, however you may find that not all browsers will parse the input string, and your output format is up to the browser as well.
// Get the current local date as a string
const date = new Date().toLocaleDateString();
// Parse the date and time in the local time zone
// Warning: This is non-standard and may fail in some environments
const dt = new Date(date + ' 1:30 pm');
// Format the output, converting to the desired time zone
const result = dt.toLocaleString(undefined, { timeZone: 'America/Los_Angeles' });
console.log(result);
There are, of course, manual ways to parse and format dates (using regex, etc.) but I'll leave that up to you or another person to complete.
My script like this :
function checkDatetime() {
const e = new Date((new Date).toLocaleString("en-US", {
timeZone: "Asia/Jakarta"
}));
return e.getDay() >= 1 && e.getDay() <= 5 && e.getHours() >= 8 && e.getHours() < 17
}
if checkDatetime return true, it will show message "Available"
if checkDatetime return false, it will show message "Not Available"
I had testing it and it works. My position at jakarta(indonesia)
But there are people who call the function at 18:20 and 22:22 and it show "Available'
Should it show "Not available"
Is this a timezone problem?
Update :
Another solution from me :
function checkDatetime() {
const offsetHours = new Date().getTimezoneOffset() / 60;
const day = new Date();
day.setHours(day.getHours() + offsetHours);
return day.getDay() >= 1 && day.getDay() <= 5 && day.getHours() >= 1 && day.getHours() < 10;
}
You have a fundamental misunderstanding of ECMAScript Date objects. They are just an offset from 1970-01-01T00:00:00Z and have no other associated data such as timezone or offset.
The expression:
new Date((new Date).toLocaleString("en-US", {
timeZone: "Asia/Jakarta"
}));
(new Date) creates a date for the current time, calling toLocaleString with options produces a timestamp for the current date and time for location "Asia/Jakarta".
That is then parsed by the enclosing call to the Date constructor to produce a Date for the same instant in time. If the string produced includes the timezone name or offset for "Asia/Jakarta" (which it may or may not as it's implementation dependent) it's the same as:
new Date();
So getDay returns the day for the local timezone, not "Asia/Jakarta". Note also that the built–in parser may not correctly parse the string returned by toLocaleString, especially where options have been used.
If you want to use built–in methods, consider the Intl.DateTimeFormat constructor and the formatToParts method. That will return string values for the date and time parts for a particular representative location like "Asia/Jakarta". You can then do things with the parts as you would with Date methods.
Note that some of the date parts have different names to the related date methods, also the day names are returned rather than numbers.
function checkDatetime(loc) {
let f = new Intl.DateTimeFormat('en', {
year : 'numeric',
month : '2-digit',
day : '2-digit',
weekday : 'short',
hour : '2-digit',
hour12 : false,
minute : '2-digit',
second : '2-digit',
timeZone: loc
});
let temp = f.formatToParts(new Date());
let parts = temp.reduce((acc, part) => {
if (part.type != 'literal') {
acc[part.type] = part.value;
}
return acc;
}, Object.create(null));
// Debug
console.log(parts);
return parts.weekday != 'Sat' && parts.weekday != 'Sun' &&
parts.hour >= 8 && parts.hour < 17;
}
console.log(checkDatetime('Asia/Jakarta'));
JavaScript's Date object records and tracks the time in UTC internally, but it accepts input and produces output in the local time on which it's running. It has very few facilities for working with time in other time zones.
You can use library moment.js and then use the function
var timeDifference = new Date().getTimezoneOffset();
to calculate the timezone difference of client from UTC.
Or, you can use
var timeZone = moment.tz.guess();
It will return an IANA time zone identifier, such as America/Los_Angeles for the US Pacific time zone.
This is one of the picture from moment.js website where you can see the way to handle it.
I tested your function and it works the way it should, ie, return true if it's M-F, 8-5 in JKT and false if it's not.
I can think of two possible problems:
1. There's a problem with the function calling checkDateTime and returning "Available" or "Not Available"
2. Your server is not set to the current date and time, and therefore even if you convert the datetime to JKT timezone, your function will not return the expected output.
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.
Mongodb stores date object in UTC. I need to query client entered tasks based on "today". Today varies from the user's perspective, who can be in Eastern time zone or Pacific. Here's the issue I ran into:
Record is entered as "2017-05-10 15:15:11.283Z". When I query the server later at night, the server date will be already 2017-05-11, however from the client's perspective it is still 2015-05-10.
I need to find all the tasks client entered "today".
I tried this:
let start = new Date();
start.setHours(0,0,0,0);
let end = new Date();
end.setHours(23,59,59,999);
But while that works during the day (same day), it does not show the records when the client queries at night, but before midnight.
Based on the comments below and my own brainstorming, I now pass client date to the server, and convert to UTC, like this:
let clientDate = req.params.month +'/'+ req.params.date +'/'+ req.params.year; //'5/10/2017'
console.log('clientDate ' + clientDate);
let start = new Date(clientDate);
start.setHours(0,0,0,0);
start = convertDateToUTC(start);
let end = new Date(clientDate);
end.setDate(end.getDate() + 1); //need this, otherwise queries next day
end.setHours(0,0,0,0);
end = convertDateToUTC(end);
console.log('start ' + start +' end ' + end);
return Taks.find({goalId: {$in: goalIds}, createdAt: {$gte: start, $lt: end}}).lean() //only today
//using function found on stack overflow
function convertDateToUTC(date) {
return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
}
It works correctly, it just seems hacky to supply client date to the server.
If I understand you correctly, dates are stored in the database strings with timezone offset +00:00. You want to get Date objects for the start and end of "today" in the client's time zone (whatever that might be).
The following assumes (possibly incorrectly) that the values in req.params are UTC, but are being treated as local on the host system doing the processing:
let clientDate = req.params.month +'/'+ req.params.date +'/'+ req.params.year; //'5/10/2017'
console.log('clientDate ' + clientDate);
let start = new Date(clientDate);
That is why your dates are out by the timezone offset and you have to shift them back.
You should not create a string then leave it up to the Date constructor to parse it. Give the values directly to the Date constructor.
Next, you seem to want create a Date for the start and end of the local day that the UTC date falls on, so the following should do that:
var req = {
params: {
year: '2017',
month: '5',
date: '10'
}
};
// No parsing, treat date values as UTC
var clientDate = new Date(Date.UTC(req.params.year, req.params.month - 1, req.params.date));
// Copy date
var start = new Date(clientDate);
// Set to local start of day
start.setHours(0,0,0,0);
// Copy start to end and add 24 hours
var end = new Date(start);
// Adjusting UTC hours avoids daylight saving issues
// Could also just add 1 to the date
end.setUTCHours(end.getUTCHours() + 24)
console.log('Client date: ' + clientDate.toISOString() +
'\nstart date : ' + start.toString() +
'\nend date : ' + end.toString());
For me, being east of Greenwich, the start of 10 May UTC falls on 10 May in my local timezone so I see start and end dates for 10 May. For users west of Greenwich, 10 May starts on 9 May, so they will see start and end dates for 9 May.
I hope that makes sense (and is what you were after).
If, on the other hand, the values in req.params are client values, you must know the client timezone offset in order to adjust them to the correct date.
I am struggling to find out the beginning of day factoring in timezones in javascript. Consider the following:
var raw_time = new Date(this.created_at);
var offset_time = new Date(raw_hour.getTime() + time_zone_offset_in_ms);
// This resets timezone to server timezone
var offset_day = new Date(offset_time.setHours(0,0,0,0))
// always returns 2011-12-08 05:00:00 UTC, no matter what the offset was!
// This has the same issue:
var another_approach_offset_day = new Date(offset_time.getFullYear(),offset_time.getMonth(),offset_time.getHours())
I expect when i pass a Pacific Timezone offset, to get: 2011-12-08 08:00:00 UTC and so on.
What is the correct way to achieve this?
I think that part of the issue is that setHours method sets the hour (from 0 to 23), according to local time.
Also note that I am using javascript embedded in mongo, so I am unable to use any additional libraries.
Thanks!
Jeez, so this was really hard for me, but here is the final solution that I came up with the following solution. The trick was I need to use setHours or SetUTCHours to get the beginning of a day -- the only choices I have are system time and UTC. So I get the beginning of a UTC day, then add back the offset!
// Goal is given a time and a timezone, find the beginning of day
function(timestamp,selected_timezone_offset) {
var raw_time = new Date(timestamp)
var offset_time = new Date(raw_time.getTime() + selected_timezone_offset);
offset_time.setUTCHours(0,0,0,0);
var beginning_of_day = new Date(offset_time.getTime() - selected_timezone_offset);
return beginning_of_day;
}
In JavaScript all dates are stored as UTC. That is, the serial number returned by date.valueOf() is the number of milliseconds since 1970-01-01 00:00:00 UTC. But, when you examine a date via .toString() or .getHours(), etc., you get the value in local time. That is, the local time of the system running the script. You can get the value in UTC with methods like .toUTCString() or .getUTCHours(), etc.
So, you can't get a date in an arbitrary timezone, it's all UTC (or local). But, of course, you can get a string representation of a date in whatever timezone you like if you know the UTC offset. The easiest way would be to subtract the UTC offset from the date and call .getUTCHours() or .toUTCString() or whatever you need:
var d = new Date();
d.setMinutes(d.getMinutes() - 480); // get pacific standard time
d.toUTCString(); // returns "Fri, 9 Dec 2011 12:56:53 UTC"
Of course, you'll need to ignore that "UTC" at the end if you use .toUTCString(). You could just go:
d.toUTCString().replace(/UTC$/, "PST");
Edit: Don't worry about when timezones overlap date boundaries. If you pass setHours() a negative number, it will subtract those hours from midnight yesterday. Eg:
var d = new Date(2011, 11, 10, 15); // d represents Dec 10, 2011 at 3pm local time
d.setHours(-1); // d represents Dec 9, 2011 at 11pm local time
d.setHours(-24); // d represents Dec 8, 2011 at 12am local time
d.setHours(52); // d represents Dec 10, 2011 at 4am local time
Where does the time_zone_offset_in_ms variable you use come from? Perhaps it is unreliable, and you should be using Date's getTimezoneOffset() method. There is an example at the following URL:
http://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp
If you know the date from a different date string you can do the following:
var currentDate = new Date(this.$picker.data('date'));
var today = new Date();
today.setHours(0, -currentDate.getTimezoneOffset(), 0, 0);
(based on the codebase for a project I did)
var aDate = new Date();
var startOfTheDay = new Date(aDate.getTime() - aDate.getTime() % 86400000)
Will create the beginning of the day, of the day in question
You can make use of Intl.DateTimeFormat. This is also how luxon handles timezones.
The code below can convert any date with any timezone to its beginging/end of the time.
const beginingOfDay = (options = {}) => {
const { date = new Date(), timeZone } = options;
const parts = Intl.DateTimeFormat("en-US", {
timeZone,
hourCycle: "h23",
hour: "numeric",
minute: "numeric",
second: "numeric",
}).formatToParts(date);
const hour = parseInt(parts.find((i) => i.type === "hour").value);
const minute = parseInt(parts.find((i) => i.type === "minute").value);
const second = parseInt(parts.find((i) => i.type === "second").value);
return new Date(
1000 *
Math.floor(
(date - hour * 3600000 - minute * 60000 - second * 1000) / 1000
)
);
};
const endOfDay = (...args) =>
new Date(beginingOfDay(...args).getTime() + 86399999);
const beginingOfYear = () => {};
console.log(beginingOfDay({ timeZone: "GMT" }));
console.log(endOfDay({ timeZone: "GMT" }));
console.log(beginingOfDay({ timeZone: "Asia/Tokyo" }));
console.log(endOfDay({ timeZone: "Asia/Tokyo" }));