Google Form on Submit get values and format the time - javascript

I am using Google Apps Script with a Google form. When the user submits the Google Form I get a value from a question. I then take that value and make it a date object, from what I saw on this post about daylight savings I use that to determine the timezone. I run the date object through Utilities.formatDate and want to get the correctly formatted date.
example: 9:00 AM
But instead I am getting a completely different time than expected.
My question is: Can someone help me understand why the below code is outputting a time that is 3 hours different?
function onSubmit(e) {
var values = e.values;
Logger.log(values);
try {
var start1 = new Date(values[3]);
var startN = new Date(start1).toString().substr(25,6)+"00";
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log(startT);
} catch(error) {
Logger.log(error);
}
}

The assumption that Utilities formatDate does not support GMT... parameter is not true.
The post you mentioned in reference is used to get calendar events and is a useful way to get the right value when you get events from another daylight saving period (getting the TZ info from the calendar event itself), for example events for next month will be in "summer time" while we are still in "winter time"...
Your issue might come from different sources depending on time zone settings of your script vs timezone of the source. Could you describe the exact configuration in which you use this script ?
In the mean time, here is a small code that demonstrates how the code is working + the logger results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date();
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
try {
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : \n');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,6)+"00";
Logger.log('startN = '+startN);
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log('result in timeZone = '+startT);
} catch(error) {
Logger.log(error);
}
}
EDIT : additionally, about the 30 and 45' offset, this can easily be solved by changing the substring length like this :
var startN = new Date(start1).toString().substr(25,8);
the result is the same, I had to use the other version a couple of years ago because Google changed the Utilities.formatDate method at some moment (issue 2204) but this has been fixed.
EDIT 2 : on the same subject, both methods actually return the same result, the GMT string has only the advantage that you don't have to know the exact timezone name... there is also the Session.getScriptTimeZone() method. Below is a demo script that shows the resulst for 2 dates in January and July along with the log results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date(2014,0,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
values['3'] = new Date(2014,6,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : ');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,8);
Logger.log('startN = '+startN);
Logger.log('result in timeZone using GMT string = '+Utilities.formatDate(start1, startN, "MMM,d h:mm a"));
Logger.log('result in timeZone using Joda.org string = '+Utilities.formatDate(start1, 'Europe/Brussels', "MMM,d h:mm a"));
Logger.log('result in timeZone using Session.getScriptTimeZone() = '+Utilities.formatDate(start1, Session.getScriptTimeZone(), "MMM,d h:mm a")+'\n');
}
Note also that the Logger has its own way to show the date object value ! it uses ISO 8601 time format which is UTC value.

Try this instead:
var timeZone = Session.getScriptTimeZone();
var startT = Utilities.formatDate(start1, timeZone, "h:mm a");
The Utilities.formatDate function expects a time zone that is a valid IANA time zone (such as America/Los_Angeles), not a GMT offset like GMT+0700.
I am making the assumption that Session.getScriptTimeZone() returns the appropriate zone. If not, then you might need to hard-code a specific zone, or use a different function to determine it.
Additionally, the +"00" in the script you had was assuming that all time zones use whole-hour offsets. In reality, there are several that have 30-minute or 45-minute offsets.

Related

Date conversion resulting into time zone issue

I am trying to get functionality - if the user entered date is less than the current date I need to show an error message on the screen, I implemented the following code which is working fine in my local system date but not working in other time zones. Can anyone please help in getting this.
I need to use only javascript or jquery. I was not supposed to use other libraries.
dateFormat = function(value, event) {
let newValue = value.replace(/[^0-9]/g, '').replace(/(\..*)\./g, '$1');
const dayOrMonth = (index) => index % 2 === 1 && index < 4;
// on delete key.
if (!event.data) {
return value;
}
return newValue.split('').map((v, i) => dayOrMonth(i) ? v + '/' : v).join('');
}
checkStart = function(value) {
var newDate = new Date().toISOString();
var inputDate = new Date(value).toISOString();
var today = new Date(newDate).setHours(0,0,0,0);
var userDate = new Date(inputDate).setHours(0,0,0,0);
if(userDate < today) {
$('#error-msg3').show();
$('#startDate').val('');
} else {
$('#error-msg3').hide();
}
}
<input type="tel" maxlength="10" id="startDate" name="startDate" placeholder="mm/dd/yyyy"
oninput="this.value = dateFormat(this.value, event)" onblur="checkStart(this.value)" required/>
<span id="error">Start date should not be lesser than the current date.</span>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
Server and Db May run on a different timezone, (UTC preferred ) and when you sen date as a string it doesn't have any timezone there instead it is just a string.
Try sending it as a timestamp or UTC date string
So that server and db will automatically convert it to their timzone and store it. and when any user fetch it from other time zone it will automatically converts to their timezone (but you store string it will just be treated as a string everywhere)
let d = new Date()
console.log(d.getTime())
//or get utc string
console.log(d.toUTCString())
Send this value to your server (API)
Your code runs entirely on the client so timezone is irrelevant.
In the OP there is:
var newDate = new Date().toISOString();
...
var today = new Date(newDate).setHours(0,0,0,0);
The conversion of Date to string to Date to number is inefficient and unnecessary. The following is equivalent:
let today = new Date().setHours(0,0,0,0);
Similarly for inputDate:
var inputDate = new Date(value).toISOString();
...
var userDate = new Date(inputDate).setHours(0,0,0,0);
is equivalent to:
let userDate = new Date(value).setHours(0,0,0,0);
All calculations are local so timezone is irrelevant. Also see Why does Date.parse give incorrect results?
Attempting to control user input using script is always fraught as there are many cases that are either impossible or impractical to code around. The use of a tel input for Dates is an example. The whole issue can be avoided by using a date input and setting a min value to today. Then users can't select a date before today and your issue is solved, e.g.
window.onload = function() {
let dateEl = document.getElementById('dateInput');
dateEl.min = new Date().toLocaleString('en-CA', {year:'numeric', month:'2-digit', day:'2-digit'});
}
<input id="dateInput" type="date">
If you are comparing the date sent by the user to a date on the server, then user system clock accuracy and timezone may be an issue, but that isn't explained in the OP.
If that is an issue, then you need to ask another question on that specific topic.
If you really want to manually control the input date and show an error message when invalid dates are selected, then parse the value from the date input and compare it to the start of today and go from there:
// Parse YYYY-MM-DD as local
function parseYMDLocal(s) {
let [Y, M, D] = s.split(/\D/);
return new Date(Y, M-1, D);
}
// Check if date in YYYY-MM-DD format is before today
function isBeforeToday(d) {
return parseYMDLocal(d) < new Date().setHours(0,0,0,0);
}
function checkValue() {
let errEl = document.getElementById('errEl');
errEl.textContent = '';
console.log(typeof this.value);
if (isBeforeToday(this.value)) {
errEl.textContent = 'Date must be today or later';
} else {
// do something else
}
}
window.onload = function() {
document.getElementById('dateInp').addEventListener('blur', checkValue, false);
}
#errEl {color: red}
<input id="dateInp" type="date"><span id="errEl"></span>

How to compare dates to make sure Google Apps Script is only sending an alert once a day?

I have a script in a Google Sheet that is sending out an alert if a certain condition is met. I want to trigger the script to run hourly, however, if an alert was already sent out today, I don't want to send out another one (only the next day). What is the best way to achieve this?
I've tried formatting the date several ways, but somehow the only thing working for me so far is getting the year, month and day from the date object as int and comparing them separately.
function sendAlert{
var now = new Date();
var yearNow = now.getYear();
var monthNow = now.getMonth() + 1;
var dayNow = now.getDate();
var sheet = SpreadsheetApp.getActive().getSheetByName('CHANGE_ALERT');
var sentYear = sheet.getRange("R2").getValue();
var sentMonth = sheet.getRange("S2").getValue();
var sentDay = sheet.getRange("T2").getValue();
if (yearNow != sentYear || monthNow != sentMonth || dayNow != sentDay) {
sendEmail();
var sentYear = sheet.getRange("R2").setValue(yearNow);
var sentMonth = sheet.getRange("S2").setValue(monthNow);
var sentDay = sheet.getRange("T2").setValue(dayNow);
else {
Logger.log('Alert was already sent today.');
}
}
I think this solution is definitely not the best approach, but I cannot come up with another that merges the date into one. Only comparing the new Date() doesn't work, since the time of day will not necessarily be the same. If I format the date to YYYY-MM-dd, it should work, but then when I get the date again from the spreadsheet it gets it as a full date with the time again.
Requirement:
Compare dates and send an email if one hasn't been sent already today.
Modified Code:
function sendAlert() {
var sheet = SpreadsheetApp.getActive().getSheetByName('blank');
var cell = sheet.getRange(2,18); //cell R2
var date = new Date();
var alertDate = Utilities.formatDate(cell.getValue(), "GMT+0", "yyyy-MM-dd");
var currentDate = Utilities.formatDate(date, "GMT+0", "yyyy-MM-dd");
if (alertDate !== currentDate) {
sendEmail();
cell.setValue(date);
} else {
Logger.log('Alert was already sent today.');
}
}
As you can see, I've removed all of your year/month/day code and replaced it with Utilities.formatDate(), this allows you to compare the dates in the format you specified in your question. I've also changed the if statement to match this, so now we only need to compare alertDate and currentDate.
References:
Utilities.formatDate()
Class SimpleDateFormat

Insert wrong date in MongoDB

I am tring to save dates in my MongoDB table using C#.
Here is a JavaScript logic that send data using Ajax to C# controller
$(function ($, w, d) {
var _user = {}
//var time = moment("2016-04-02", "YYYYMMDD").fromNow()
//var bime = moment().endOf('day').fromNow();
//var crime = moment("20120620", "YYYYMMDD").fromNow();
// document.getElementById('time').innerHTML = time;
var obj = {};
var holidaylist = ["Mar-31-2018", "Apr-01-2018","Apr-04-2018","Apr-07-2018","Apr-08-2018"];
var startdate = new Date("Apr-02-2018");
obj.endDate = "Apr-06-2018";
obj.holidaylist = holidaylist;
obj.NumberOfCount = 9;
CallAjax("POST", '/LeaveManagement/', 'checkleavelogic', obj, onsuccessaddemployee, '');
here is the C# logic that saves the data in MongoDB:
public JsonResult checkLeaveLogic(LeaveLogicModel leaveLogic)
{
string strconnectiomstring = "mongodb://10.10.32.125:27017";
MongoClient Client = new MongoClient(strconnectiomstring);
var DB = Client.GetDatabase("TimeClock");
List<DateTime> leavesDate = new List<DateTime>();
var collection = DB.GetCollection<LeaveLogicModel>("leaves1");
collection.InsertOne(leaveLogic);
}
Now this is the MongoDB table that saves as a previous date.
Here you can see that my StartDate is Apr-02-2018 but it saves as Apr-01-2018 and for all date it is the same.
Can someone tell me where I am wrong?
When you create new dates with just date or date and time, it usually creates them in the same timezone as the program is.
So if you dont specify the timezone, it becomes the timezone the server is in. When you save to Mongo, the Date is serialized to UTC -> zero timezone.
Create the dates with +00:00 timezone and you should have consistent data.
It's a good practice to set your datetimes to UTC, however if you want to convert it, you can use something like this.
var date = DateTime.UtcNow;
TimeZoneInfo.ConvertTime(date, TimeZoneInfo.Local);

How to get time difference between two date time in javascript?

I want time duration between two date time. I have the start date, start time, end date and end time. Now I have to find the difference between them.
Actually I have tried with this following code, but I got the alert like 'invalidate date'.
function myfunction()
{
var start_dt = '2013-10-29 10:10:00';
var end_dt = '2013-10-30 10:10:00';
var new_st_dt=new Date(start_dt);
var new_end_dt=new Date(end_dt);
alert('new_st_dt:'+new_st_dt);
alert('new_end_dt:'+new_end_dt);
var duration=new_end_dt - new_st_dt;
alert('duration:'+duration);
}
the alert msg like as follows:
new_st_dt:invalid date
new_end_dt: invalid date
duration:NaN
when I run in android simulator I got these alert messages.
Please help me how to get it? How to implement this?
You're passing an invalid ISO date string to that Date() constructor. It needs a form like
YYYY-MM-DDThh:mm:ss
for instance
2013-10-29T10:10:00
So you basically forgot the T to separate date and time. But even if the browser reads in the ISO string now, you would not have an unix timestamp to calculate with. You either can call
Date.parse( '2013-10-29T10:10:00' ); // returns a timestamp
or you need to explicitly parse the Date object, like
var duration=(+new_end_dt) - (+new_st_dt);
Further read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
Try formatting you timestamps as isoformat so javascript recognizes them. (you put a "T" between the date and time). An example: '2013-10-29T10:10:00'
function dateDiff(){
var start_dt = '2013-10-29 10:10:00';
var end_dt = '2013-10-30 10:10:00';
var d1= start_dt ;
d1.split("-");
var d2= end_dt ;
d2.split("-");
var t1 = new Date(d2[0],d2[1],d2[2]);
var t2 = new Date(d1[0],d1[1],d1[2]);
var dif = t1.getTime() - t2.getTime();
var Seconds_from_T1_to_T2 = dif / 1000;
return Math.abs(Seconds_from_T1_to_T2);
}

Relative Time jQuery Plugin (like Twitter, FB, etc) Breaks in Safari?

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?

Categories

Resources