I have a date with a time, like this
const date = {year: 2020, month: 12, day: 31};
const time = {hours: 16, minutes: 2};
How do I get UTC representation of that time depending on a timezone?
(without using any libraries)
convetToUTC(date, time, "Europe/Moscow") // => <UTC timestamp>
convetToUTC(date, time, "America/New_York") // => <UTC timestamp>
Examples
convetToUTC(
{year: 2021, month: 7, day: 30},
{hours: 16, minutes: 15},
"Europe/Moscow"
) // => 1627650900
convetToUTC(
{year: 2021, month: 7, day: 30},
{hours: 16, minutes: 15},
"America/New_York"
) // => 1627676100
Piggy-backing on Achempion's response, I fixed the timezone offset calculation. The timezone date should be subtracted from the UTC date. The result of this difference should be in minutes.
You will need to then convert the minute offset back into milliseconds and subtract this from the date.
/**
* Calculates the timezone offset of a particular time zone.
* #param {String} timeZone - a database time zone name
* #param {Date} date - a date for determining if DST is accounted for
* #return {Number} returns an offset in minutes
* #see https://stackoverflow.com/a/68593283/1762224
*/
const getTimeZoneOffset = (timeZone = 'UTC', date = new Date()) => {
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
return (tzDate.getTime() - utcDate.getTime()) / 6e4;
}
const defaultDateConfig = { year: 0, month: 0, date: 0 };
const defaultTimeConfig = { hours: 0, minutes: 0, seconds: 0 };
const convetToUTC = (dateConfig, timeConfig, timeZone) => {
const { year, month, date } = { ...defaultDateConfig, ...dateConfig };
const { hours, minutes, seconds } = { ...defaultTimeConfig, ...timeConfig };
const d = new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds));
const offsetMs = getTimeZoneOffset(timeZone, d) * 6e4;
return (d.getTime() - offsetMs) / 1e3;
};
// Main
const date = { year: 2021, month: 7, date: 30 };
const time = { hours: 16, minutes: 15 };
console.log(convetToUTC(date, time, 'America/New_York')); // 1627676100
console.log(convetToUTC(date, time, 'Europe/Moscow')); // 1627650900
const dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
let date = new Date(Date.UTC(year, month, day, hour, minute, second));
let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
let offset = utcDate.getTime() - tzDate.getTime();
date.setTime( date.getTime() + offset );
return date;
};
dateWithTimeZone("America/New_York", 2021, 7 - 1, 30, 16, 15, 0).getTime() / 1000)
// => 1627676100
dateWithTimeZone("Europe/Moscow", 2021, 7 - 1, 30, 16, 15, 0).getTime() / 1000)
// => 1627650900
7 - 1 used to illustrate that function accepts month's index, not month's number
Related
I'm trying to get first available time either hour or hour and a half (will explain what I mean by hour and a half in an example) in a given day when I have other non-available hours in javascript.
Example #1:
If the current time is 8:45am and the unavailable hours are between "7:00am and 8:00am" + "10:00am and 11:00am" the first available time would be 9:00am.
Example#2:
If the current time is 1:35pm and the unavailable hours are between "2:00pm and 3:00pm" then the first available time is 3:30pm
Example #3:
If the current time is 11:00pm and the unavailable hours are between "11:30pm and 1:00pm" + "1:30pm and 2:00pm" then the first available time is 2:30pm
I'd suggest creating a function getFirstAvailableTime() to search for the desired time.
We'd start at now plus one minute, then iterate through each timeslot, checking each one to see if it overlaps any of our unavailable timeslots.
If it does not overlap an unavailable timeslot we return it as the first available time.
function getFirstAvailableTime(now, unavailable, timeSlotSize = 30) {
// Start at the first available timeslot _after_ now.
const start = Math.ceil((minutes(now) + 1) / timeSlotSize) * timeSlotSize;
for (let min = start; min < 1440; min += timeSlotSize) {
if (!unavailable.find(({ start, end }) => (min >= minutes(start) && min <= minutes(end)))) {
return { hour: Math.floor(min / 60), minute: min % 60 };
}
}
}
function minutes( { hour, minute }) {
return hour * 60 + minute;
}
function formatTime({ hour, minute }) {
return [hour, minute].map(n => (n + '').padStart(2, '0')).join(':');
}
function formatSpan({ start, end }) {
return [formatTime(start), formatTime(end)].join(' - ');
}
const inputs = [{ now: { hour: 8, minute: 45 }, unavailable: [{ start: { hour: 7, minute: 0}, end: { hour: 8, minute: 0} } ] },{ now: { hour: 13, minute: 35 }, unavailable: [{ start: { hour: 14, minute: 0}, end: { hour: 15, minute: 0} } ] },{ now: { hour: 11, minute: 0 }, unavailable: [{ start: { hour: 11, minute: 30}, end: { hour: 13, minute: 0} },{ start: { hour: 13, minute: 30}, end: { hour: 14, minute: 0} } ] } ]
console.log('Now'.padEnd(10, ' '), 'Unavailable'.padEnd(32, ' '), 'Next Slot');
for(let input of inputs) {
console.log(formatTime(input.now).padEnd(10, ' '), input.unavailable.map(formatSpan).join(', ').padEnd(32, ' '), formatTime(getFirstAvailableTime(input.now, input.unavailable)))
}
.as-console-wrapper { max-height: 100% !important; }
i want to get the list of next week days in given format "March 12 Sunday" and then convert that is final list of week days as given below.
Here is a my current code with which i am trying to get desire output but that returns "06/12/22" format..
Current code :
const nextWeek = [...Array(7).keys()].map(days => new Date(Date.now() + 86400000 * days).toLocaleDateString('en-us', { weekday:"long", month:"short", day:"numeric"}))
console.log("== > ",nextWeek)
current output :
["09/17/22", "09/18/22", "09/19/22", "09/20/22", "09/21/22", "09/22/22", "09/23/22"]
first i want this output
["Sunday, March 4", "Monday, March 4", "Tuesday, March 4", "Wednesday, March 4", "Thursday, March 4", "Friday, March 4", "Saturday, March 4"]
and then final desire output is:
const nextWeekdata = [
{ id: 1, name: "Sunday" ,date:21,Month:"March" },
{ id: 2, name: "Monday" ,date:22,Month:"March" },
{ id: 3, name: "Tuesday" ,date:23,Month:"March" },
{ id: 4, name: "Wednesday" ,date:24,Month:"March" },
{ id: 5, name: "Thursday" ,date:25,Month:"March" },
{ id: 6, name: "Friday" ,date:26,Month:"March" },
{ id: 7, name: "Saturday" ,date:27,Month:"March" },
];
If you want to calculate the range of dates for next week, break your logic into smaller chunks.
You should have three functions:
Function that gets the date for a given day of the week; next week
Function that gets the range of all the dates next week
Function to map the dates to object data
const getNextDay = (dayIndex) => {
const today = new Date();
today.setDate(today.getDate() + (dayIndex - 1 - today.getDay() + 7) % 7 + 1);
return today;
};
const getNextWeek = () => [...Array(7).keys()].map(getNextDay);
const nextWeek = getNextWeek().map((date, index) => ({
id: index + 1,
name: date.toLocaleDateString('en-us', { weekday: 'long' }),
date: date.getDate(),
month: date.toLocaleDateString('en-us', { month: 'long' }),
year: date.getFullYear()
}));
console.log(nextWeek);
.as-console-wrapper { top: 0; max-height: 100% !important; }
const nextWeek = [...Array(7).keys()].map((days) =>
new Date(Date.now() + 86400000 * days).toLocaleDateString("en-us", {
weekday: "long",
month: "short",
day: "numeric",
})
);
let newArray = [];
nextWeek.forEach((item, index) => {
let data = item.split(",");
let secondLength = data[1].length;
newArray.push({
id: index + 1,
name: data[0],
date: data[1].substring(secondLength - 2),
month: data[1].substring(1, secondLength - 3),
});
});
console.log("days", newArray);
does this work? yes
is this the right way to do it? nope
is this the easy way? for me, it is.
Actually, when I use your current code I get this as a result:
(7) ['Saturday, Sep 17', 'Sunday, Sep 18', 'Monday, Sep 19', 'Tuesday, Sep 20', 'Wednesday, Sep 21', 'Thursday, Sep 22', 'Friday, Sep 23']
Which means it works as you wish; what browser are you using?
Regarding the Second Question of yours, I'd have used something such as this:
class weekDay {
constructer(id, x) {
let name = x.split(", ")[0];
let day = x.split(", ")[1].split(" ")[1];
let month = x.split(", ")[1].split(" ")[0];
this.id = id;
this.name = name;
this.day = day;
this.month = month;
}
}
const nextWeek = [...Array(7).keys()].map(days => new Date(Date.now() + 86400000 * days).toLocaleDateString('en-us', { weekday:"long", month:"long", day:"numeric"}))
var arr = [];
for (let i=0; i<=6; i++) {
arr.push(new weekDay(i+1, nextWeek[i]));
}
This is my way of doing it, but take heed that I haven't tested it so it might have some syntax errors, if there was any, please notify me.
I would like to get the occurences of a day in a specific week (the week that includes today). I have an array of visits with a specified location and date that I want to transform.
const today = new Date(Date.parse("2021-05-13")); // set today
const numDay = today.getDate();
const twoDaysAgo = new Date();
twoDaysAgo.setDate(numDay - 2); // set 2 days ago from today
const twelveDaysAgo = new Date();
twelveDaysAgo.setDate(numDay - 12); // set 12 days ago from today
const visits = [{
location: "Paris",
date: today
},
{
location: "Berlin",
date: twoDaysAgo
},
{
location: "Brussels",
date: twoDaysAgo
},
{
location: "Rome",
date: twelveDaysAgo
}
];
I would like to process the data so that the number of times a day occurs in the specific week can be tracked as a number and the locations are stored in an array. If there are no visits for a certain day, they do not need to be stored and it should just say numVisits: 0.
This is the outcome I would like to achieve.
const thisWeekResult = [{
monday: {
numVisits: 0
},
tuesday: {
numVisits: 2,
locations: ["Berlin", "Brussels"]
},
wednesday: {
numVisits: 0,
},
thursday: {
numVisits: 1,
locations: ["Paris"]
},
friday: {
numVisits: 0,
},
saturday: {
numVisits: 0,
},
sunday: {
numVisits: 0,
}
}];
You could start with the beginning of the week (start in the code below being sunday just gone).
Then you reduce the 7 days beyond that date, and look for records on that dat from the original set using filter. Finally you just build up the object required for each day
const today = new Date(Date.parse("2021-05-13")); // set today
const numDay = today.getDate();
const twoDaysAgo = new Date();
twoDaysAgo.setDate(numDay - 2); // set 2 days ago from today
const twelveDaysAgo = new Date();
twelveDaysAgo.setDate(numDay - 12); // set 12 days ago from today
const visits = [{
location: "Paris",
date: today
},
{
location: "Berlin",
date: twoDaysAgo
},
{
location: "Brussels",
date: twoDaysAgo
},
{
location: "Rome",
date: twelveDaysAgo
}
];
const start = new Date()
start.setDate(today.getDate() - today.getDay())
const result = [1,2,3,4,5,6,7].reduce( (acc,d) => {
const date = new Date()
date.setDate(start.getDate() + d);
const day = new Intl.DateTimeFormat('en', {weekday:'long'}).format(date).toLowerCase();
const records = visits.filter(v => v.date.getYear() == date.getYear() && v.date.getMonth() == date.getMonth() && v.date.getDate() == date.getDate());
acc[day] = records.length
? {
numVisits: records.length,
locations: records.map(r => r.location)
}
: {numVisits: 0 };
return acc;
},{});
console.log(result);
There will be 2 inputs for dates and the output should look like this.
Assume that 2 inputs are 2020-01-01 and 2020-02-23
{
"2021-01" : 31,
"2021-02" : 23,
}
And the second thing is, if a month includes all its days, it should be represented as:
{
"2021-01" : {
days: 31,
isAll : true
},
"2021-02" : {
days: 23,
isAll : false
},
}
How could I do that?
You can use .reduce to iterate over the object entries, and for each key, save an object of the number of days and whether it is the last day:
const _getLastDay = date => {
const [year, month] = date.split('-');
return new Date(year, month, 0).getDate();
}
const getDates = (date1, date2) => {
const data = {
[`${date1.getFullYear()}-${('0'+(date1.getMonth()+1)).slice(-2)}`]: date1.getDate(),
[`${date2.getFullYear()}-${('0'+(date2.getMonth()+1)).slice(-2)}`]: date2.getDate()
};
return Object.entries(data).reduce((acc, [date,days]) => {
acc[date] = { days, isAll: _getLastDay(date)===days };
return acc;
}, {});
}
console.log( getDates(new Date("2021-01-31"),new Date("2021-02-23")) );
I am trying to implement a function using reduce that allows me to group an array of objects { date: '2019-03-11', count: 8 } by weeks. So far, I was able to group dates by weeks but I am having trouble to add together count if the date falls in the same week.
const dates = [
{ date: '2019-03-11', count: 8 },
{ date: '2019-03-12', count: 7 },
{ date: '2019-03-09', count: 6 },
{ date: '2019-02-27', count: 10 },
{ date: '2019-02-26', count: 11 },
{ date: '2019-02-22', count: 12 },
{ date: '2019-04-21', count: 3 },
{ date: '2019-04-18', count: 2 },
{ date: '2019-04-17', count: 4 },
{ date: '2019-04-19', count: 5 }
];
Date.prototype.getWeek = function() {
const onejan = new Date(this.getFullYear(), 0, 1);
return Math.ceil(((this - onejan) / 86400000 + onejan.getDay() + 1) / 7);
};
const groups = dates.reduce(function(acc, item) {
const today = new Date(item.date);
const weekNumber = today.getWeek(today);
// check if the week number exists
if (typeof acc[weekNumber] === 'undefined') {
acc[weekNumber] = [];
}
acc[weekNumber].push(item.date, item.count);
return acc;
}, {});
console.log(groups);
Current Result
Desired Result
[
{ weekStart: '2019-02-17', count: 12 },
{ weekStart: '2019-02-24', count: 21 },
{ weekStart: '2019-03-03', count: 6 },
{ weekStart: '2019-03-10', count: 15 },
{ weekStart: '2019-04-14', count: 11 },
{ weekStart: '2019-04-21', count: 21 }
]
where weekStart is the first date of the week (Sunday) by which it was grouped
SOLUTION
const dates = [
{ date: '2019-02-24', count: 10 },
{ date: '2019-02-25', count: 11 },
{ date: '2019-02-26', count: 12 },
{ date: '2019-03-09', count: 8 },
{ date: '2019-03-10', count: 7 },
{ date: '2019-03-11', count: 6 },
{ date: '2019-04-14', count: 3 },
{ date: '2019-04-15', count: 2 },
{ date: '2019-04-16', count: 4 },
{ date: '2019-04-22', count: 5 }
];
/**
* Returns the week number for this date. dowOffset is the day of week the week
* "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday),
* the week returned is the ISO 8601 week number.
* #param int dowOffset
* #return int
*/
Date.prototype.getWeek = function(dowOffset) {
/*getWeek() was developed by Nick Baicoianu at MeanFreePath: http://www.epoch-calendar.com */
dowOffset = typeof dowOffset == 'int' ? dowOffset : 0; //default dowOffset to zero
var newYear = new Date(this.getFullYear(), 0, 1);
var day = newYear.getDay() - dowOffset; //the day of week the year begins on
day = day >= 0 ? day : day + 7;
var daynum =
Math.floor(
(this.getTime() -
newYear.getTime() -
(this.getTimezoneOffset() - newYear.getTimezoneOffset()) * 60000) /
86400000
) + 1;
var weeknum;
//if the year starts before the middle of a week
if (day < 4) {
weeknum = Math.floor((daynum + day - 1) / 7) + 1;
if (weeknum > 52) {
nYear = new Date(this.getFullYear() + 1, 0, 1);
nday = nYear.getDay() - dowOffset;
nday = nday >= 0 ? nday : nday + 7;
/*if the next year starts before the middle of
the week, it is week #1 of that year*/
weeknum = nday < 4 ? 1 : 53;
}
} else {
weeknum = Math.floor((daynum + day - 1) / 7);
}
return weeknum;
};
function getWeekStart(date) {
var offset = new Date(date).getDay();
return new Date(new Date(date) - offset * 24 * 60 * 60 * 1000)
.toISOString()
.slice(0, 10);
}
function groupWeeks(dates) {
const groupsByWeekNumber = dates.reduce(function(acc, item) {
const today = new Date(item.date);
const weekNumber = today.getWeek();
// check if the week number exists
if (typeof acc[weekNumber] === 'undefined') {
acc[weekNumber] = [];
}
acc[weekNumber].push(item);
return acc;
}, []);
return groupsByWeekNumber.map(function(group) {
return {
weekStart: getWeekStart(group[0].date),
count: group.reduce(function(acc, item) {
return acc + item.count;
}, 0)
};
});
}
console.log(groupWeeks(dates));
You could take the weekday as offset and subtract the milliseconds from the given date.
Reducing works with a callback and a clsoure over the key with the week start date.
(m, { date, count }) => // outer callback with map and object
(k => m.set(k, (m.get(k) || 0) + count)) // closure over k and updating the count in map
(getWeekStart(date)) // get value for k
function getWeekStart(date) {
var offset = new Date(date).getDay();
return new Date(new Date(date) - offset * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
}
const
dates = [{ date: '2019-03-11', count: 8 }, { date: '2019-03-12', count: 7 }, { date: '2019-03-09', count: 6 }, { date: '2019-02-27', count: 10 }, { date: '2019-02-26', count: 11 }, { date: '2019-02-22', count: 12 }, { date: '2019-04-21', count: 3 }, { date: '2019-04-18', count: 2 }, { date: '2019-04-17', count: 4 }, { date: '2019-04-19', count: 5 }],
result = Array.from(
dates.reduce((m, { date, count }) =>
(k => m.set(k, (m.get(k) || 0) + count))(getWeekStart(date)),
new Map),
([date, count]) => ({ date, count })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }