How to create All Day Calendar Event 14 days from now - javascript

I'm trying to create an all-day calendar event to 14 days from today using Google App Script but I keep getting the error
Exception: The parameters (String,String) don't match the method signature for CalendarApp.Calendar.createAllDayEvent.
My script is:
var SheetApp = SpreadsheetApp.getActiveSheet();
var SelectedRow = SheetApp.getActiveRange().getRowIndex();
var value = SheetApp.getRange(SelectedRow, 8).getValue(); //Name of the event
var eventCal = CalendarApp.getCalendarById("sampleemail#gmail.com");
eventCal.createAllDayEvent(value.toString(), new Date() + 14);

When you add a number to a Date object, the Date is automatically converted to its underlying value, i.e., the number of milliseconds since the epoch. The result of the addition will be that number of milliseconds plus 14, which is just a big integer. The createAllDayEvent() method expects a Date object rather than an integer.
To get a date that is 14 days from now, use Date.setDate(). This method has the benefit that it automatically takes care of rolling into the next month or next year as necessary, but be careful. The setDate() method modifies a Date object in place, but does not return the Date object. Instead, it returns the underlying value, i.e., the number of milliseconds since the epoch.
This means that you cannot just assign the result you get from setDate() to a variable in the hope of getting a valid Date. Something like const twoWeeksFromNow = new Date().setDate(new Date().getDate() + 14) will not work if you expect twoWeeksFromNow to contain a Date.
An easy way to get it right is to use a short utility function, like this:
function testCreateAllDayEvent() {
const twoWeeksFromNow = dateOffset_(new Date(), +14);
const eventCal = CalendarApp.getCalendarById("sample_email#gmail.com");
const sheet = SpreadsheetApp.getActiveSheet();
const eventTitle = sheet
.getRange('H' + sheet.getActiveRange().getRow())
.getDisplayValue();
eventCal.createAllDayEvent(eventTitle, twoWeeksFromNow);
}
/**
* Gets a new Date object that is offset by numDays from date.
*
* #param {Date} date The date from which to count.
* #param {Number} numDays The number of days to add or subtract from date.
* #return {Date} A new Date object that is offset by numDays from date.
*/
function dateOffset_(date, numDays) {
const newDate = new Date(date);
newDate.setDate(date.getDate() + numDays);
return newDate;
}

Change second parameter
new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() + 14)

Related

moment.js | check isBefore getting confused with date

I'm currently working with a project where I need to find if a time in HH:mm is before another time.
I am using moment time zone and setting the time zone globally to UTC.
The date which I am comparing is: 2020-09-02T00:00:00.0000+00:00
I am running a check where I am doing the following:
const example = '2020-09-02T00:00:00.0000+00:00'
const time = moment(example)
const timeStart = moment('08:00', 'HH:mm')
console.log(time.isBefore(timeStart))
The console log returns a result of false.
The reason I believe is that the timeStart is evaluating to the current day, so it's failing due to the date being in the future. I need to stop moment from comparing the date, is there a way to do this?
What I am trying to achieve is something of the following
'00:00:00'.isBefore('08:00')
const time = moment('2020-09-02T00:00:00.0000+00:00')
const timeStart = moment('08:00', 'HH:mm')
console.log(time)
console.log(timeStart)
console.log(time.isBefore(timeStart))
<script src="https://cdn.jsdelivr.net/momentjs/2.13.0/moment.min.js"></script>
You can do this with moment.js by converting the timestamp to a moment object, then cloning the object and setting its time to the required comparison time. That way you're always comparing times on the same date.
To keep everything as UTC, use utc. E.g.
let ts = '2020-09-02T00:00:00.0000+00:00';
let d = moment.utc(ts); // Invoke UTC mode
let time = '08:30';
let [h, m] = time.split(':');
let e = d.clone().hours(h).minutes(m).seconds(0).milliseconds(0);
console.log(d.format() + ' is before\n' +
e.format() + '? ' + d.isBefore(e));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>
You can compare just the time with plain JS fairly easily:
/**
* Compare a time to just the time part of a
* Date, all using UTC.
*
* #param {Date} date - date to compare
* #param {string} time - time in HH:mm format
* #returns {boolean} true if time in date is before time passed to function
*/
function isTimeBefore(date = new Date(), time) {
// Get time value for start of UTC day
// Copy date so don't affect original
let dayStart = new Date(+date).setUTCHours(0,0,0,0);
// Convert the time to milliseconds since midnight
let [h, m] = time.split(':');
let ms = h*3.6e6 + m*6e4;
// Compare to the date's milliseconds elapsed since midnight
return ms > (date - dayStart);
}
// UTC timestamp
let ts = '2020-09-02T00:00:00.0000+00:00';
// Convert to Date
let d = new Date(ts);
// local time
let time = '08:30';
console.log('UTC: ' + d.toISOString() + '\nis before ' + time + ' UTC? : ' + isTimeBefore(d, time));
The built–in parser should generally be avoided, however the above uses it to convert the timestamp to a Date because it's about the only supported format that is reliably parsed by browsers in use.
const time = moment('2020-09-02T00:00:00.0000+00:00')
const timeStart = moment('09:00', 'HH:mm')
time.isBefore(timeStart) //returns true
returns true to me.

javascript add 1ms to a string date

I have a var in js which is represented like so:
"lastTimeModified": "2019-02-26T11:38:20.222Z"
and I want to add to it 1ms
I have tried something like this:
dateObj = new Date();
dateObj.setMilliseconds(1);
var newDate = lastTimeModified + dateObj
but that doesn't seem to work.
If you can get your date in milliseconds than you can directly add 1 millisecond to it
var date = new Date(lastTimeModified) // convert string to date object
var mill = date.getMilliseconds() // Get millisecond value from date
mill += 1 // Add your one millisecond to it
date.setMilliseconds(mill) // convert millisecond to again date object
The below takes your time string turns it into a date object, gets the milliseconds since the 1. of January 1970 adds 1ms and turns it back into a date object
var newDate = new Date(new Date(lastTimeModified).getTime() + 1)

How to calculate the difference between 2 dates

All I am trying to do is this.
Let's say in Google Sheets, I have a page which has the following columns:
In column I3 I have the date of 11/30/19.
In column J3, I have Today's date of 4/30/20.
I want to be able to calculate the difference between these two dates and tell me how many months has it been since 11/30/19.
Currently, I think the code is working somewhat but the result I get is:
[20-05-02 01:43:18:650 MDT] 5 months, 6 days
[20-05-02 01:43:18:656 MDT] 5 months, 6 days
[20-05-02 01:43:18:660 MDT] 5 months, 6 days
But the date calculations are still wrong. For example from Jan 1st - Jan 25, 2020, it shows 5 month and 6 days.
Also the loop I have inside Filter 1, is just calculating the first available date and then it does this three times instead of going to the next record and calculate.
I have the following code so far:
function myFunction() {
}
var moment = Moment.load();
/**
* #summary gets date difference
* #param {Date} startDate
* #param {Date} endDate
* #returns {string}
*/
function getDuration(startDate, endDate) {
const start = moment(startDate);
const end = moment(endDate);
const units = ['years', 'months', 'days'];
const lastIndex = units.length - 1;
const parts = units
.map((unit,i) => {
const diff = Math.floor(end.diff(start, unit, true));
if (diff > 0 || i === lastIndex) {
end.subtract(unit, diff);
return `${diff} ${unit}`;
}
})
.filter(Boolean);
return parts.join(', ');
}
function Filter2() { // Calculate the Time
const spread = SpreadsheetApp.getActiveSpreadsheet();
const sheets = spread.getSheets();
const [ sheet_1, sheet_2 ] = sheets;
const row = sheet_1.getRange("A:M");
const arr_col = sheet_1.getRange("I3:I50");
const lastSeen_col = sheet_1.getRange("J3:J50");
const startDate = arr_col.getValue();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var headerRowNumber = 2;
var rows = sheet.getDataRange().offset(headerRowNumber, 0, sheet.getLastRow() - headerRowNumber).getValues();
var filter = rows.filter(row => row[9] !== '');
// var digest = filter[0][9];
for(var i=0;i<filter.length; i++){
var res = getDuration(startDate, lastSeen_col);
Logger.log(res);
}
}
Why Dec, 31 1969
31st of December, 1969 in GMT-7 timezone offset is January 1st, 1970 00:00:00 UTC, which is the unix epoch. Therefore it is likely to be caused by an invalid date format passed to the Moment class instance.
Problem
getRange() method call returns an instance of Range from which you have to extract value via getValue() / getValues() before being able to use it. With that in mind, let's track what's going on in your script:
var ArrRow = sheet_1.getRange("I3:I") returns an instance of Range
mydata1 is defined somewhere globally (let's assume it holds an instance of Date)
getDuration is thus called like this: getDuration( <Range>, <Date> )
startDate and endDate are respectively an instance of Range and Date
start and end hold results of invoking the moment factory that creates a wrapper around Date object.
Step 5 is most likely to be the culprit, as per Moment library docs, moment() can accept String, Number, Date instances as well as format options, but start recieves a Range instance instead.
Possible solution
First, you need to ensure you pass in correct types (this why a lot of folks prefer TypeScript - if that's not your cup of tea, at least start using JSDoc - it will save you a ton of debug time).
I don't know how big the I3:I range is, so I assumed from the context that it is a single cell containing start date. Also note that I removed the var sld = new Date(dateString) assignment since you return a human readable string from getDuration() of format Y years, M months, D days which is not a dateString that Date constructor can accept.
Additionally, I would suggest changing forEach() to map() method for cleaner and less side effect prone code. General rule of thumb is that if input and output are of the same type, you likely want to use a mapping function.
function myFunction() {
Filter2();
}
function Filter2() {
const spread = SpreadsheetApp.getActiveSpreadsheet();
const sheets = spread.getSheets();
const [ sheet_1, sheet_2 ] = sheets;
const arr_col = sheet_1.getRange("I3:I");
const startDate = arr_col.getValue();
var dateString = getDuration(startDate, mydata1);
Logger.log(dateString);
}
var moment = Moment.load();
/**
* #summary gets date difference
* #param {Date} startDate
* #param {Date} endDate
* #returns {string}
*/
function getDuration(startDate, endDate) {
const start = moment(startDate);
const end = moment(endDate);
const units = ['years', 'months', 'days'];
const lastIndex = units.length - 1;
const parts = units
.map((unit,i) => {
const diff = Math.floor(end.diff(start, unit, true));
if (diff > 5 || i === lastIndex) {
end.subtract(unit, diff);
return `${diff} ${unit}`;
}
})
.filter(Boolean);
return parts.join(', ');
}
Notes
The answer assumes you use V8 engine (if you don't - switch to it, old one will be deprecated some time in the future, and as a bonus you get to use all the juciest language features [well, most of them]).
References
Date constructor docs on MDN
Range class docs
getValue() method docs
getValues() method docs

Stop date to epoch conversion in javascript

I have a code where I try to set date 20 days back from current date on server. I have used a variable(say dateRange) in javascript to get current date. But on using the same variable second time for setDate() function value of dateRange is changed to epoch from date. I know I can convert epoch to date and proceed but is there a way to stop this automatic conversion.
var dateRange=new Date(currentDate);
dateRange = dateRange.setDate(dateRange.getDate() - 20);
setDate modifies the date object, and returns the epoch value. Just don't save the epoch value in dateRange, so you can use the date object after modifying it:
var currentDate = new Date();
var dateRange = new Date(+currentDate);
console.log(dateRange.toISOString());
dateRange.setDate(dateRange.getDate() - 20);
console.log(dateRange.toISOString());
Side note: Copying a date by doing var dateRange = new Date(currentDate); is/was unreliable on some browsers. In the above, I've changed it to var dateRange = new Date(+currentDate); (note the +, converting the date to its epoch value), which is reliable.
Internally dates are stored as milliseconds from the epoch date.
To achieve what you are trying to do you can subtract the number of milliseconds that corresponds to 20 days:
var currentDate = "2019-07-29T07:14:57.269Z";
var dateRange = new Date(currentDate);
var pastDate = new Date(dateRange - 1000 * 60 * 60 * 24 * 20);
console.log('current', currentDate);
console.log('20 days ago', pastDate);
anyway if you are doing a lot of date/time manipulations in your app i suggest you to use this library: https://momentjs.com/

(getDate() - 1) function is getting the value zero if the current date is 1

My Requirement:
I'm having to fields Start Date and End Date, If the End Date is left empty while saving the record, the End Date Field value is populated with plus 1 year based on the entered from date.
My Issue:
If the Start Date is "9/1/2016" and the End Date is Left Empty means it should automatically populate the End Date value as "8/31/2016" but it returning the End Date value as "9/0/2016" and also i'm getting the following ERROR MESSAGE
Error: JS_EXCEPTION
INVALID_FLD_VALUE You have entered an Invalid Field Value Invalid Date for the following field: custrecord_end_date
CODE:
SCRIPT : CLIENT SCRIPT, EVENT :SaveRecord
function saveRecord(scriptContext) {
var newRecord= scriptContext.currentRecord;
var fromDate = new Date(newRecord.getValue('custrecord_created_date'));
var endDate = newRecord.getValue('custrecord_end_date');
if (endDate == null || endDate == '') {
//getting plus 1 year based on the From Date
tempEndDate = addingPlusYearOfTheCurrentDate(fromDate);
//setting the value to the End Date Field
newRecord.setValue('custrecord_end_date', tempEndDate);
}
}
// Add Plus Year from the Start Date when the End Date is Empty
function addingPlusYearOfTheCurrentDate(fromDate ) {
var date = new Date();
var Month = (fromDate.getMonth() + 1);
var Dates = (fromDate.getDate() - 1);
var Year = (fromDate.getFullYear() + 1);
var last_Day = new Date(Month + '/' + Dates + '/' + Year);
log.debug('last_Day:', last_Day);
return last_Day;
}
Not sure why you expected to be able to subtract 1 from 1 and get anything other than 0, but you can solve this problem by using the Date object's setFullYear() and setDate().
function addingPlusYearOfTheCurrentDate(fromDate) {
var date = new Date(fromDate);
date.setFullYear(date.getFullYear() + 1);
date.setDate(date.getDate() - 1);
return date;
}
console.log(addingPlusYearOfTheCurrentDate(new Date(2015, 10, 1)));
You should use the method nlapiStringToDate() for string to date conversions, as NetSuite gives date field value as string, which you must convert to date, and before you set back date, you must use nlapiSetFieldValue(YOUR_FIELD_ID, nlapiStringToDate(dateObject))
Please see below on suggested usage on reading and setting date fields.
function saveRecord(scriptContext) {
var newRecord = scriptContext.currentRecord;
var fromDate = nlapiStringToDate(newRecord.getValue('custrecord_created_date'));
var endDate = nlapiStringToDate(newRecord.getValue('custrecord_end_date'));
if (endDate == null || endDate == '') {
//getting plus 1 year based on the From Date
tempEndDate = addingPlusYearOfTheCurrentDate(fromDate);
//setting the value to the End Date Field
newRecord.setValue('custrecord_end_date', nlapDateToString(tempEndDate));
}
Parsing strings with the Date constructor (and Date.parse, they are equivalent for parsing) is strongly recommended against since parsing is almost entirely implementation dependent and inconsistent. Manually parse strings with a custom function or use a library.
Adding a year to a Date is fairly simple, but it seems you want the date that is one day prior to the same date next year. So add one year then subtract one day.
// Parse m/d/y format string to a Date and validate the result
function parseMDY(s) {
var b = s.split(/\D/);
var d = new Date(b[2], --b[0], b[1]);
return d && d.getMonth() == b[0]? d : new Date(NaN);
}
// Add 1 year to a Date
function addYear(d) {
if (Object.prototype.toString.call(d) != '[object Date]') return;
d.setFullYear(d.getFullYear() + 1);
d.setDate(d.getDate() -1);
return d;
}
var d = parseMDY('9/1/2016');
console.log(d.toLocaleString())
addYear(d);
console.log(d.toLocaleString())
Note that for 29 February, adding one year gives 1 May, then subtracting one day will give 28 February.
Is this a 1.0 or 2.0 script?
NetSuite's 1.0 API offers a couple date manipulation methods that might be helpful to you here: nlapiAddMonths and nlapiAddDays, as well as the Date-String conversion methods.
Here's an example of what you could do in 1.0
// 1.0 API does not pass scriptContext to saveRecord
function saveRecord() {
// Use nlapiStringToDate instead of raw Date constructor
var fromDate = nlapiStringToDate(nlapiGetFieldValue('custrecord_created_date'));
// Instead of the full extra conditional, just use || as fallback
var endDate = nlapiStringToDate(nlapiGetFieldValue('custrecord_end_date')) ||
calculateEndDate(fromDate);
// setting the value to the End Date Field
nlapiSetFieldValue('custrecord_end_date', nlapiDateToString(endDate));
}
/** #param fromDate {Date} */
function addYear(fromDate) {
return nlapiAddMonths(fromDate, 12);
}
/** #param fromDate {Date} */
function dayBefore(fromDate) {
return nlapiAddDays(fromDate, -1);
}
/** #param startDate {Date} */
function calculateEndDate(startDate) {
// add 1 year first, then subtract one day
return dayBefore(addYear(startDate));
}
If you're using 2.0 just add a comment, and I will try to update the example if I can. If you've got any questions about how this works, feel free to let me know as well.

Categories

Resources