I have an HTML page where I have applied some Knockout bindings to some elements:
<td class="tepvval" data-bind="text: tepv"></td>
<td class="dateval" data-bind="text: dueIn(due)"></td>
In the second element, I call a function to populate the data content of the element. I'm trying to do the same for the style of the element, but I'm having issues. I need to call a function to compute the difference between the date (due) and today, and then return the color I want to use for the background of the element - green is due more than 30 days, yellow is 30-15 days, orange 15-1 days, red is overdue.
I tried using
<td class="dateval" data-bind="text: dueIn(due),
style: { backgroundColor: colorDue(due) }"></td>
but that doesn't work.
what am I missing in the syntax?
here is the function code I'm calling in colorDue:
function colorDue(due) {
rd = 1;
od = 15;
yd = 30;
var difference = dateDiff(due);
if (difference>yd) { color="green"; }
else if (yd>difference && difference>od) { color="yellow"; }
else if (od>difference && difference>rd) { color="orange"; }
else if (difference <=rd) { color="red"; }
return color;
}
function dateDiff(due) {
var df, ymd, now, dateStr, diff;
df = due.split(" ");
ymd = df[0].split("-");
now = new Date();
dateStr = new Date(ymd[0],ymd[1],ymd[2],17,0,0);
diff = dateStr.getTime()-now.getTime();
diff = diff/86400000;
return diff;
}
Your problem is here:
dateStr = new Date(ymd[0],ymd[1],ymd[2],17,0,0);
In js (don't ask why), month starts at 0 (while day and year starts at 1...). See doc
month Integer value representing the month, beginning with 0 for
January to 11 for December.
So you need a -1 here:
dateStr = new Date(ymd[0],ymd[1] -1,ymd[2],17,0,0);
There are other stuff that you should change here, like declaring your variables local:
function colorDue(due) {
var rd = 1;
var od = 15;
var yd = 30;
var color = "";
//...
}
Related
I'm currently stuck to find a way to;
When user select 2 dates (Check in & Check out), it'll count how many days - my count days function totally not working
Then, to check the date range if it falls under certain dates have different rates- I stored the rates and range of dates in arrays, then check wit if statement - It might be another ways to iterate those rates and range of dates
Finally, to display how much $ the total stay
This is for my learning purposes, not for commercial. This question might be useful for other learners as well. I know we can use other frameworks but I don't want to use it.
document.getElementById('checkInDate').addEventListener("focus", dateInputMinMax);
document.getElementById('checkOutDate').addEventListener("focus", dateInputMinMax);
function dateInputMinMax(){
let todayDate = new Date().toISOString();
let todayDateISO = todayDate.slice(0,10);
document.getElementById('checkInDate').min = todayDateISO;
document.getElementById('checkOutDate').min = todayDateISO;
//--------- Max: ADD 3 months to today's date -----------
let date = new Date();
//change the date to be today + 3 months
date.setMonth(date.getMonth()+3);
let dateThreeMonthsISO = date.toISOString();
dateThreeMonthsISO = dateThreeMonthsISO.slice(0,10);
document.getElementById('checkInDate').max = dateThreeMonthsISO;
document.getElementById('checkOutDate').max = dateThreeMonthsISO;
}
var rates = new Array();
rates[0] = 200;
rates[1] = 200;
rates[2] = 220;
rates[3] = 250;
var dateStartQ1 = new Date("2021-06-01");
var dateEndQ1 = new Date("2021-08-31");
var dateStartQ2 = new Date("2021-09-01");
var dateEndQ2 = new Date("2021-12-18");
var dateStartQ3 = new Date("2022-02-01");
var dateEndQ3 = new Date("2022-05-31");
var dateStartQ4 = new Date("2021-12-19");
var dateEndQ4 = new Date("2022-01-31");
var a, b, c, d;
dateStartQ1, dateEndQ1 = a;
dateStartQ2, dateEndQ2 = b;
dateStartQ3, dateEndQ3 = c;
dateStartQ4, dateEndQ4 = d;
function rateCalculator(){
if (dateInputMinMax == "") {
alert("Invalid Input");
return false;
} else if (dateInputMinMax == a) {
alert(rates[0]);
return false;
} else if (dateInputMinMax == b) {
alert(rates[1]);
return false;
} else if (dateInputMinMax == c) {
alert(rates[2]);
return false;
} else {
alert(rates[3]);
return false;
}
}
document.getElementById('totalDays').innerHTML = rateCalculator();
<form>
<label for="check_in">Check-In Date : </label>
<input type="date" id="checkInDate" min="">
<label for="check_out">Check-Out Date : </label>
<input type="date" id="checkOutDate">
<input type="submit" onclick="dateInputMinMax()">
<label for="totalDays" id="totalDays">Total Stay: </label>
</form>
function Weekday (name, traffic) {
this.name = name;
this.traffic = traffic;
}
function mostPopularDays(week) {
week = [];
week.push(this.name, this.traffic);
if(week.length > 1){
return week;
}else if(week.length === null || week === []){
return null;
}else {
return week[0];
}
}
var mon = new Weekday("Monday", 200);
mostPopularDays(mon);
/**
* Determines which day of the week had the most nnumber of people visiting the
* pet store. If more than one day of the week has the same, highest amount of
* traffic, an array containing the days (in any order) should be returned.
* (ex. ["Wednesday", "Thursday"]). If the input is null or an empty array, the function
* should return null.
* #param week an array of Weekday objects
* #return a string containing the name of the most popular day of the week if there is only one most popular day, and an array of the strings containing the names of the most popular days if there are more than one that are most popular
*/
I've been seriously learning Javascript for over 2 weeks now then I took this course in EDx and apparently, still can't solve this kind of problem. The code above is my own code and I know its wrong(except the Weekday function). A help would be gladly appreciated.Thanks
The mostPopularDays function never actually uses the provided argument as it is replaced on the first line.
function mostPopularDays(week) {
week = [];
Then there is an out of context this, the function mostPopularDays is not related in any way to the WeekDay, so the this you are referring to is actually the mostPopularDays function itself and not the intended WeekDay you provided.
From what I understand from the text describing the assignment there should be more than one argument to mostPopularDays in order to filter out the most most popular ones ("#param week an array of Weekday objects").
So, your TODO-list is as follows:
rename the argument to mostPopularDays to represent the input to be an array of WeekDay (I'd use weeks (+s) or weekList)
there is no need to push anything into the array from within mostPopularDays.
there is no use for this within mostPopularDays, you want to be referring to an item in the provided array of WeekDay objects
I don't known which ways of traversing an array your course had covered so far, there are several:
for (var i = 0; i < weekList.length; ++i) { ... }
weekList.forEach(function(week) { ... })
(If you've already covered the reducing and sorting of arrays, you may want to look into those, as that would be my preferred choice)
For test you should create objects(days) and array of objects.
Then sort objects and check if there is more than one day you should return.
I also made a condition to give full answer if there is more than one day with highest traffic.
var mon = new Weekday('Monday', 5);
var tue = new Weekday('Tuesday', 3);
var wed = new Weekday('Wednesday',8);
var thu = new Weekday('Thursday', 5);
var fri = new Weekday('Friday', 14);
var sat = new Weekday('Saturday', 14 );
var sun = new Weekday('Sunday', 0);
var weekDays = [mon, tue, wed, thu, fri, sat, sun];
function mostPopularDays(week) {
week.sort(compare);
var mostPopularDay = [];
var mostPopularDayValue = week[0].traffic;
week.forEach(day => {
if(day.traffic == mostPopularDayValue){
mostPopularDay.push(day.name);
}
});
if(mostPopularDay.length>1){
console.log('The most popular days are: '+ mostPopularDay);
} else{
console.log('The most popular day is '+ mostPopularDay);
}
}
function compare(a,b) {
if (a.traffic < b.traffic)
return 1;
if (a.traffic > b.traffic)
return -1;
return 0;
}
function Weekday (name, traffic) {
this.name = name;
this.traffic = traffic;
}
mostPopularDays(weekDays);
This is easy and you can understand quickly.
function mostPopularDays(week) {
if(week.length === 0){
return null;
}
var maxArray = [];
var max = '';
for(i=0; i < week.length; i++){
if(week[i].traffic === max){
maxArray.push(week[i].name);
}else if(week[i].traffic > max){
maxArray = [];
maxArray.push(week[i].name);
max = week[i].traffic;
};
}
if(maxArray.length === 1){
return maxArray[0];
} else {
return maxArray;
}
}
How can I create an array of dates with format DD-MM-YYYY from today and 1 ahead?
I guess it's something like
var dates = [];
var date = moment();
while (date <= date.clone().add(1, 'month')) {
dates.push(date.format('DD-MM-YYYY'));
date = date.clone().add(1, 'd');
}
but is this the best way to do it?
And how can I do the same with minutes? I want an array with ['00:00', '00:05', '00:10', ..., '23:50', '23:55'].
I guess it's something like
var minutes = [];
var time = moment('00:00', 'hh:mm');
while (time < time.clone().add(1, 'day')) {
minutes.push(time.format('hh:mm'));
time = time.clone().add(5, 'minutes');
}
It's not important to use moment.js for this, but I guess it's easier.
Since these can be general functionality, you should make them configurable.
Time Array
For Time array, i guess creating moment object and manipulating its values will be a waste of resource. You can do that with normal loops.
Non moment version
function getDoubleDigits(str) {
return ("00" + str).slice(-2);
}
function formatTime(h, m, is24Hr) {
var tmp = "";
if(is24Hr){
tmp =" " + (Math.floor(h/12) ? "p.m." : "a.m.")
h=h%12;
}
return getDoubleDigits(h) + ":" + getDoubleDigits(m) + tmp;;
}
function getTimeByInterval(interval, is24HrFormat) {
var times = []
for (var i = 0; i < 24; i++) {
for (var j = 0; j < 60; j += interval) {
times.push(formatTime(i, j, is24HrFormat))
}
}
return times.slice(0);
}
console.log(getTimeByInterval(5, false))
console.log(getTimeByInterval(5, true))
Date Array
Since you want dates between 2 dates with a specific interval, its better to make them configurable:
Moment version
I have made even format configurable in this version. This can be done in non-moment version as well but I guess that(how to format date in pure JS) goes out of question's scope and so not doing that.
function getDatesInrange(d1, d2, interval, format){
var dates = [];
while(d1.isBefore(d2)){
dates.push(d1.format(format));
d1.add(interval, "days");
}
console.log(dates)
return dates.slice(0)
}
getDatesInrange(moment(), moment().add(1, "month"), 1, "DD-MM-YYYY")
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
Non Moment Version
function getDatesInrange(d1, d2, interval){
var dates = [];
while(+d1 < +d2){
dates.push(formateDate(d1));
d1.setDate(d1.getDate() + interval)
}
console.log(dates)
return dates.slice(0)
}
function formateDate(date){
return [getDoubleDigits(date.getDate()),
getDoubleDigits(date.getMonth() +1),
date.getFullYear()].join('-')
}
var startDate = new Date();
var endDate = new Date();
endDate.setMonth(endDate.getMonth() + 1);
getDatesInrange(startDate, endDate, 1)
function getDoubleDigits(str) {
return ("00" + str).slice(-2);
}
Nothing wrong with your current implementation idea (except that the current code will cause an infinite loop. The ceiling should be declared separately), but since moment.js can take objects for duration, it's also possible to create a single helper function in which the values are calculated. For some cases this may not be the most efficient way (for times without a date, this could be overkill), but it would keep it versatile. e.g. if the range should stay the same but only the format should change to include a date, only one parameter would have to be changed.
The example below uses an ES6 generator, but the same could be easily converted to return an array instead:
function* createRange(start, duration, interval, format){
let dt= start.clone(), target = start.clone().add(duration);
while(dt < target){
yield dt.format(format);
dt.add(interval || {years:1});
}
}
let dates = createRange(moment(),{month:1}, {days:1}, 'DD-MM-YYYY'),
times= createRange(moment('00:00','HH:mm'),{days:1},{minutes:5}, 'HH:mm');
console.log([...dates]);
console.log([...times]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
Of course if certain ranges, such as the month from now, are recurring, they can be put in a separate function which calls the range function.
I have an array of dates formatted as MM/DD/YYYY. I need to find the next closest date in the future starting from today. Say today was 1/22/2016 then 2/19/2016 would return.
2/3/2015
7/5/2015
1/21/2016
2/19/2016
7/1/2016
I've tried doing substrings to get the month, day, year separate and attempting a sort based off those values but surely there has to be a better way.
There is no need for a sorting algorithm. You only need to iterate once and find the closest date that is greater or equals today.
Pseudocode
closest <- infinity
foreach date in dates:
if (date >= now and date < closest) then
closest <- d
return closest
JavaScript
const dates = [
'2/3/2035',
'7/5/2035',
'1/21/2036',
'2/19/2036',
'7/1/2036',
'10/22/2039',
'08/12/2039',
];
const now = new Date();
let closest = Infinity;
dates.forEach(function(d) {
const date = new Date(d);
if (date >= now && (date < new Date(closest) || date < closest)) {
closest = d;
}
});
console.log(closest);
Personally I would use a library such as the very good Moment.JS library, to handle all the horrible complexity of dates.
It has a difference method:
http://momentjs.com/docs/#/displaying/difference/
e.g.
var a = moment([2007, 0, 29]);
var b = moment([2007, 0, 28]);
a.diff(b) // 86400000
It would then be trivial to Math.min() the differences of each date in your list.
There's also a moment.min, which might shortcut this entirely, if all your dates are in the future already:
http://momentjs.com/docs/#/get-set/min/
A naïve implementation would be to parse each date as a string and sort them in ascending order. Then, remove any dates that are in the past, and get the first child of the array of remaining dates. See this jsbin example:
var dates = [
'2/3/2015',
'7/5/2015',
'1/21/2016',
'2/19/2016',
'7/1/2016'
];
// parse each string as a Date object and sort them in ascending order
function sortDates(dates) {
return dates.map(function(date) {
return new Date(date).getTime();
}).sort(function(a, b) {
return a - b;
});
}
var orderedDates = sortDates(dates);
// remove any dates in the past, and get the first child of the array of remaining dates
var nextDate = orderedDates.filter(function(date) {
return (Date.now() - date) > 0;
})[0];
Keep in mind that this depends on the format of the date string that you pass to the Date object (in other words, is 1/12/2015 January 12th, or December 1st? JavaScript will parse it as January 12th.
You can use while loop, new Date()
var dates = ["2/3/2015","7/5/2015","1/21/2016","2/19/2016","7/1/2016"]
, d = "1/22/2016", n = -1, res = null;
while (++n < dates.length && new Date(dates[n]) < new Date(d));
res = dates[n] || d;
console.log(res)
Lots of answers, one more can't hurt.
Date strings should always be manually parsed. A library can help, but if you only have a single format, a simple function is all that's required.
The following uses reduce to loop over the array of dates and finds the closest future date. If no date is in the future, it returns null.
The returned value is the string from the array, not a Date.
function parseMDY(s) {
var b = (s || '').split(/\D/);
return new Date(b[2], b[0]-1, b[1])
}
function getClosestDateToToday(arr) {
var now = new Date();
now.setHours(23,59,59);
return arr.reduce(function (acc, s) {
var d = parseMDY(s);
return d < now? acc : (acc && d > parseMDY(acc)? acc : s);
}, null);
}
var dates = ['2/3/2015', '7/5/2015','1/21/2016',
'2/19/2016','7/1/2016'];
document.write(getClosestDateToToday(dates));
This really depends upon your dates and data structures (the ones shown in original example are not so great for me).
From the other answers...
To take the example from Josh, you could also keep a pointer to which date you are using, or simply shift off of a sorted queue of dates to make it work, but it's really adding noise to your code, disrupting the purpose.
Frederik.L answer is really beautiful code, but it would still have to be executed multiple times, so I cannot recommend it.
Feedback warning
I've been given feedback in comments that Date.parse can behave inconsistently. I'll move to passing a date parsing callback function, and demonstrate Date.UTC usage in the callback for OP-specific date format. Please be careful when defining your own callbacks, and please do not copy-paste.
Suggestion
I'd suggest utilizing Date functions i.e. Date.parse; but also try where possible to get data sources sorted without needing application-level sorting. Then you can store-once and step through the array using array.shift() or similar;
Ideally also YYYY-MM-DD
Four-Digit Year
Two-Digit Month
Two-Digit Day
... (continue from least occurring to most occurring)
sample code
var dates = [
'2/3/2015',
'7/5/2015',
'7/1/2016',
'1/21/2016',
'2/19/2016'
]; // unsorted garbage dates
var DateList = function( dateList, getDate ) {
var sortedDates = dateList.sort( function(a, b) {
return getDate(a) - getDate(b);
});
this.next = function() {
var dt = sortedDates.shift();
sortedDates.push(dt); // comment to remove cyclical nature
return dt;
}
};
// specific implementation parser for this format
var getDisgustingDateFormat = function(dStr) {
var dParts = dStr.split('/');
return new Date(Date.UTC(dParts[2],dParts[0],dParts[1]));
};
var dl = new DateList( dates, getDisgustingDateFormat );
Usage
dl.next(); // "2/3/2015"
dl.next(); // "7/5/2015"
dl.next(); // "1/21/2016"
dl.next(); // "2/19/2016"
dl.next(); // "7/1/2016"
dl.next(); // "2/3/2015"
Hope this helps (Updated for clarity)
What about this version using for of and momentjs:
const getClosestFutureDate = (dates) => {
if (dates.length === 0) {
return null;
}
let minDiff = 0;
for (const date of dates) {
minDiff += minDiff + 30;
var currentDate = moment(date);
if (currentDate.isAfter(moment()) && currentDate.diff(moment(), "days") <= minDiff) {
break;
}
}
return currentDate;
};
Assuming now = 2019-08-21
console.log(getClosestFutureDate(["2019-05-07", "2019-06-01", "2019-07-13", "2019-11-09", "2019-11-10", "2019-11-11"]));
// 2019-11-09
I am fan of momentjs, but this can be easily refactored to use only vanilla Date.
const FindDate = (date, allDate) => {
// moment().diff only works on moment(). Make sure both date and elements in allDate list is in moment
let nearestDate = -1;
allDate.some(d => {
const currentDate = moment(d)
const difference = currentDate.diff(date); // Or date.diff(currentDate) depending on what you're trying to find
if(difference >= 0){
nearestDate = d
}
});
console.log(nearestDate)
}
In Livescript:
x =
* "2/3/2015"
* "7/5/2015"
* "1/21/2016"
* "2/19/2016"
* "7/1/2016"
sim-unix-ts = (date-str) ->
# Simulate unix timestamp like concatenating
# convert "MM/DD/YYYY" to YYYYMMDD (integer)
# so we can simply compare these integers
[MM, DD, YYYY] = date-str.split "/"
MM = "0#{MM}".slice -2 # apply zero padding
DD = "0#{DD}".slice -2 # apply zero padding
parse-int "#{YYYY}#{MM}#{DD}"
today = sim-unix-ts "2/18/2016"
date-list = [sim-unix-ts(..) for x]
# find next date
next-dates = [.. for date-list when .. > today]
next-date = next-dates.0
next-date-orig = x[date-list.index-of next-date]
alert [next-date, next-date-orig]
..in Javascript:
var x, simUnixTs, today, dateList, res$, i$, x$, len$, nextDates, y$, nextDate, nextDateOrig;
x = ["2/3/2015", "7/5/2015", "1/21/2016", "2/19/2016", "7/1/2016"];
simUnixTs = function(dateStr){
var ref$, MM, DD, YYYY;
ref$ = dateStr.toString().split("/"), MM = ref$[0], DD = ref$[1], YYYY = ref$[2];
MM = ("0" + MM).slice(-2);
DD = ("0" + DD).slice(-2);
return parseInt(YYYY + "" + MM + DD);
};
today = simUnixTs("2/18/2016");
res$ = [];
for (i$ = 0, len$ = x.length; i$ < len$; ++i$) {
x$ = x[i$];
res$.push(simUnixTs(x$));
}
dateList = res$;
res$ = [];
for (i$ = 0, len$ = dateList.length; i$ < len$; ++i$) {
y$ = dateList[i$];
if (y$ > today) {
res$.push(y$);
}
}
nextDates = res$;
nextDate = nextDates[0];
nextDateOrig = x[dateList.indexOf(nextDate)];
alert([nextDate, nextDateOrig]);
Hey all,
I'm trying to teach myself Javascript. The following is my first script so bear with me if there are some "rookie" mistakes. :)
It works in FF and Chrome, but not in IE or Safari.
JSLint won't process past the for loop.
IE debugger is giving me an Invalid argument error on the line(#37):
document.getElementById(progBarId).style.width = barWidth + 'px';
I've confirmed that the barWidth variable is a number.
I've Googled IE issues with my syntax and came up empty.
Any help is greatly appreciated. Thanks.
window.onload = upDtProgBars;
function upDtProgBars() {
var allTags = document.getElementsByTagName("div");
for (var i = 0; i < allTags.length; i++) {
if (allTags[i].className.indexOf("progBar") > -1) {
progBarId = setProgBarWidth(allTags[i].id);
}
// END if
}
// END for loop
function setProgBarWidth(progBarId) {
var today = new Date();
var startDate;
var numWeeks;
var barWidth = 0;
var progBarID = "";
switch (progBarId) {
case "html":
startDate = new Date('5,1,2009')
break;
case "html5":
startDate = new Date()
break;
case "js":
startDate = new Date('1, 1, 2011')
break;
case "csharp":
startDate = new Date('9,3, 2010')
break;
default:
}
// END Switch
if (progBarId != "") {
numWeeks = getNumWeeks(today, startDate);
barWidth = parseInt(numWeeks * 2.76);
document.getElementById(progBarId).style.width = barWidth + 'px';
}
// END if not empty string
}
// END setProgBarWidth
function getNumWeeks(d1, d2) {
var ONE_WEEK = 1000 * 60 * 60 * 24 * 7;
var diffInWeeks = Math.round(Math.abs(d2.getTime() - d1.getTime())) / ONE_WEEK;
return diffInWeeks;
}
// END getNumWeeks
}
// END upDtProgBars
Your dates are not formatted properly for IE and Safari. This indirectly causes an invalid argument on the line where you set width because of the following example process:
var date = new Date("garbage"); // Invalid date
var time = date.getTime(); // returns NaN from an invalid date
var barWidth = time * 2.76; // Still NaN
// The following line results in "NaNpx" for the width, which throws an error
document.getElementById(progBarId).style.width = barWidth + 'px';
Stick to using multiple arguments; new Date(year, month, date). Example:
case "csharp":
startDate = new Date(2010, 8, 3);
Remember that months start with 0 in this case, so 8 is September, not August. Also, don't forget your semi-colons at the end of lines.
Date - MDC
I'm not a JavaScript expert and am not sure what is causing the error. But you should pass the object itself rather than just the ID to setProgBarWidth(). Why make your code lookup the same item more than once? That would also rule out any problems related to the ID.
Try to use jquery for DOM manipulation, is much easier and prevents some mistakes.