Manipulating Date/Time Values with Google Apps Script - datetime

I've been looking through Google App Script and having trouble finding info about the datetime object(?) they use. In my particular case, I'm trying to manipulate date-time values inputted by the user in a Google Spreadsheet. For example, we could return the time someone entered in cell B3 rounded to the nearest minute in a spreadsheet cell via some formula like =MROUND(B3,"00:01:00"). However, I don't know if there's an appropriate javascript/apps-script operation that does the same thing via the script editor?
I imagine maybe the trick involves finding some equivalent integer value to the datetime value (similar to the Date object in JS?) that we can then do a regular math operation on? Unfortunately, I only see the outputs in date-time format when I try to Logger.log the appropriate cell value(s)..

I think this does what you requested. Checkout Utilities.formatDate().
function NEARESTMINUTE(value)
{
if(time)
{
return Utilities.formatDate(new Date(value), Session.getScriptTimeZone(), "HH:mm:00")
}
else
{
return 'Invalid Inputs';
}
}

Related

Get Firestore Server Timestamp from the client side

I'm building an app that tracks time. It calculates the time by differentiating the seconds like so:
serverTimestamp: FieldValue // {seconds: number, milliseconds: number}.
getSeconds() {
const createdTime = new Date(this.serverTimestamp * 1000).getTime()
const currentTime = new Date().getTime();
return Math.abs((currentTime - createdTime) / 1000);
}
The problem is that Date's values equal to the current client's device clock, while firestore's timestamp equal to the firestore server, which may cause inequivalence between these two dates.
The leads me with two alternatives:
1. Save Date instead of serverTimestamp().
This way, the difference between these two dates will be accurate. Although, they won't represent the real date. Plus, if the user will change his clock on his machine, then the seconds would be changed too.
2. Create an HTTPS function that retrieves the current server timestamp.
This way, the difference between these two dates will be accurate either. Although, it feels like I'm going too far only to get the current timestamp of the server.
I am probably going to stick with alternative number two. But I was hoping if there's a better solution to achieve my goal.
PS - I'm using #angular/fire. So, solutions from this package would be welcomed.
You can also write the current timestamp with a serverTimestamp, then read the time back out of the location it was just written. Then you can make changes or calculations with that value.

adding current time to ambiguous date using momentjs

I'm currently using FullCalendar and need to add the current time to an ambiguous date moment in the select callback function.
When selecting a day in month view, the callback function parameters "start" and "end" return an ambiguous date (no time) moment. I'm using the following to add the current time to the date then convert it back to a moment but wonder if this is the proper way to do it...
if(!start.hasTime()){
start = moment(start.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')));
end = moment( end.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')));
}
As you can see, I'm converting the "start" and "end" dates and current time moments into strings then converting the concatenated string back into a moment.
I want to make sure that the moment i'm creating is in valid ISO 8601 format.
Thanks,
Any help would be appreciated.
In the official docs says if you call format() function you get the date in ISO 8601 format.
check this out: jsfiddle
Furthermore, if you dont want to handle the timezones, you can use the utc() method
I had the same issue, strangely when I upgraded to fullcalendar v3.40. According to fullcalendar docs,
the format() and toISOString functions have been overridden within fullcalendar for ambiguously timed moments to return dates only i.e. '2017-03-06'. To overcome this and set a time, you need to create a new moment object which is what you have done. To ensure consistent parsing across browsers though, you should specify the format you are parsing as recommended by moment docs. So for your scenario, it will be:
if(!start.hasTime()){
var format = 'YYYY-MM-DD HH:mm:ss';
start = moment(start.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')), format);
end = moment( end.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')), format);
}
To make the code cleaner and easily readable though, you could create a new moment using the unix timestamp (which is a number and doesn't rely on string formats) of the start moment and set the time using the moment set function. So it could now look like this:
if(!start.hasTime()){
var timestamp = start.valueOf();
var now = moment();
var startMomentWithNowTime = moment(timestamp).set({
'hour': now.hour(),
'minute': now.minute(),
'second': now.second()
});
start = startMomentWithNowTime;
end = startMomentWithNowTime;
}
Excuse the descriptive variable naming startMomentWithNowTime. A few more lines but hopefully easier to read. You could also wrap this logic in a function to use on demand. Hope it helps!

System.DateTime comparison ('>') or ('<') does not give what I expected

I want to get files from a list for all the files whose filedate > today's cutOff - so, I have the following codelet
string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf")
.Where(file => new FileInfo(file).LastWriteTime > dtCutOff).ToArray();
I have a file whose LastWriteTime is "{11/3/2015 1:33:26 PM}" being picked up by my collection with dtCutOff == "{11/3/2015 1:33:26 PM}"! So '>' didn't seem to work.
First, I would try running it without the Where clause, just to make sure that all files you expect are indeed part of the initial array returned from Directory.GetFiles. It's entirely possible that date/time comparison is not the source of the discrepancy. It may be more related to the issue Ivan linked to in the question comments, or it may be permission related, or some other thing.
Next, be aware that DateTime violates SRP in that it has a Kind property, which is one of the three DateTimeKind enumeration values. It's either Local, Utc, or Unspecified.
In the case of DateTime.Now, the Kind will be DateTimeKind.Local. File.GetLastWriteTime also returns its value with local kind. Therfore, if you always derive your dtCutOff from DateTime.Now in the manner you showed in the question, then it will almost always be the correct comparison function.
The "almost" stems from the fact that DateTimeKind.Local can actually represent two different kinds under the covers. In other words, there are actually four kinds, but two of them are exposed by one. This is described as "DateTime's Deep Dark Secret" in Jon Skeet's blog post More Fun with DateTime, and is also mentioned in the comments in the .NET Framework Reference Source. In practice, you should only encounter this in the ambiguous hour during a fall-back daylight saving time transition (such as just occurred last Sunday 2015-11-01 in the US).
Now, to the more likely case that your dtCutOff is actually derived not from DateTime.Now, but rather from user input or database lookup or some other mechanism, then its possible that it actually represents the local time in some other time zone than the one on your local computer. In other words, if the dtCutOff has a Kind of DateTimeKind.Utc, then the value is in terms of UTC. If it has a Kind of DateTimeKind.Unspecified, then the value might be in terms of UTC, or the local time zone, or some other time zone entirely.
Here's the kicker: Comparison of two DateTime values only evaluates the value underlying the Ticks property. It does not consider Kind.
Since file times are absolute points in universal time (on NTFS anyway), then you really should use the File.GetLastWriteTimeUtc method, rather than the methods that work in local time.
There are two approaches you could use:
Load the modified property as UTC, using:
myResult.modified = File.GetLastWriteTimeUtc(myFile);
Populate dtOffset appropriately.
If you're loading from the current time, then use DateTime.UtcNow.
If you're loading from other input, ensure the value is converted to UTC to match the input scenario. For example, use .ToUniversalTime() if the value is in terms of the local time zone, or use the conversion functions in the TimeZoneInfo class if the value is in another time zone.
OR
Change your modified property to be a DateTimeOffset instead of a DateTime.
Load that using:
myResult.modified = new DateTimeOffset(File.GetLastWriteTimeUtc(myFile));
Define dtCutOff as a DateTimeOffset, and populate appropriately.
If you're loading from the current time, then use DateTimeOffset.UtcNow.
If you're loading from other input, ensure the offset is set to match the input scenario. Use TimeZoneInfo functions if you need to convert from another time zone.
DateTimeOffset has many advantages over DateTime, such as not violating SRP. It's always representing an absolute moment in time. In this scenario, it helps to know that comparison operators on DateTimeOffset always reflect that absolute moment. (In other words, it internally adjusts to UTC before doing the comparison.)
This code works:
var cutffDate = new DateTime(2015,1,1); // or whatever
var allFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf");
var datedFiles = allFiles.Where(f => (new FileInfo(f)).LastWriteTime > cutffDate);
Update:
Since your issue seems to be a precision-related one you could change the comparison to:
const long precision = 10; // vary this as needed
allFiles.Where(f =>
(new FileInfo(f)).LastWriteTime.ToFileTime()/precision > cutffDate.ToFileTime()/precision);
Alternatively you could use ...LastAccessTime.Ticks/TimeSpan.TicksPerMillisecond
In addition to that you may need to convert all DateTime values to UTC (LastAccessTimeUtc and DateTime.UtcNow) to make sure it's not some weird timezone issue
Since the files were fed into the queue once a day so the scale of precision is not required down to a millisecond or something. So that one-second TimeSpan difference is acceptable to do the trick and make my case work.
string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf")
.Where(file => new FileInfo(file).LastWriteTime - TimeSpan.FromSeconds(1) > dtCutOff)
.ToArray();
Now my file with modified date "{11/3/2015 1:33:26 PM}" didn't go into my collection when my cutOffDate is "{11/3/2015 1:33:26 PM}" while my other file with modified date "{11/3/2015 1:33:27 PM}" have successfully passed into my collection as expected!! So, it works and that's how it should work after all these advises! Thanks ye-all.
Looks like your Where clause lambda might be incorrect. Try this.
string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf").Where(file => file.modified > dtCutOff).ToArray();

Errors When Calculating Distance Between Two Addresses

I have the following script which is being used in a spreadsheet to calculate the driving distance between two cities or a city and a zip code of another city. It is being run for approximately 25 locations simultaneously. To better explain, I have cell B3 in which I enter a new city every time. The script is then used in cells adjacent to my 25 plant locations to calculate the distance from each of my plants to the variable city.
It uses google sheets built in mapping api and works on 80% of the calculations but returns "TypeError: Can Not Read Property "legs" from undefined. (line 16). The plants that it fails on vary with every new city so its not like it is for certain locations. It is almost like the api times out before it completes some of them. I split it into two separate scripts with a varied name and that worked for a day but then 20% fail again.
To make things slightly more odd, I have another script that sorts the plants based on closest distance to the variable address. When you sort the plants, even the ones with errors go to their correct location based on distance. So it is like the distance script is obtaining the correct disance but displaying the error anyways.
Clear as mud? Would love any input I could get on how to correct the issue or an alternate mapping api that could solve my problems.
function distancecalcone(origin,destination) {
var directions = Maps.newDirectionFinder()
//Set the Method of Transporation. The available "modes" are WALKING, DRIVING, BICYCLING, TRANSIT.
.setMode(Maps.DirectionFinder.Mode.DRIVING)
//Set the Orgin
.setOrigin(origin)
//Set the Destination
.setDestination(destination)
//Retrieve the Distance
.getDirections();
return directions.routes[0].legs[0].distance.value/1609.34;
}
Have you tried using a try-catch block around directions.routes[0].legs[0].distance.value ?
try{
return directions.routes[0].legs[0].distance.value/1609.34;
}
catch (e){
console.log("error",e)
}
or you could try something like this
alert(directions);
alert(directions.routes[0]);
alert(directions.routes[0].legs[0]);
alert(directions.routes[0].legs[0].distance);
alert(directions.routes[0].legs[0].distance.value);
and so on...to find out which one comes up as undefined the first. That might help you to debug the issue.
Enable Direction Api
1)Go to "google cloud platform"
2)go to "Api and services"
3)search for "direction api" and enable it
The directions service is subject to a quota and a rate limit. Check the return status before parsing the result.
For lots of distances (or at least more than 10), look at the DistanceMatrix.
I'm able to run the script from the Script editor, but not from spreadsheet. The error is "unable to read property legs" when the function is called from spreadsheet. But the property is in place when called from Script editor and contain correct values.
You probably need to use WEB API and have API KEY:
Google Apps Script - How to get driving distance from Maps for two points in spreadsheet

Using Client-Side script to evaluate month difference using PeterBlum DifferenceValidator

Problem:
I am using Peter Blum's Professional validation controls (http://www.peterblum.com/DES/MoreValidators.aspx) throughout my project and have come across a validation that should be done on the client side rather than going back to the server. The screen needs to send two dates(month/year) to the server, one for start date and one for end date. The dates are only month/year using Peter's MonthYearTextBox (http://www.peterblum.com/DES/DemoMoreDAT.aspx#MYTB). The two dates must not be more than 3 months apart (please note I did not say 90 days apart).
Here is the end goal:
A user comes to the screen to run a report. They are prompted for a start date (Month/Year) and an end date. These dates get sent to the server so that the report can be generated for the items within this date range. The user can only run this report for any 3 month period.
What I've Done:
I started off by using the DifferenceValidator from Peter's Validation And More package. In doing so, I set the difference that I expected to be 90 days, however soon realized that 7/2009 - 10/2009 is a 3 month difference (which is allowed) however it is a 92 day difference (which is outside the validator's range).
Question:
Is there any way that I can override the evaluation function in a client side function so that I may compare the month portion of the dates that are being evaluated?
Thank you,
Scott Blue
Yes there is. Here's a Javascript example:
function ValidateDateRange(cond)
{
var p = DES_GetById(cond.IDToEval).value; // Get the value of the control you're evaluating
if (p == undefined || p.length == 0) return 1;
// Perform custom validation here
// return 1 if valid, or return 0 if invalid
}
And you have to specify CustomEvalFunctionName="ValidateDateRange" in order for this to work.
I believe the CustomEvalFunctionName is supported in all of the different Peter Blum Validation controls.

Resources