I have an array of some dates that have gaps in them. I want to be able to get the first day of each month in my dataset, given that gaps may fall on the 01 day of the month.
What is the best way to do this?
dates are in MM/DD/YYYY format.
dates = [10/31/2014, 11/03/2014, 11/04/2014, 11/05/2014, ...]
I want the result of the filter to be 11/03/2014, which is the first day of Nov 2014 that is contained in my data set. Weekends are omitted from the data set in this example.
But assuming my data set is large and continues with more gaps, I want to find the first day of every month.
function foo(list, month) {
var monthdates = list.filter(function(e,i,a) {
if(Number(e.split("/")[0]) == month)
return e;
});
var leastdateofmonth = monthdates.reduce(function(p,c,i,a) {
var pd = Number(p.split("/")[1]);
var cd = Number(c.split("/")[1]);
if(pd < cd)
return p;
else
return c;
});
console.log(leastdateofmonth);
}
//call it like this
foo(["10/31/2014", "11/03/2014", "11/04/2014", "11/05/2014"],11);
Test results:
[anupam#localhost ~]$ node sortmonth.js
11/03/2014
Call the function for each month and append it to a "leastdate" list. Here I called it for month 11.
Also consider, using a callback instead of returning, so that nothing gets blocked.
Related
I want to divide a given date range into months. And I want to separate these months according to weeks. I want to get the date from the start date to the end of the month at 7-day intervals. For example: (Like 15/12/2020, 22/12/2020, 29/12/2020 -newMonth- 05/01/2020)
It is difficult to understand with this explanation. After all I want to create a json like this.
Starting Date: 15/11/2020 - End Date: 20/01/2021
[
{
"dates": [
15, // Starting date(15/11/2020)
22, // 1 week later
29 // 1 week later
],
"firstDay": "15/11/2020"
},
{
"dates": [
6,
13,
20,
27
],
"firstDay": "06/12/2020"
},
{
"dates": [
3,
10,
17
],
"firstDay": "03/01/2021"
}
]
There are a few tricks that you can use to create the array structure you want:
Use a while loop, which keeps a current date that is constantly incremented by 7 days, to be compared against the end date. The current date should be initialized using the start date in the beginning of the loop.
To perform the "group by month" in your desired structure, you will need to using an object (which serves as a dictionary). The object will use the month + year combination as key, so that if your date ranges span over multiple years you won't collapse months from different years into the same entry.
For each iteration of the while loop, you construct the key from the current date, and then check if the key exists in your dictionary. If not, then we can push this object into your dictionary:
{
dates: [currentDate],
firstDate: currentDate
}
If it already exists, then you simply push the date into the dates array of the sub-object.
Finally, to convert your dictionary into the array strcuture you wanted, using ES6 Object.values() will work. See proof-of-concept below:
function generateRanges(startDate, endDate) {
let current = moment(startDate, 'DD/MM/YYYY');
const end = moment(endDate, 'DD/MM/YYYY');
// A dictionary to track unique month+year combinations
const daysByMonth = {};
while (current < end) {
// Construct key based on month+year
const key = `${current.month()}${current.year()}`;
const date = current.date();
// If key already exists, then we push into the `dates` array
if (key in daysByMonth) {
daysByMonth[key].dates.push(date);
}
// Otherwise we construct a brand new sub-object
else {
daysByMonth[key] = {
dates: [date],
// Since this is the first time we encounter the key,
// We can assume this is the earliest/first date of the month
firstDate: current.format('DD/MM/YYYY')
}
}
// At the end of the while loop, increment by a week, rinse and repeat
current.add(7, 'days');
}
// Once done, we only want the values in the dictionary
// We don't need to keep the unique month+year key
return Object.values(daysByMonth);
}
const range = generateRanges('15/11/2020', '20/01/2021');
console.log(range);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
The Moment.JS has an API for adding days to a date: the add function.
In order to build your final object, you would start from your startDate, and add 7 days for each loop iteration until reaching the end date. Like this:
let currentDate = moment(startDate);
while(currentDate < endDate) { // Make sure endDate is also a moment object
// Add the current date - you will want to adapt this
dates.push(currentDate.clone()); // Since .add() will mutate the currentDate object, clone the object when adding it to the array so that it's not affected by the add()
// Prepare for the next iteration
currentDate.add({days: 7});
}
As for month changes, remember the last month for every iteration (use month) and create a new object in your final array, then add dates to object instead of the previous one.
Side note: Months are zero-indexed in Javascript, which is not the case for the month day (date)
When dealing with date or datetime is always a good practice to use milliseconds. Also it is space efficient, a long integer = 4kb instead of a Date() object which is 32bytes.
In your case it is very straightforward to convert our dates to milliseconds, find how many weeks are between as integer and then run a loop increment by this iteration.
let from = new Date('2020/11/15').getTime();
let to = new Date('2021/01/20').getTime();
let week = 604800000;
let day = 86400000;
let allWeeks = [];
let current =0;
let weeks = (to-from)/day/7
for (i=0; i<weeks; i++){
allWeeks.push(new Date(from += week).toLocaleDateString())
}
console.log(JSON.stringify(allWeeks))
//["22/11/2020","29/11/2020","06/12/2020","13/12/2020","20/12/2020","27/12/2020","03/01/2021","10/01/2021","17/01/2021","24/01/2021"]
Finally you will have a list applicable for JSON to build any logic you prefer.
I hope to pin point a differed approach to your case!
I am trying to compare two dates in javascript without the time portions. The first date comes from a jquery datepicker and the second date comes from a string.
Although I could swear that my method worked a while ago it looks like it is not working now.
My browser is Firefox but I also need my code to work in IE.
function selectedDateRetentionDaysElapsed() {
var dateSelectedDate = $.datepicker.parseDate('dd/mm/yy', $('#selectedDate').val());
// dateSelectedDate is equal to date 2015-09-30T14:00:00.000Z
var parsedRefDate = isoStringToDate('2015-11-10T00:00:00');
var reportingDate = getPredfinedDateWithoutTime(parsedRefDate);
// reportingDate is equal to date 2015-11-10T13:00:00.000Z
var businessDayCountFromCurrentReportingDate = getBusinessDayCount(dateSelectedDate,reportingDate);
// businessDayCountFromCurrentReportingDate is equal to 39.9583333333336
if (businessDayCountFromCurrentReportingDate >= 40) {
return true;
}
return false;
}
function isoStringToDate(dateStr) {
var str = dateStr.split("T");
var date = str[0].split("-");
var time = str[1].split(":");
//constructor is new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
return new Date(date[0], date[1]-1, date[2], time[0], time[1], time[2], 0);
}
function getPredfinedDateWithoutTime(myDate) {
myDate.setHours(0,0,0,0)
return myDate;
}
My issues are...
My isoStringToDate function is returning a date with a time even though I am not specifying a time.
The setHours call on a date does not seem to be working either.
Can someone please help me with this.
The simplest way to get the number of whole days between two dates is to create two date objects for the subject dates that are set to the same time. Noon is convenient as it means the date part is unaffected by daylight saving (some places introduce it at midnight) if you happen to print out just the date part.
The following does all calculations in the time zone of the host system. UTC could be used (and the hours set to 0 as daylight saving isn't an issue at all), but it's more to type.
E.g.:
function differenceInDays(d0, d1) {
// Copy dates so don't affect originals
d0 = new Date(+d0);
d1 = new Date(+d1);
// Set to noon
d0.setHours(12,0,0,0);
d1.setHours(12,0,0,0);
// Get difference in whole days, divide by milliseconds in one day
// and round to remove any daylight saving boundary effects
return Math.round((d1-d0) / 8.64e7)
}
// Difference between 2015-11-12T17:35:32.124 and 2015-12-01T07:15:54.999
document.write(differenceInDays(new Date(2015,10,12,17,35,32,124),
new Date(2015,11,01,07,15,54,999)));
so in my backbone-app copyCLDRView I try to copy/paste weeks (and the underlying components/data). Or to be a little abstract, "copy one week and the models in the week into another week".
Now I generally want to implement a check if the target-week has at löeast one component on at least one day (targetweek from monday to sunday). To have a check if there are components in the targetweek, I have the following array of datestrings which contains all dates that have components:
debug("[copyCLDRView::initialize] -> allDaysWithComponents: ", this.allDaysWithComponents);
which for example could contain the following values (Datestrings in format DDMMYYYY):
[copyCLDRView::initialize] -> allDaysWithComponents: ["20042015", "21042015", "22042015", "23042015", "24042015", "27042015", "28042015", "29042015", "30042015", "01052015", "11052015", "12052015", "13052015", "14052015", "15052015", "18052015", "19052015", "20052015", "21052015", "22052015", "25052015", "26052015", "27052015", "28052015", "29052015", "01062015", "02062015", "03062015", "04062015", "05062015"]
Now I have to check if at least one of the logical dates in this array is in the same week than a given Moment.js-object (further called "moment"), which I managed to always be a monday.
paste: function (evt) {
//this.selected is my momentobject, e.g. Mon May 18 2015 00:00:00 GMT+0200
if (this.selected && this.selected !== null) {
//Here I need the check,
//Pseudocode: if weekHasComponent(this.selected, alldaysWIthComponents) ...
this.pasteData(this.selected, this.tmpStorage);
}
},
So in this example I chose the selected week to be the one starting with 18th of May 2015, now I want my check to return "true" when there is a component in at least one of the days of the week => looking above, it should return true if the array contains at least one of the following values: 18052015, 19052015, 20052015, 21052015, 22052015
So I'm asking myself if moment.js could help me out with this comparison here, but I didn't find something yet and hope you could help.
Best regards,
Dominik
Create moment objects out of your various date strings, then compare them using isSame inside of a call to Array.prototype.some (or alternatively, you can use underscore.js or lodash)
var dates = ["20150101", "20150201"]
var testDate = moment("20150102", "YYYYMMDD")
dates.some(function(date){
return moment(date, "YYYYMMDD").isSame(testDate,"week")
})
//Should return true for this first value of testDate
testDate = moment("20150115", "YYYYMMDD");
dates.some(function(date){
return moment(date, "YYYYMMDD").isSame(testDate,"week")
})
//Should return false for this
I have a a spreadsheet which I use to note how I spent my time on a project. In that spreadsheet I have a couple of columns, one of which is the time spent doing something and the other is the category of what that something is (for example meetings or accounting or calling customers). I am trying to write a script which I pass the name of the category and it then loops though all the rows to see if the category equals the category I passed it, and if so it adds the time to the counter. I am however having trouble adding the time together. What I have so far is this:
function getTimeInCat(cat){
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var time = new Date();
var counter = 0;
for(var i = 6; i < numRows; i++){
var carCell = "F" + i;
var cellName = "E" + i;
if(sheet.getRange(i, 6).getValue() == cat){
time += sheet.getRange(i, 5).getValue();
counter++;
}
}
return time;
}
Instead of what I want I get this in return:
Thu Jan 30 2014 18:29:22 GMT-0000 (GMT)Sat Dec 30 1899 00:50:39 GMT-0000 (GMT)Sat Dec 30 1899 00:05:39 GMT-0000 (GMT)
EDIT: It is giving the right amount of rows (The remnants of that test are still in the code)
Try var time = 0. Date() I think will transform your time to a date.
The values of those cells are actually Date objects. This is why you're getting a long string of timestamps when you add the values of the cells together. For whatever reason, Google decided that when you're entering a duration, it will actually be an offset of HH:mm:ss after 12/30/1899. Try changing the format of those times to a date (Format > Number > Date) and you'll see it changes to that date.
Luckily, with Date objects, you can use the getTime() function to get your total duration. getTime() gives you the number of milliseconds since 1/1/1970 so it will actually return very large negative numbers for those cells but that's easy enough to manage. You just need to create a Date object with 12/30/1899 as the date and subtract that from the value of the cell you want.
var base = new Date("12/30/1899").getTime(); // -2.2091436E12
var cell = sheet.getRange(row,col).getValue().getTime(); // some other large negative number
var delta = cell - base; // this should be your duration in milliseconds
Works in all browsers except Firefox, any ideas?
(function($){
$.relativetime = function(options) {
var defaults = {
time:new Date(),
suffix:'ago',
prefix:''
};
options = $.extend(true, defaults, options);
//Fixes NaN in some browsers by removing dashes...
_dateStandardizer = function(dateString){
modded_date = options.time.toString().replace(/-/g,' ');
return new Date(modded_date)
}
//Time object with all the times that can be used throughout
//the plugin and for later extensions.
time = {
unmodified:options.time, //the original time put in
original:_dateStandardizer(options.time).getTime(), //time that was given in UNIX time
current:new Date().getTime(), //time right now
displayed:'' //what will be shown
}
//The difference in the unix timestamps
time.diff = time.current-time.original;
//Here we save a JSON object with all the different measurements
//of time. "week" is not yet in use.
time.segments = {
second:time.diff/1000,
minute:time.diff/1000/60,
hour:time.diff/1000/60/60,
day:time.diff/1000/60/60/24,
week:time.diff/1000/60/60/24/7,
month:time.diff/1000/60/60/24/30,
year:time.diff/1000/60/60/24/365
}
//Takes a string and adds the prefix and suffix options around it
_uffixWrap = function(str){
return options.prefix+' '+str+' '+options.suffix;
}
//Converts the time to a rounded int and adds an "s" if it's plural
_niceDisplayDate = function(str,date){
_roundedDate = Math.round(date);
s='';
if(_roundedDate !== 1){ s='s'; }
return _uffixWrap(_roundedDate+' '+str+s)
}
//Now we loop through all the times and find out which one is
//the right one. The time "days", "minutes", etc that gets
//shown is based on the JSON time.segments object's keys
for(x in time.segments){
if(time.segments[x] >= 1){
time.displayed = _niceDisplayDate(x,time.segments[x])
}
else{
break;
}
}
//If time.displayed is still blank (a bad date, future date, etc)
//just return the original, unmodified date.
if(time.displayed == ''){time.displayed = time.unmodified;}
//Give it to em!
return time.displayed;
};
})(jQuery);
In Safari, this code returns the given date which my plugin date if it fails. This could happen due to a future date or an invalid date. However, I'm not sure as the time that is given is standard YY MM DD HH:mm:ss
Demo:
http://jsfiddle.net/8azeT/
I think the string used is wrong and then stripped of '-' very wrong:
'010111' - interpreted by FF as Jan 1 1911 (US FF)
Correct format is '01/01/2011' (US FF)
I wouldn't use this format at all as each country has it's own way of showing/ parsing dates.
The safest way to parse a string is probably to use:
'January 1, 2011 1:30:11 pm GMT'
but I would use a date object instead in the options and skip the string parsing to make sure the date is correct.
http://jsfiddle.net/8azeT/4/
Question is about Safari but content FF?