Trying to add to dateTime in sheets - javascript

function getFirstEmptyRow() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
}
return (ct);
}
function getLatestTime() {
return SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange(getFirstEmptyRow(),1).getValue();
}
function getLatestPoints() {
return SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange(getFirstEmptyRow(),2).getValue();
}
function getLatestAverage() {
return SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange(getFirstEmptyRow(),3).getValue();
}
function daysLeft(){
var pointsLeft = 24250 - getLatestPoints();
return (pointsLeft / getLatestAverage()) / 24;
}
function nextRedeem() { //Problem is with this function:
var redeemTime = getLatestTime() + daysLeft() + (2/24);
return redeemTime;
}
In my sheet I have a list of rows with 1)a date/time 2)a point value and 3)A running average of points per hour. I am trying to write a function that figures out how much time is left before the points reach a certain number and add it to the latest time to figure out at what time I expect to have that number of points.
I have little experience with java script and weak typing. My problem is that when I try to add a number to my returned date value I either get a string or just NaN. My other problem is that sheets seems to interpret dates into a number differently than Number() does.
If my nextRedeem() function simply returns getLatestTime(), I can get sheets to show it either as a date or the number of days since 1/1/1900 or whatever it is. At that point, in a cell I can add to it. I can add getLatestTime() and daysLeft() in a cell and it works fine. I can also add the timezone offset and it works, in a cell. But when I do it in this function nothing seems to work. I have tried adding .value to the function calls and using parseFloat() but that gives me NaN.
How do I do arithmetic with these function returns?

The issue is that isn't how Dates are handled in javascript.
var redeemTime = getLatestTime().setDate( getLatestTime().getDate() + daysLeft() + 12 );
This will set the Day of the Month of your DateTime object to whatever the original date was + daysLeft(). I'm not sure what you're referring to with 2/24 (as that's always 12) but I included it in case you have some other context. If you add days over the number of days in the month, it will go into the next month, and similarly if you add negative days below 0, it will go into the previous month.
For more information on handling dates with JavaScript, see this link at W3Schools.

So Date.parse gets me half way there, but it give me milliseconds since 1/1/1970, where google is days since 12/30/1899. So I just had to use some math which I left uncomputed for clarity.
function dateToNum(date) {
return (Date.parse(date)/1000/60/60/24 + 25569)
}
Now I can use dateToNum(getLatestTime()) and do whatever math I want to it. When this number is put into a cell that is formatted to datetime it will display correctly.

Related

Datetime array to array with dates, get corresponding time afterwards

Specific situation.. I'm having an array filled with datetimes I pull in via an api.
Users should be able to select a date from a datepicker (only showing dates available in the array) and afterwards see the corresponding time.
So what I've done..
The original array is obtained via php, so before starting to populate the datepicker with possible dates I create an extra array with dates only.
Since I maintain the key's it's possible to put these 2 arrays next to eachother.
Array looks as following:
["8-8-2017,07:00", "26-8-2017,07:00"];
So far so good...
After a user picks a date I trigger this to be able to start digging for the time corresponding that date.
Now it's getting messy...
$('#datepick').datepicker().on("input change", function(e) {
$("#uur").text('');
var selecteddate = e.target.value;
var searchArr = datesArray;
var ind = searchArr.indexOf(selecteddate.toString());
var result = datesArray.filter(function(item) {
return typeof item == 'string' && item.indexOf(selecteddate.toString()) > -1;
});
var afterComma = result.toString().substr(result.toString().indexOf(",") + 1);
var final = afterComma.replace(":", "u");
$("#uur").text("De warming up party gaat van start rond " + final);
});
The result is that this only works on the last element of the array.
Because I'm splitting based on the comma's. Now I know the easiest way to work arround this would be to change the , that's seperating date and time in another symbol but still I'm wondering why this couldn't be easier.
You convert whole array to string every time. You should change following code:
var afterComma = result.toString().substr(result.toString().indexOf(",") + 1);
To this;
var afterComma = item.toString().substr(item.toString().indexOf(",") + 1);
Edit:
I also missed the loop above
//for every item in result, afterComma will refer to related minute string
for (var item in result) {
var afterComma = item.toString().substr(item.toString().indexOf(",") + 1);
// Do rest here
}

How to get date of a page (HTML, PHP... etc.) that uploaded in Server?

I am developing an Application that gets the " Date of Upload " of a Webpage. So, my doubt is How?
Example: I want to get the date of this post. Like when I send this URL to my program it should give date (The day it was uploaded) as output.
I want Date for this URL: http://refer2earn.16mb.com/
from php official documentation http://php.net/manual/en/function.filemtime.php
<?php
// outputs e.g. somefile.txt was last modified: December 29 2002 22:16:23.
$filename = 'somefile.txt';
if (file_exists($filename)) {
echo "$filename was last modified: " . date ("F d Y H:i:s.", filemtime($filename));
}
?>
For a more detailed discussion of dates in web pages, please see my answer to Citation of a website: How to determine the year? on Academia.
Determining the date from the page can be complex if the Last Modification Date is set to the current date. When the date is set to the current date, that usually, but not always, indicates that the page was generated on the fly (e.g. from a database). About the only thing you can do at that point is try to scrape the page for things that look like dates. That can, however, be complex, and usually requires human intervention to determine which date, if any, is the correct one to use.
Below is a bookmarklet that will both show the last modified date and scrape the page for some common date formats:
javascript: void((function () {
var toRm = document.getElementById('showTagsWithDate');
if (toRm) {
document.body.removeChild(toRm);
}
var tags = [];
function addMoreDates(reg) {
var addTags = document.documentElement.innerHTML.match(reg);
if (addTags) {
addTags.forEach(function (newTag) {
if (tags.indexOf(newTag) === -1) {
tags.push(newTag);
}
});
}
}
addMoreDates(/(20\d\d|1\d\d\d)[\s\/\-.,]\s*([1-9]|0[1-9]|[1][012])[\s\/\-,.]\s*([1-9]|0[1-9]|[12]\d|3[01])\s*(st|nd|rd|th){0,1}(?=\D)/img);
addMoreDates(/([1-9]|0[1-9]|[12]\d|3[01])(st|nd|rd|th){0,1}[\/\-\s]\s*(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[\s,.\/\-][\s,.\/\-]?\s*(20\d\d|1\d\d\d)/img);
addMoreDates(/(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[\s,.\/\-][\s,.\/\-]?\s*([1-9]|0[1-9]|[12]\d|3[01])(st|nd|rd|th){0,1}[\s,.\-]+(20\d\d|1\d\d\d)/img);
addMoreDates(/\b([1-9]|0[1-9]|[1][012])[\s\/\-.,]\s*([1-9]|0[1-9]|[12]\d|3[01])[\s\/\-,.]\s*(20\d\d|1\d\d\d)\s*\b/img);
addMoreDates(/\b([1-9]|0[1-9]|[12]\d|3[01])[\s\/\-.,]\s*([1-9]|0[1-9]|[1][012])[\s\/\-,.]\s*(20\d\d|1\d\d\d)\s*\b/img);
addMoreDates(/\b(winter|spring|summer|fall|autumn|january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[\s,.\/\-][\s,.\/\-]?\s*(20\d\d|1\d\d\d)\b/img);
addMoreDates(/(20\d\d|1\d\d\d)[\s,.\/\-]\s*(winter|spring|summer|fall|autumn|january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)/img);
addMoreDates(/\b(20\d\d|1\d\d\d)(0[1-9]|[1][012])(0[1-9]|[12]\d|3[01])\b/img);
tags.sort(function (a, b) {
var aVal = Date.parse(a);
var bVal = Date.parse(b);
if (aVal === bVal) {
return 0;
}
if (aVal > bVal) {
return 1;
}
return -1;
});
if (tags.length === 0) {
tags = ['No dates were detected in the page.'];
}
document.body.insertAdjacentHTML('afterbegin', '<div id="showTagsWithDate" style="background-color:white;color:black;">The page was last modified on ' + document.lastModified + '<br>Dates in the HTML in multiple numeric and English language formats:<ul/></div>');
var myul = document.body.firstChild.lastChild;
tags.forEach(function (tag) {
myul.appendChild(document.createElement('LI')).appendChild(document.createTextNode(tag));
});
document.body.firstChild.appendChild(document.createElement('BR'));
})())
With more processing available than is reasonable in a bookmarklet, you can do a more extensive job of finding dates in the page. However, matching even more possible date formats will increase the number of false positive matches. While you can reject a reasonable number based on dates that appear invalid, you will still have quite a few which are not accurate.
Ultimately, you are probabably best off using the last modification date as reported by the server, unless a human indicates that one of the dates scraped from the page is accurate. Even then the last modification date may be most appropriate, depending on what you are using the date for, which you don't state in the question.

Format a date from a timestamp without BST in JavaScript

Consider the following timestamp I get back from the server:
2016-09-15T18:15:00.0000000
If I do the following:
function friendlyTime(timeslot) {
const time = timeslot,
timeDate = new Date(time),
timeHours = `0${timeDate.getHours().toString()}`.slice(-2),
timeMinutes = `0${timeDate.getMinutes().toString()}`.slice(-2);
return `${timeHours}:${timeMinutes}`;
}
const slot = friendlyTime('2016-09-15T18:15:00.0000000');
I get back:
19:15, when what I really want is a reflection of the timestamp without BST, like 18:15.
Is there an easy way to do this at all? My head explodes when it comes to dates and timezones in JavaScript.
It needs to look the same regardless of where anyone is in the world. It shouldn't be reflecting anyone's personal time settings.
Just manipulate the string, one possibility that would support 4 to 6 digit common spec years (or more).
function friendlyTime(timeslot) {
return timeslot.split('T').pop().split(':', 2).join(':');
}
console.log(friendlyTime('2016-09-15T18:15:00.0000000'));
Another, if you only need to support 4 digit years. (Javascript is spec'd for 4)
function friendlyTime(timeslot) {
return timeslot.slice(11, 16);
}
console.log(friendlyTime('2016-09-15T18:15:00.0000000'));
Instead of getHours() and getMinutes() use getUTCHours() and getUTCMinutes(). Also, as Mike McCaughan pointed out in comments, you have to add Z to the end of the string to make sure it will be parsed correctly in all browsers.
function friendlyTime(timeslot) {
const time = timeslot,
timeDate = new Date(time),
timeHours = `0${timeDate.getUTCHours().toString()}`.slice(-2),
timeMinutes = `0${timeDate.getUTCMinutes().toString()}`.slice(-2);
return `${timeHours}:${timeMinutes}`;
}
const slot = friendlyTime('2016-09-15T18:15:00.0000000Z');
console.log(slot);

Sorting and filtering on Date

I have a date column and need to be able to both sort and filter on it. The data comes in as strings like 2010-12-23 and can pre-processed as needed. It should be shown as 23.12.2010. Some internationalization will come later.
I wonder what's the proper internal representation:
a string like "23.12.2010" is bad for sorting (it could be done by sorting on function result, but it'd be slow)
a string like "2010-12-23" sorts correctly, can be formatted easily, but filtering for 23.12 does not work (it could be done, but it'd be slow)
Date would probably get sorted correctly, but filtering would be slow
moment could be the solution, no idea
My current idea is to create an object containing both milliseconds and the displayed string, so that all operations can be fast. But I'd bet that someone was that smart before me....
Let's assume that showing dates in the form like 2010-12-23 is unacceptable, otherwise the problem is solved. To summarize, the problem is that I need to
display and filter in the DD.MM.YYYY format
sort according to the numerical value (or equivalently, as if it was in th ISO format).
I think the method you're proposing wouldn't run in to too many performance issues, unless you're going for really old browsers or mobile devices.
I've mocked up an example to do a quick (performance) test. First, I'm defining an object that holds a value optimized for sorting, and a value optimized for display:
var MyDate = function(dateString) {
var date = new Date(dateString);
var displayValue = "{0}.{1}.{2}"
.replace("{0}", prefixZeroIfNeeded(date.getUTCDate()))
.replace("{1}", prefixZeroIfNeeded(date.getUTCMonth() + 1))
.replace("{2}", date.getUTCFullYear());
return {
sortKey: date.getTime(),
displayValue: displayValue
};
};
The prefixZeroIfNeeded method ensures we get the DD.MM format rather than the dd.mm one:
var prefixZeroIfNeeded = function(nr) {
return nr < 10 ? "0" + nr : "" + nr;
};
Then, we need some data to convert:
var data = [];
var myDates = data
.map(MyDate)
.sort(function(date1, date2) {
return date1.sortKey - date2.sortKey;
});
Finally, a quick example of a very basic search function:
var searchMyDates = function(str) {
return myDates.filter(function(myDate) {
return myDate.displayValue.indexOf(str) !== -1;
});
};
Now, we can create some mockup data and check how long it would actually take to A) map and sort the raw strings to the MyDate objects, and B) search for a string in our collection.
Here's how I generated the raw data:
for (var i = 0; i < 10000; i += 1) {
var y = Math.floor(Math.random() * 101) + 1900;
var m = prefixZeroIfNeeded(Math.floor(Math.random() * 13));
var d = prefixZeroIfNeeded(Math.floor(Math.random() * 29));
data.push(y + "-" + d + "-" + m);
}
Using console.time to measure, processing the data on my machine (A) takes around 40ms. Searching for the string .12. takes around 5-10ms.
Concluding: I think you were definitely on the right track and could continue work in the proposed direction. However, in my personal experience, I've learned that whenever I start work on a feature that involves dates and times, moment.js is the way to go. You'll eventually run in to daylight saving time, time zones, you name it and regret you thought it was simple...
Let me know if this is of any help.
Edit: the code in a snippet (check your browser console for output)
var data = [];
var prefixZeroIfNeeded = function(nr) {
return nr < 10 ? "0" + nr : "" + nr;
};
// Generate random data:
for (var i = 0; i < 10000; i += 1) {
var y = Math.floor(Math.random() * 101) + 1900;
var m = prefixZeroIfNeeded(Math.floor(Math.random() * 13));
var d = prefixZeroIfNeeded(Math.floor(Math.random() * 29));
data.push(y + "-" + d + "-" + m);
}
var MyDate = function(dateString) {
var date = new Date(dateString);
var displayValue = "{0}.{1}.{2}"
.replace("{0}", prefixZeroIfNeeded(date.getUTCDate()))
.replace("{1}", prefixZeroIfNeeded(date.getUTCMonth() + 1))
.replace("{2}", date.getUTCFullYear());
return {
sortKey: date.getTime(),
displayValue: displayValue
};
};
console.time("Map and sorting");
var myDates = data
.map(MyDate)
.sort(function(date1, date2) {
return date1.sortKey - date2.sortKey;
});
var searchMyDates = function(str) {
return myDates.filter(function(myDate) {
return myDate.displayValue.indexOf(str) !== -1;
});
};
console.timeEnd("Map and sorting");
console.time("Search");
console.log("Searching for the month 12, %d results.", searchMyDates(".12.").length);
console.timeEnd("Search");
This may help you a bit. I have used the same thing working with React. Here is a link for Moment.js -
http://momentjs.com/docs/#/displaying/format/
If you go under Display -> Format on the right menu bar, you'll see localized formats, you will need to use format L - pre defined format from moment which will show you 09/04/1986 (September 4, 1986); otherwise you can create your own using DD-MM-YYYY format.
For Example, The way I used in React for my exercise is
To define a variable using let:
let deadlineFormated = Moment(this.props.ToDoItem.deadline).format('llll');
Hope this helps for Angular!
Gist: Decouple sorting and filtering. Do sorting on the internal representation and filtering on the presentation.
Sort on internal representation that is in any naturally sortable format. Your raw YYYY-MM-DD date strings would work, so would parsing them into Date objects. The performance difference could be negligible unless you're dealing with lots and lots of rows -- but in that case you would already have other issues with latency and rendering performance.
It's more intuitive if free-text filtering is done on what's displayed (the presentation). So if you're formatting the dates as "May 7, 2016", do a substring match on that. If you're formatting the dates as DD.MM.YYYY, do a substring match on that.
If filtering is driven by actual date selections from controls like a date picker or a select field, you can do the filtering on the internal representation.
Try with this:
Get Unixtimestamp for date (i.e. Numeric format) and use jquery sort.
Please check this example for jquery sort. Regarding example replace your unixtimestamp to value.
<ul id="datelist">
<li value="1360013296">Date 1</li>
<li value="1360013297">Date 2</li>
<li value="1360013298">Date 3</li>
<li value="1360013299">Date 4</li>
</ul>
https://jsfiddle.net/ajaygokhale/bohgoq8o/
To reliably implement sorting converting it into a Date Object is recommended (new Date(str))
If you need to be flexible in formatting moment has formatting support (check moment.format()) as well. Moment has pretty deep locale support as well.
You can always keep it a Date Object as the source of truth and for filtering you could do Date.toString() just at the time of filtering. This returns a string you could then filter with.

Show only one month name in FullCalendar when WeekView wraps two different months

I am developing using fullcalendar.js, and in the week view, when a week is from 2 different months (for example 27 july - 2 august) the fullcalendar week view shows the two months text. I am searching everywhere but there is no solution for this. Maybe stackoverflow users can help me.
That's what I have:
And that's what I need:
I see the date format but is MMMM YYYY, and it returns two months or one automatically and it seems impossible to change this.
In Calendar.defaults (aprox. line 8300 in non-minimized code) object I can notice this:
titleRangeSeparator: ' \u2014 ', // emphasized dash
monthYearFormat: 'MMMM YYYY', // required for en. other languages rely on datepicker computable option
As I explained, monthYearFormat seems to only be one month, but in a specific moment it merges with titleRangeSeparator to become two months.
Do you know how this is solvable?
Thank you.
EDIT
I found the functions that make this complex string, but is used by month and day views that I don't want to change (I need only to week view). The code is the next. How can I modify this code to solve it?
// Date Range Formatting
// -------------------------------------------------------------------------------------------------
// TODO: make it work with timezone offset
// Using a formatting string meant for a single date, generate a range string, like
// "Sep 2 - 9 2013", that intelligently inserts a separator where the dates differ.
// If the dates are the same as far as the format string is concerned, just return a single
// rendering of one date, without any separator.
function formatRange(date1, date2, formatStr, separator, isRTL) {
var localeData;
date1 = fc.moment.parseZone(date1);
date2 = fc.moment.parseZone(date2);
localeData = (date1.localeData || date1.lang).call(date1); // works with moment-pre-2.8
// Expand localized format strings, like "LL" -> "MMMM D YYYY"
formatStr = localeData.longDateFormat(formatStr) || formatStr;
// BTW, this is not important for `formatDate` because it is impossible to put custom tokens
// or non-zero areas in Moment's localized format strings.
separator = separator || ' - ';
return formatRangeWithChunks(
date1,
date2,
getFormatStringChunks(formatStr),
separator,
isRTL
);
}
fc.formatRange = formatRange; // expose
function formatRangeWithChunks(date1, date2, chunks, separator, isRTL) {
var chunkStr; // the rendering of the chunk
var leftI;
var leftStr = '';
var rightI;
var rightStr = '';
var middleI;
var middleStr1 = '';
var middleStr2 = '';
var middleStr = '';
// Start at the leftmost side of the formatting string and continue until you hit a token
// that is not the same between dates.
for (leftI=0; leftI<chunks.length; leftI++) {
chunkStr = formatSimilarChunk(date1, date2, chunks[leftI]);
if (chunkStr === false) {
break;
}
leftStr += chunkStr;
}
// Similarly, start at the rightmost side of the formatting string and move left
for (rightI=chunks.length-1; rightI>leftI; rightI--) {
chunkStr = formatSimilarChunk(date1, date2, chunks[rightI]);
if (chunkStr === false) {
break;
}
rightStr = chunkStr + rightStr;
}
// The area in the middle is different for both of the dates.
// Collect them distinctly so we can jam them together later.
for (middleI=leftI; middleI<=rightI; middleI++) {
middleStr1 += formatDateWithChunk(date1, chunks[middleI]);
middleStr2 += formatDateWithChunk(date2, chunks[middleI]);
}
if (middleStr1 || middleStr2) {
if (isRTL) {
middleStr = middleStr2 + separator + middleStr1;
}
else {
middleStr = middleStr1 + separator + middleStr2;
}
}
return leftStr + middleStr + rightStr;
}
This isn't directly supported unfortunately, but there is still a better way than modifying the FC source (that get's messy with patches and stuff).
There are several render hooks available that we can use to fix the formatting after the fact. viewRender doesn't work because it's called before the title changes. So we can use eventAfterAllRender instead.
eventAfterAllRender:function(){
if(view.name!=="agendaWeek")
return;
var $title = $("#calendar").find(".fc-toolbar h2"); //Make sure this is the right selector
var text = $title.text();
text = text.match(/.*? /)+text.match(/[0-9]+/);
$title.text(text); //replace text
}
JSFiddle - titleFormat hack
Not the most elegant thing in the world, but it should work better than modifying the source. Let me know if there are any issues.
Edit:
Also, if you're having problems with it flashing the wrong dateformat before the correct one, use css the make the title invisible. Then add a class to the element in eventAfterAllRender that makes it visible again.

Categories