const all = ['2021-04-26T08:00:00', '2021-04-27T10:00:00',]
const range = ["2021-04-26T00:00:00.000Z",
"2021-04-26T01:00:00.000Z",
"2021-04-26T02:00:00.000Z",
"2021-04-26T03:00:00.000Z",
"2021-04-26T04:00:00.000Z",
"2021-04-26T05:00:00.000Z",
"2021-04-26T06:00:00.000Z",
"2021-04-26T07:00:00.000Z",
"2021-04-26T08:00:00.000Z",
"2021-04-27T00:00:00.000Z",
"2021-04-27T01:00:00.000Z",
"2021-04-27T02:00:00.000Z",
"2021-04-27T03:00:00.000Z",
"2021-04-27T04:00:00.000Z",
"2021-04-27T05:00:00.000Z",
"2021-04-27T06:00:00.000Z",
"2021-04-27T07:00:00.000Z",
"2021-04-27T08:00:00.000Z",
"2021-04-27T09:00:00.000Z",
"2021-04-27T10:00:00.000Z"
]
console.log(all, range)
is it possible to loop over a list of date like (all) and get all time range from midnight with moment.js like (range)
🙏🏻 any hint
Below is an example of achieving this. It simply loops until it finds the matching end timestamp and keeps pushing the ISO strings to the ranges array.
const all = ['2021-04-26T08:00:00Z', '2021-04-27T10:00:00Z'];
const end = moment(all[1]);
let current = moment(all[0]).startOf('day');
const ranges = [];
while (!current.isSame(end)) {
ranges.push(current.toISOString());
current = current.add(1, 'hour');
}
console.log(ranges);
<script src="https://cdn.jsdelivr.net/npm/moment#2.29.1/moment.js"></script>
Related
I am using a variety of moment.js methods to find the difference in milliseconds between values originally represented as dates. This is what I'm doing:
const stopTimeLong = '2021-05-19 09:54:08';
const startTimeLong = '2021-05-19 09:54:04';
const currentTimeLong = '2021-05-19 09:54:10';
// Get values in milliseconds
const stopTime = moment(stopTimeLong).valueOf();
const startTime = moment(startTimeLong).valueOf();
const currentTime = moment(currentTimeLong).valueOf();
const duration = moment.duration(stopTime - startTime).asSeconds(); // is 4
const final = moment.duration(currentTime - (stopTime - startTime)).asSeconds();
Now, the value for duration here gives me what I expect based on the initial times: 4
However the value for final is 1621432450 where I would expect 6.
What is the issue here? What do I need to change in order to get 6 as the final result based on the initial dates?
The last line should be:
const final = moment.duration(currentTime - startTime).asSeconds();
The stop time is irrelevant, based on the data you gave and the result you asked for.
I have 2 sheets viz., Sht1 & Sht2. I am looping through the ID column in Sht1 and trying to match it with ID column in Sht2. If there is a match, then check if the Date in Sht2 is different than the Date in Sht1. If it is, then update the Date in Sht1 with this new Date.
Sht1:
Sht2:
for e.g.:
From the above screenshots, the Sht1 matching ID's in Sht2 are 10001,10003,10006,10010,10011.
Of these, only 10001,10006,10011 have new Dates. So only for these ID's, these new Dates should get copied to Sht1.
As the Data is large, looping through the ID columns, matching them and then checking if the dates are different and finally updating new dates back to Sht1, is taking a lot of time. Is there an eloquent way to do this without looping and reading/writing to sheet - like picking up the Data in the 2 Sheets as JSON Objects viz., ObjSht1 & ObjSht2, updating the Dates for the matching ID's in ObjSht1, then transferring this JSON Object ObjSht1 to Sht1 in one go?
Edit:
#TheMaster the modified script is :
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sht1 = ss.getSheetByName("Sht1");
const sht2 = ss.getSheetByName("Sht2");
const idMap = sht2.getDataRange().getValues().reduce( (mp,[,id,,date]) => mp.set(id, date), new Map );
console.log(idMap);
const rg1 = sht1.getDataRange();
const values1 = rg1.getValues();
values1.forEach(row => row[2] = idMap.get(row[0]));
rg1.setValues(values1);
}
#TheMaster, This time it replaces the dates in Sht1 with the dates from Sht2 where the ID's match. But it also wipes out the other dates from Sht1 where the ID's do not match.
Also, I think the code is not comparing if dates have changed, but just dumping the dates from Sht2 to Sht1.
See updated Screenshots:
Create a map of {id => date} of the second sheet using reduce
Modify the first sheet values in place using forEach
Sample script:
function datesFromSht2ToSht1() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sht1 = ss.getSheetByName("Sht1");
const sht2 = ss.getSheetByName("Sht2");
const idMap = sht2.getDataRange().getValues().reduce( (mp,[,id,,date]) => mp.set(id, date), new Map );
const rg1 = sht1.getDataRange();
const values1 = rg1.getValues();
values1.forEach(row => row[2] = idMap.get(row[0]) || row[2]);
rg1.setValues(values1);
}
I have one array of dates, I want to create object containing start and end by checking continue dates.
EX.
dateArray = [
"2020-01-22T00:00:00.000Z",
"2020-01-23T00:00:00.000Z",
"2020-01-28T00:00:00.000Z",
"2020-01-29T00:00:00.000Z",
"2020-01-30T00:00:00.000Z",
"2020-01-31T00:00:00.000Z",
"2020-02-01T00:00:00.000Z",
"2020-02-02T00:00:00.000Z",
"2020-02-03T00:00:00.000Z",
"2020-02-04T00:00:00.000Z",
"2020-02-05T00:00:00.000Z",
"2020-02-06T00:00:00.000Z",
"2020-02-07T00:00:00.000Z",
"2020-02-16T00:00:00.000Z",
"2020-02-17T00:00:00.000Z",
"2020-02-18T00:00:00.000Z",
"2020-02-19T00:00:00.000Z",
"2020-02-20T00:00:00.000Z"
]
myRequirement = [{
start: "2020-01-22T00:00:00.000Z",
end: "2020-01-23T00:00:00.000Z"
},
{
start: "2020-01-28T00:00:00.000Z",
end: "2020-02-07T00:00:00.000Z"
},
{
start: "2020-02-16T00:00:00.000Z",
end: "2020-02-20T00:00:00.000Z"
}
]
I want to do this using in node.js.
I tried this using some nested for loops.
First i am running loop on main dateArray, Then checking is it first date or not, If it is first date then storing it as first objects start date, Then in next date case checking is it next most date of previous date or not.
let gapArray = [];
let startEndObj = {};
let tempStartDate;
let tempEndDate;
let tempNextDate;
await asyncForEach(finalAvailablityDatesArrayOFi.availeblityDatesArray, async (availeblityDatesArrayOFi) => {
console.log("availeblityDatesArrayOFi", availeblityDatesArrayOFi);
if (!tempStartDate) {
console.log("In if");
startEndObj.startDate = availeblityDatesArrayOFi;
tempStartDate = availeblityDatesArrayOFi;
let oneDatePlus = new Date(availeblityDatesArrayOFi).setDate(new Date(availeblityDatesArrayOFi).getDate() + 1);
tempNextDate = new Date(oneDatePlus);
console.log("startEndObj", startEndObj);
}
else if (tempStartDate) {
console.log("in else");
if (new Date(availeblityDatesArrayOFi).getTime() == new Date(tempNextDate).getTime()) {
console.log("Do nothing!");
tempStartDate = availeblityDatesArrayOFi;
tempEndDate = availeblityDatesArrayOFi;
let oneDatePlus = new Date(availeblityDatesArrayOFi).setDate(new Date(availeblityDatesArrayOFi).getDate() + 1);
tempNextDate = new Date(oneDatePlus);
}
else {
startEndObj.endDate = new Date(tempEndDate);
gapArray.push(startEndObj);
tempStartDate = '';
tempEndDate = '';
startEndObj = {};
}
}
});
Thank you!
Looks like a job for Array.prototype.reduce().
Note: hereafter assumption is made that few prerequisites are met:
source array items are valid ISO-formatted date strings or others, parseable by new Date() constructor, otherwise should be brought to one of supported format
source array items are sorted in ascending order, otherwise Array.prototype.sort() method must be applied in advance
array items do not include time of the day part (or this part is exactly the same for all items), otherwise consecutive date records may happen to have difference greater than 864e5 milliseconds (1 day) and more complex comparison is required
You may walk through your array and compare current items with previous/following, once you have a gap greater than 1 day you push new range into resulting array or modify end date for the last one:
const src = ["2020-01-22T00:00:00.000Z","2020-01-23T00:00:00.000Z","2020-01-28T00:00:00.000Z","2020-01-29T00:00:00.000Z","2020-01-30T00:00:00.000Z","2020-01-31T00:00:00.000Z","2020-02-01T00:00:00.000Z","2020-02-02T00:00:00.000Z","2020-02-03T00:00:00.000Z","2020-02-04T00:00:00.000Z","2020-02-05T00:00:00.000Z","2020-02-06T00:00:00.000Z","2020-02-07T00:00:00.000Z","2020-02-16T00:00:00.000Z","2020-02-17T00:00:00.000Z","2020-02-18T00:00:00.000Z","2020-02-19T00:00:00.000Z","2020-02-20T00:00:00.000Z"],
ranges = src.reduce((res,date,idx,self) => {
const rangeStart = !idx || new Date(date) - new Date(self[idx-1]) > 864e5,
rangeEnd = idx == self.length-1 || new Date(self[idx+1]) - new Date(date) > 864e5
if(rangeStart) res.push({startdate:date,enddate:date})
else if(rangeEnd) res[res.length-1]['enddate'] = date
return res
}, [])
console.log(ranges)
.as-console-wrapper {min-height:100%}
You need to be careful with this type of processing to determine all the business rules exactly. If the time component is not to be considered, then it should be removed, otherwise when comparing say 2020-01-01T00:00:00 to 2020-01-02T012:00:00 you will get a difference greater than 1 day but might not want it to be treated as the start of a new range.
For that reason, the "days difference" logic should be in a separate function, which also makes it easier to change date libraries if you're using one. The days difference is also signed, so make sure they are passed in the right order.
Otherwise, the following is pretty much the same as Yevgen's answer but a little more efficient I think as it only creates two Dates on each iteration instead of four.
let dateArray = [
"2020-01-22T00:00:00.000Z",
"2020-01-23T00:00:00.000Z",
"2020-01-28T00:00:00.000Z",
"2020-01-29T00:00:00.000Z",
"2020-01-30T00:00:00.000Z",
"2020-01-31T00:00:00.000Z",
"2020-02-01T00:00:00.000Z",
"2020-02-02T00:00:00.000Z",
"2020-02-03T00:00:00.000Z",
"2020-02-04T00:00:00.000Z",
"2020-02-05T00:00:00.000Z",
"2020-02-06T00:00:00.000Z",
"2020-02-07T00:00:00.000Z",
"2020-02-16T00:00:00.000Z",
"2020-02-17T00:00:00.000Z",
"2020-02-18T00:00:00.000Z",
"2020-02-19T00:00:00.000Z",
"2020-02-20T00:00:00.000Z"
];
// Simple difference in days function
function daysDiff(d0, d1) {
return Math.round((d1 - d0) / 8.64e7);
}
let ranges = dateArray.reduce((acc, curr, i, arr) => {
// If first date, initialise first object
if (!acc.length) {
acc.push({start: curr, end: curr});
} else {
let d0 = new Date(curr);
let d1 = new Date(arr[i-1]);
// If difference greater than 1 day, end previous range
// and start a new range
if (daysDiff(d1, d0) > 1) {
acc[acc.length - 1].end = arr[i-1];
acc.push({start: curr, end: curr});
}
}
return acc;
}, []);
console.log(ranges);
I am trying to parse a date from a text format to see if milliseconds and seconds were included in it.
For e.g.
let text = '2016-02-02 14:30:34.234';
const timezone = 'America/Los_Angeles';
// const hasMS = utcParse('%Y-%m-%d %H:%M:%S.')(text);
// const hasSeconds = utcParse('%Y-%m-%d %H:%M:')(text);
const hasMS = !!moment.tz(text, 'YYYY-MM-DD HH:MM:ss.', timezone);
console.log('hasMS', hasMS);
const hasSeconds = !!moment.tz(text, 'YYYY-MM-DD HH:MM:', timezone);
console.log('hasSeconds', hasSeconds);
Basically I am trying to replace the commented code. utcParse() from d3.time-format would check if the date text has milliseconds and seconds in it. I tried a couple of things for momentjs library, but it doesn't seem to work.
By using Regular expressions you can check if certain parts of the string are present.
If you call matchDateTime() with the string you get an array back with all the matched groups.
let text_min = '2016-02-02 14:30';
let text_sec = '2016-02-02 14:30:34';
let text_ms = '2016-02-02 14:30:34.234';
function matchDateTime(text) { return text.match(/(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/); }
function hasSeconds(text) { return !!matchDateTime(text)[6]; }
function hasMilliSeconds(text) { return !!matchDateTime(text)[7]; }
function testTimeString(text) {
console.log(text, "hasSeconds=", hasSeconds(text));
console.log(text, "hasMilliSeconds=", hasMilliSeconds(text));
}
testTimeString(text_min);
testTimeString(text_sec);
testTimeString(text_ms);
console.log(matchDateTime(text_ms));
I have an array of objects that have a keys called timestamp and motion. motion contains a value and timestamp contains a unix timestamp. I want to iterate over a number of the objects and find what "time of day" period they correspond to, I then want to total up the motion values for that given time of day and save the entire thing in an array of arrays. I want the duration to be changeable.
Let's say these are my objects;
{
timestamp: 1397160634,
motion: 2,
id: '534771d8c311731e21c75c9f'
},
{
timestamp: 1397160634,
motion: 3,
id: '534771d8c311731e21c75c9f'
}
Now I create my results array
var sampleDuration = 60; // Min
var minutesInDay = 1440;
var samplesPerDay = minutesInDay/sampleDuration;
var finalResultItem = []
for (var i = 0; i < samplesPerDay; i++) {
var IndividualresultArray = []
IndividualresultArray.push(60*i);
IndividualresultArray.push(0);
finalResultItem.push(IndividualresultArray);
}
I now have an array of arrays with each subarray's first item being a number (corresponding to a minute stamp) and the second value being zero.
I would now like to loop through all my objects and increment the second value (motion) based on the time of day range that is in the timestamp
_forEach(objects, function (object) {
{
// grab the timestamp
// figure out which minute range it coresponds to
// increment the array value that corresponds to the minute stamp
// rinse and repeat
}
this is where I go blank, I need the end result to look something like this
[[30, 5],[60, 20],[90, 5],[120, 0] .........]
or it could even look like this
[[000002400, 5],[000003000, 20],[000003600, 5],[000004200, 0] .........]
where the first value is a timestamp that ignores the year, month, and day, and only considers the time of day.
I have considered using moment.js in some capacity but I'm not sure how. Any help with this problem would be great.
I created a jsFiddle for you. The motion increment logic should look like (I'm using jQuery here but you get the point)
// Loop through and increment motion
$.each(objs, function (idx, obj) {
var date = new Date(obj.timestamp * 1000); // Convert to milliseconds
var minutesInDay = date.getUTCHours() * 60 + date.getUTCMinutes(); // Remove UTC for local time!
var minuteRange = Math.floor(minutesInDay / sampleDuration);
finalResultItem[minuteRange][1] += obj.motion;
});
EDIT: Removed some discussion after your edit. I also used more generic logic based on sampleDuration.
This should do it:
_forEach(objects, function (object) {
var date = new Date(objec.timestamp*1000);
var minuteOfDay = date.getUTCHours()*60+date.getUTCMinutes();
finalResultItem[minuteOfDay][1] += object.motion;
})
For a variable sample rate, employ a secondOfDay and divide that by your sampleDuration, then floor it to get your array index.