Javascript: Tabbed time period charts - javascript

I'm trying to produce a graph like the following:
From an array of events as follows:
var events = {
"0": {"guid": "78926349827546", "created": "2017-07-07 14:14:21" },
"1": {"guid": "78926349827546", "created": "2017-07-08 15:44:10" },
"2": {"guid": "20936752065745", "created": "2017-07-09 12:09:24" },
"3": {"guid": "20936752065745", "created": "2017-07-11 06:55:42" },
"4": {"guid": "20936752065745", "created": "2017-07-11 22:10:29" },
...
};
I'm currently using the Google Line Chart. Although I'm happy with the aesthetic, I still need to find a way to produce a tabbed display of several timescales, e.g. Today, Last 7 Days, Last Month and Total.
Programmatically, this is proving to be a sisyphean task, as I have to count occurrences across (in one instance) every hour in the last day, and then (in another instance) every day in the last week etc.
And there's a lot of date conversion, counting backwards from today and so on.
Is there a way of taking my array and producing a new array of human-readable dates relative from today, across several timescales?

This is really a duplicate of a couple of questions like Where can I find documentation on formatting a date in JavaScript?, How to add months to a date in JavaScript? and Add days to JavaScript Date. So there are plenty of existing examples to work from.
Also, Google Charts has its own date formatter.
Anyway, you might use a function that takes a start date, end date and increment and returns an array of timestamps in a particular format. Formatting the strings can use a second function or the Google Charts formatter.
A bare bones version is very little code, to add some logic for forward or backward series takes a few more lines.
// Return date string in YYYY-MM-DD HH:mm:ss format
function formatDate(date) {
function z(n){return (n<10? '0':'') + n}
return date.getFullYear() + '-' +
z(date.getMonth() + 1) + '-' +
z(date.getDate()) + ' ' +
z(date.getHours()) + ':' +
z(date.getMinutes()) + ':' +
z(date.getSeconds());
}
// Return date strings from start date to end date
// with increment inc in hours
function getDateSeries(start, end, inc) {
var d = new Date(+start);
inc = +inc;
var dates = [];
// Deal with backwards sequences
var reverse = false, t;
if (start > end) {
t = start;
start = end;
end = t;
reverse = true;
}
if (inc < 0) {
reverse = true;
inc *= -1;
}
while (start <= end) {
dates.push(formatDate(start));
start.setHours(start.getHours() + inc);
}
return reverse? dates.reverse() : dates;
}
// Hourly intervals over 2 days forwards
console.log(getDateSeries(new Date(2017,7,18), new Date(2017,7,19), 1));
// 6 hourly intervals over 10 days backwards
console.log(getDateSeries(new Date(2017,7,28), new Date(2017,7,18), -6));
// Hourly intervals from now going back 24 hours
var now = new Date();
var end = new Date(+now);
end.setDate(end.getDate() - 1);
console.log(getDateSeries(now, end, -1))
// Daily intervals from today going back 30 days
var now = new Date();
now.setHours(0,0,0,0);
var end = new Date(+now);
end.setDate(end.getDate() - 30);
console.log(getDateSeries(now, end, -24))
There are plenty of libraries around to help with formatting, incrementing and decrementing dates but if this is all you want to do, it doesn't take much to write.
This could be modified so the start is always "now" or "today" and use an interval in days rather than a start and end date with hours.
Where a library would come in handy is if you want say monthly intervals on the last day of the month or similar (since months aren't of equal length). So using moment.js you could do:
function getMonthlySequenceStart(months) {
var format = 'YYYY-MM-DD HH:mm:ss'
var now = moment().startOf('month');
var count = Math.abs(months);
var direction = months < 0? -1 : 1;
var result = [];
while (count--) {
result.push(now.format(format));
now.add(direction, 'months');
}
return result;
}
console.log(getMonthlySequenceStart(-12));
function getMonthlySequenceEnd(months) {
var format = 'YYYY-MM-DD HH:mm:ss'
var now = moment().endOf('month').startOf('day');
var count = Math.abs(months);
var direction = months < 0? -1 : 1;
var result = [];
while (count--) {
result.push(now.format(format));
now.add(direction, 'months');
now.endOf('month').startOf('day');
}
return result;
}
console.log(getMonthlySequenceEnd(-12));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
Not using a library isn't too hard either. The following sets the date to the first of the month as then it's easy to decrement by 1 month, then get the day before (the last day of the previous month) for the string:
// Sequence of end of months from current month
// back for num months
function getMonthlySequenceEnd(num) {
var now = new Date();
now.setHours(0,0,0,0);
var t, result = [];
// Set to first day of next month
now.setMonth(now.getMonth() + 1, 1)
while (num--) {
t = new Date(+now);
t.setDate(t.getDate() - 1);
result.push(formatDate(t));
now.setMonth(now.getMonth() - 1);
}
return result;
}
function formatDate(date) {
function z(n) {return (n < 10 ? '0' : '') + n}
return date.getFullYear() + '-' + z(date.getMonth() + 1) + '-' + z(date.getDate()) + ' ' +
z(date.getHours()) + ':' + z(date.getMinutes()) + ':' + z(date.getSeconds());
}
console.log(getMonthlySequenceEnd(24));
So I think you now have enough to do whatever is required.

Related

Why does does removing a line effect the next one?

I'm attempting to return different days based on a given date, such as the first Monday of the week, the Friday of the week, as well as days in previous and following weeks so that I can figure out the start and end of my pay periods (for my two jobs, each are different) as well as the pay date for that period.
When I remove the Logger.log("Prev Monday > " + addDays(pMonday, -7));
the next line's value changes. What is removed line doing to mess up the following one?
I'm using this in Google App Scripts so I can pull data from a Google Calendar to a Google Sheet easily. It also means no extra libraries.
function test_monday(){
var theDate = new Date();
theDate.setDate(16);
theDate.setMonth(5);
theDate.setFullYear(2016);
theDate.setHours(12,0,0,0)
Logger.log(theDate);
var pMonday = new Date();
pMonday = getMonday( theDate ) ;
Logger.log("pMonday: " + pMonday)
Logger.log("Prev Monday > " + addDays(pMonday, -7));
Logger.log("Following Friday > " + addDays(pMonday, 4));
}
function getMonday( date ) {
var day = date.getDay() || 7;
if( day !== 1 )
date.setHours(-24 * (day - 1));
return date;
}
function addDays(d, n){
var date = new Date();
date = d;
var offset = n;
date.setHours(24 * ( offset ));
return date;
}
date = d;
This line right here completely undoes the work you did in the previous line. It says that date is the exact same instance as d. That means that whenever you modify date (date.setHours(24 * offset)) you're also modifying d. You might as well be writing d.setHours(24 * offset) because they mean the same thing.
If you want to create a new Date object equal to another one, just pass the original into the Date constructor. So you could rewrite your function as:
function addDays(d, n) {
var date = new Date(d);
date.setHours(24 * n);
return date;
}

How to make this function not return a negative date diffence?

The function is trying to fund the difference between to dates, but I am struggling to not return a negative number if the date is past a certain point. I have tried a few work around like using ABS but it can cause problems in future areas.
var DateCalc = {};
DateCalc.totalDaysLeft = 0;
DateCalc.calculate = function(dateToParse) {
DateCalc.init(dateToParse);
return DateCalc.stringify(DateCalc.years(), DateCalc.months(), DateCalc.days());
};
DateCalc.init = function(dateToParse) {
var date = DateCalc.parseDate(dateToParse);
var today = Date.now();
var oneDay = 24 * 60 * 60 * 1000;
DateCalc.totalDaysLeft = Math.floor((date - today) / oneDay);
};
DateCalc.parseDate = function(dateToParse) {
var dateVars = dateToParse.split(',').map(Number);
return new Date(dateVars[0], dateVars[1] - 1, dateVars[2]);
};
DateCalc.years = function() {
var years = Math.floor(DateCalc.totalDaysLeft / 365);
DateCalc.totalDaysLeft -= Math.floor(years * 365);
return years;
};
DateCalc.months = function() {
var months = Math.floor(DateCalc.totalDaysLeft / 30);
DateCalc.totalDaysLeft -= Math.floor(months * 30);
return months;
};
DateCalc.days = function() {
return Math.floor(DateCalc.totalDaysLeft / 24);
};
DateCalc.stringify = function(years, months, days) {
var dateString = "";
if (years !== 0)
dateString += years + " years, ";
if (months !== 0)
dateString += months + " months, ";
dateString += days + " day(s).";
return dateString;
};
//here is the .abs() code.
function age(year, month, day) {
var yearDifference = Math.abs(new Date().getFullYear() - year);
var monthDifference = Math.abs(new Date().getMonth() - month + 1);
var dayDifference = Math.abs(new Date().getDate() - day);
var differences = {
year: yearDifference,
month: monthDifference,
day: dayDifference
};
var final = [];
for (var time in differences) {
if (differences[time] > 0) {
var addString = differences[time] + " " + time;
if (differences[time] > 1) {
addString += "s"
}
final.push(addString);
}
}
return final.join(" ");
};
console.log(age(2017, 11, 17));
console.log(age(2016, 1, 2));
//if you tried to look up how far away next January is while you're in December, it will tell you it is 1 year 11 months from now instead of 1 month. This is because it adds the 11 months instead of subtracting it.I am trying to find a solution, as this function is more conscise but the other is more versatile. The function above, I replaced .floor() with rounds the value down with .abs() hoping it would just use the absolute value of the given operation, however, this was not the case.
The problem is that you are using the .abs() function in an inappropriate way. Most mathematical functions do no obey the distributive rule, and the .abs() function belongs to these. For an easier understanding, let us forget your current problem for a moment and let us examine a simple, reduced example:
Let's say you want to know the absolute value of -10. Obviously, the correct result is +10.
On the other hand, -10 could be written as (-20 + 10). But nevertheless, you can not compute abs(-10) using that knowledge:
abs(-20 + 10) = 10, but
abs(-20) + abs(+10) = 30
Applying that knowledge to your problem, we see that abs(Y years + M months + D days) is generally NOT equal to (abs(Y years) + abs(M months) + abs(D days)).
Regarding this problem, there is the additional oddity that each of the terms of the result has another unit, and that the terms depend on each other (e.g. there can be no term like "13 months", because that would be "1 year plus 1 month"), but I won't go into further detail here.
There is a simple solution:
1) Determine the desired resolution of your result (i.e. should your result be accurate to seconds, attoseconds, days or something else).
2) Convert the two dates into the unit determined in step 1), using a randomly chosen, yet fixed point in time as the common starting point.
3) Now you can subtract the two (converted) dates and use the .abs() function without problems.
4) Convert the result back into human readable form.
How do you do that in practice? Well, steps 1), 3) and 4) are easy, but what about step 2)?
Nearly every OS I know (and thus, nearly every programming language) does the conversion needed in step 2) for you. More often than not, the fixed point in time is 1970-01-01 00:00:00, and the OS / programming language provides routines to convert any date / time to the number of seconds (or some other unit) which have elapsed since this fixed point.
For example, in JavaScript, the myDate.getTime() function returns the number of milliseconds which have passed since 1970-01-01 up to myDate.
So convert both dates to "milliseconds since 1970-01-01" and subtract them. Use the .abs() function on the result. Then you have the desired time span as a value of positive milliseconds. Convert that back to human readable form, i.e. years, months and days (which is no problem, is it?)
A second simple solution (just for avoiding negative results):
I hope that you agree with me that comparing two dates is much easier than computing the difference between them (first compare the year; if the years differ, you have undoubtedly found the "greater" date; if the years are equal, do the same with the months, and so on). Then exchange the two dates if necessary. That way, you always can make sure that you subtract the "smaller" date from the "greater" date and that the result always will be positive.
But please note that even when doing so there will still be negative results in parts of the calculation when actually subtracting the dates, so you would have exactly the same problems when using the .abs() function.
A more complicated solution:
You could do the subtraction yourself as well, but then the .abs() function won't help you much. One of the algorithms I can think of could work like a subtraction which is done by hand (I mean the subtraction of normal numbers you have learned in school):
Begin with the least significant unit (for example the days). Subtract the days; if the result is negative, then add 28, 29, 30 or 31 (depending on the month) and make a carry to the months, otherwise keep the result; then do the same thing with the months, and so on. But as I already wrote in my comment, there are many pitfalls when doing so (leap years, months have different numbers of days, and so on), and the .abs() function will not help you here.
Conclusion:
Personally, I would prefer the first (simple) solution I have given. It is easy, understandable and future-proof.
//initial variables
var today = new Date();
var day = today.getDate();
var month = today.getMonth() + 1;
var year = today.getFullYear();
var otherDate = new Date();
var day2 = 0;
var month2 = 0;
var year2 = 0;
if (day < 10) {
day = '0' + day;
}
if (month < 10) {
month = '0' + month;
}
function age(day2, month2, year2) {
dayConv = day2;
monthConv = month2;
yearConv = year2;
newDate = day - dayConv;
newMonth = month - monthConv;
newYear = year - yearConv;
}
function mathDate() {
if (newYear >= 1) {
if (newMonth >= 1) {
if (newDate >= 1) {
console.log(newYear + " years and " + newMonth + " months and " + newDate + " days.");
return newYear + " years and " + newMonth + " months and " + newDate + " days.";
} else if (newDate <= 0) {
console.log(newYear + " years and " + newMonth + " months.");
return newYear + " years and " + newMonth + " months.";
}
} else if (newMonth <= 0) {
console.log(newYear + " years and " + newDate + " days.");
return newYear + " years and " + newDate + " days.";
}
} else if (newYear <= 1) {
if (newMonth >= 1) {
console.log(newMonth + " months and " + newDate + " days.");
return newMonth + " months and " + newDate + " days.";
} else if (newDate <= 0) {
console.log(newMonth + " months.");
return newMonth + " months.";
} else if (newMonth <= 0) {
console.log(newDate + " days.");
return newDate + " days.";
}
}
}
age(13, 4, 2016);
mathDate();
Here is the answer I was able to create.

previous quarters in javascript

Forgive me I tried several searches here and other places in general but cant seem to fix issue I am having at the moment. Can someone please help me figure out?
I am trying to find quarter strings from inputdate in JavaScript. For "01/31/2009" it should give Q1,2013 Q4,2012 etc based on offset given as input parameter. when offset is 0 then current quarter, 1 then previous, 2 then previous 2 quarter etc...
my current code: jsfiddle
function getQuarterStrings(id) {
var d = new Date();
var d = new Date("01/31/2009");
var str;
switch (id) {
...
}
Remaining code is in jsfiddle. As you can see, it fails on second last condition even though everything seems ok. Please help me figure out my mistake. Thank you!
Some of your comparisons are off, and Date tries to compensate for months that don't have as many days when you setMonth. This code should work:
function getQuarterStrings(id) {
var d = new Date("03/31/2009");
d.setDate(1);
d.setMonth(d.getMonth() - id * 3);
var month = d.getMonth() + 1;
var year = d.getFullYear();
var quarter = Math.ceil(month / 3);
return ("Q" + quarter + ", " + year);
}
This works, and is a lot more concise. It also allows you to use any offset instead of a limited set of values:
function getQuarterStrings(date, id) {
// quarter is 0-based here
var quarter = Math.floor(date.getMonth() / 3),
year = date.getFullYear();
quarter -= id;
if(quarter < 0) {
var yearsChanged = Math.ceil(-quarter / 4);
year -= yearsChanged;
// Shift quarter back to a nonnegative number
quarter += 4 * yearsChanged;
}
return "Q" + (quarter + 1) + ", " + year;
}
http://jsfiddle.net/dPmf2/6/
You can also get rid of the switch statement by doing this:
function getQuarterStrings(id) {
var d = new Date();
var d = new Date("01/31/2009");
var str;
if (id !== 0){
d.setMonth(d.getMonth() - 3*id);
}
str = getQuarter(d);
return str;
}

Customize javascript Date

I have a simple code that echos the current Hour+Minute+Date as one number sequence.
I need to add 1 to all the numbers outputted, individually.
Example: If the current time and date is: 22321512 then i need jQuery to output: 33432623.
My knowledge in jQuery is pretty slim, How can this be achieved?
HTML:
<span id="date"></span>
Code:
var now = dateFormat(new Date(), "HHMMddmm");
$('#date').append(now);
You need to do the following roughly:
var currentDate = new Date();
var myDate = new Date(currentDate.getYear() + 1, currentDate.getMonth() + 1, currentDate.getDay() + 1);
alert(myDate.getTime());
Should solve your problem.
If you want to merely increment each unit by 1 and let the JavaScript engine advance the date and time on overflow, then Captain John's answer will work perfectly.
This means that, for example, if this routine were to be run at 11:59 PM on December 31, your output would be 00000100.
If you want each unit to be incremented by 1 without the date being advanced, you will have to stop relying on Steven Levithan's [excellent] dateFormat library and do it yourself:
var now = new Date(),
hours = now.getHours() + 1, // add 1 hour
minutes = now.getMinutes() + 1, // add 1 minute
date = now.getDate() + 1, // add 1 day
month = now.getMonth() + 1, // add 1 month
padLeft = function (val) { // make a formatter
while (val.length < 2) {
val = '0' + val; // string is less than 2 characters, pad left side with '0'
}
return val; // return formatted string
},
formatted = padLeft(hours) + padLeft(minutes) + padLeft(date) + padLeft(month);
$('#date').append(formatted);
Getting number length as string you can easily sum 1 to each number.
The result is given as timestamp
To get Date object, use new Date(result);
var now = new Date().getTime(); // 22321512 on your example
// Answer
var result = 0;
var str = now.toString();
for(var i = 0; i < str.length; i++) {
result += Math.pow(10, i);
}
result += now; // Ex.: 22321512 + 11111111

Convert EDT timestamp to PST with regex / JavaScript

A third party is providing me with an EDT time-stamp in the following format:
MM/DD/YYYY hh:mm
for instance: '08/19/2013 11:31'
I need to convert it to PST with JavaScript (same date time format) and have been looking all over but can't find any info about doing this.. If someone can help me with some example code I would really appreciate it.
If you wanted to do this manually, you can try the following:
Split by a space, so you have date and time.
Split the time by ":" and split the date by "/".
Create a new Date() and provide the right values in the right order.
Subtract 3 hours using the proper methods, then recreate the format.
Here's an example of all this:
var est = "01/01/2014 02:31",
finalDate, pst;
finalDate = parseDateString(est);
finalDate.setHours(finalDate.getHours() - 3);
pst = formatDate(finalDate);
console.log(pst);
function parseDateString(str) {
var dateTime, date, time, dateSplit, month, day, year, timeSplit, hour, minute;
dateTime = est.split(" ");
date = dateTime[0];
time = dateTime[1];
dateSplit = date.split("/");
month = dateSplit[0] - 1;
day = dateSplit[1];
year = dateSplit[2];
timeSplit = time.split(":");
hour = timeSplit[0];
minute = timeSplit[1];
return new Date(year, month, day, hour, minute);
}
function formatDate(d) {
return padZero(d.getMonth() + 1) + "/" + padZero(d.getDate()) + "/" + d.getFullYear() + " " + padZero(d.getHours()) + ":" + padZero(d.getMinutes());
}
function padZero(num) {
if (+num < 10) {
num = "0" + num;
}
return "" + num;
}
DEMO: http://jsfiddle.net/MmVmR/
The padZero function is there just to prepend any 0s in case the number is less than 10.
Reference:
Dates in JS - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

Categories

Resources