32

I'm trying to utilize the momentjs library in Google Apps Script but I'm not clear on how to do so. I'm not sure how to add the library, so obviously running something like the following results in "Reference Error: 'moment' is not defined":

var a = moment([2007, 0, 29]);
var b = moment([2007, 0, 28]);
var difference = a.diff(b);
Rubén
  • 34,714
  • 9
  • 70
  • 166
Jared_C
  • 649
  • 1
  • 7
  • 16
  • 6
    I added this library key: MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48 and then defined var moment = Moment.load() and it all works great! – Jared_C Mar 14 '14 at 19:12
  • You should have mentioned that you learned about that library from [this answer](http://stackoverflow.com/a/16928369/1677912). – Mogsdad Jul 22 '15 at 02:57
  • @Mogsdad Noted. I learned of it from the accepted answer. I could have been more clear by linking to that comment again in my note. – Jared_C Aug 06 '15 at 17:27
  • 1
    Now found in the Resources menu under Libraries. Add a Library: past the key MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48. Click add. I used version 9 and it worked. – GuitarViking Jul 09 '17 at 15:01
  • As of June 2021 I am unable to add this key. I had to use the UrlFetchApp solution instead. – Anthony Elliott Jun 28 '21 at 14:23

6 Answers6

23

Most people try to use the library with the key ending in 48. That library is pretty dated (it is version 2.9 which is pretty old).

Using eval and UrlFetchApp.fetch moment.js or any other external library can be used easily in google app scripts.

function testMoment() {
  eval(UrlFetchApp.fetch('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js').getContentText());
  var date = moment().format("MMM Do YY");
  Logger.log(date)
}

You may either host the moment.js on your own server, or use a CDN like cloudflare CDN to reference the library.

For cloudflare, here is the page which shows moment.js versions and their urls:

https://cdnjs.com/libraries/moment.js/


As of writing this post 2.18.1 is the latest version.

For the example posted by OP it will look like this:

function testMomentDifference() {
  eval(UrlFetchApp.fetch('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js').getContentText());
  var a = moment([2007, 0, 29]);
  var b = moment([2007, 0, 28]);
  var difference = a.diff(b);
  Logger.log(difference);
}
apadana
  • 13,456
  • 15
  • 82
  • 98
  • 1
    This is something I didn't know was possible!! Although it will load from the CDN every time a script is run, this allows the use of many other 3rd party libs on GAS. Thanks for the heads-up! – Rodrigo Chiong Oct 25 '18 at 22:34
  • Please keep in mind that the combination of `eval` and `fetch` introduces the possibility of remote code execution in your GAS project. Although using a remote script source might seem similar to what is being done in webpages, rogue code within GAS can arguably be much more dangerous than in a sandboxed web browser. Please consider the permissions and user base of your GAS project before resorting to this approach. – MCL Dec 20 '22 at 20:47
15

The moment script ID for the Google Apps Script IDE has changed. It is now "15hgNOjKHUG4UtyZl9clqBbl23sDvWMS8pfDJOyIapZk5RBqwL3i-rlCo"

9

You can add moment and moment.tz to app scripts by creating a new Script file and adding the following code:

var cacheExpire = 3600;
var momentCache = "momentCache";
var momentUrl = "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"

var momentTzCache = "momentTzCache";
var momentTzUrl = "https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.16/moment-timezone-with-data-2012-2022.min.js"

useCachedOrLive(momentCache,momentUrl);
useCachedOrLive(momentTzCache,momentTzUrl);

function useCachedOrLive(cacheToCheck, url){

  var cache = CacheService.getUserCache();
  var cachedData = cache.get(cacheToCheck);
  console.log(cacheToCheck);
  if(cachedData !== null){
    console.log("using cached " + cacheToCheck)
    eval(cachedData);
  }
  else
  {
    console.log("getting live " + cacheToCheck);
    var response = UrlFetchApp.fetch(url).getContentText();
    cache.put(cacheToCheck, response, cacheExpire);
    eval(response);

  }
}

This uses the cache service to reduce round trip calls and you can modify it to include a subset of data if you want.

Thanks to apadana for getting me started!

Eric
  • 5,104
  • 10
  • 41
  • 70
OsulliP
  • 105
  • 1
  • 5
7

There is a better and best way to use moment

Do not use UrlFetchApp, to avoid quota exceeded, caching, and server issues

Download moment.min.js and momemt-timzone.min.js in last versions

and integrate the full files in apps script like the below screen

Moment files in apps script directly

Moment source code

There is no problems in long run with this approach, just update the files any time when you need.

After adding the two files, just publish a new version and include it in any other script

For example:

  1. I will create a script with name "MomentAPI" and include the two files mentioned, and publish a new version.
  2. in other script with name "myScript" I will include the library "MomentAPI" with its script id as known

then will use it like the below examples

const moment = MomentAPI.moment; // init the library
  
const start = moment().startOf('day').toDate(); // Dec 06 00:00:00
const end = moment().endOf('day').toDate(); // Dec 06 23:59:59
const d = moment(1767139200000).tz('America/New_York').format('ha'); // 7am EST
Islam ElKassas
  • 116
  • 1
  • 5
  • Really interesting approach. Is there anything to be aware of or concerned about in the long run with this approach? – ShibbySham Dec 08 '20 at 09:12
  • It is working perfectly and fast for me. Also to avoid UrlFetchApp quota & caching issues. We are using Moment lib in all of our projects – Islam ElKassas Dec 08 '20 at 09:31
  • I'm not sure I follow the first line in your example > const moment = MomentAPI.moment; Where is MomentAPI defined? Are you wrapping it in a new class? This errors out for me. – Rafael Slobodian Dec 23 '20 at 18:52
  • Nevermind - I was able to use it by just referencing `moment()`. The `MomentAPI` reference threw me off. You might want to update your answer. – Rafael Slobodian Dec 23 '20 at 19:33
  • @RafaelSlobodian the MomentAPI will be just a script with this name "MomentAPI", which will include the files, and will import this script in other script and call it like examples. Also you can use it directly in the same script like you made when calling moment() directly, it is the same also. I updated the answer to make the example more clear. Thanks. – Islam ElKassas Dec 30 '20 at 21:30
  • moment is working but tz is not working for me. google gives `An unknown error has occurred, please try again later.` – Jayen May 25 '21 at 03:02
  • @Jayen Note that you should import both moment.js and moment-timezone.js, otherwise timezone methods will not work You can follow this steps: 1- create 3 gs files 2- second and third one copy allover the content of moment.js and moment-timezone.js as mentioned in my answer 3- if you will use the same script, you can type directly const start = moment().startOf('day').toDate(); 4- if you will use the script as library, say will name it MyLib, so use this code in other scripts const moment = MyLib.moment; const start = moment().startOf('day'); – Islam ElKassas May 26 '21 at 11:10
  • i had imported both as .gs not library and got this error. – Jayen May 26 '21 at 15:07
  • @Jayen I added a new photo to main post to explain, how to copy the source code... you can try with an empty project from scratch and check again – Islam ElKassas May 27 '21 at 16:47
  • Thanks. I've got exactly that, made no changes, and it's working now. I guess when Google said to "try again later" they really meant it. – Jayen May 27 '21 at 23:00
  • Best answer by far, works like a charm and has scalability – tetodenega Jan 16 '22 at 18:28
4

Using external Javascript library is not so easy... Depending on the context in which you want to use it (a webapp of a document embedded script) the approach will be different.

I didn't try it in client JavaScript and I'm not sure caja will allow it but I found this post that shows a possible way to include it using a Google Script Library that a user has build and if I read the post it seems to work...

The "user" is a Google developper so he knows for sure what he is talking about ;) please update here if it worked for you.

Community
  • 1
  • 1
Serge insas
  • 45,904
  • 7
  • 105
  • 131
  • 1
    Although using that GAS library works, it's from a very outdated Momentjs version. Best way to get the latest version is using the answer below. – Rodrigo Chiong Oct 25 '18 at 22:43
  • @Rodrigo you probably didn't notice that this answer was written 4 years ago... Anyway, why downvote? What does it change? – Serge insas Oct 27 '18 at 06:54
  • I did notice but in the meantime it seems a new method (see answer below) that allows you to import any library into GAS, including moment.js latest version. Granted, this isn't a reason for a downvote but while I tried to walk back from it it seems my vote is locked until the answer is edited. Would you mind adding the method from answer below to the accepted answer? I'll upvote your answer once SO lets me (and sorry for the frenetic downvote!) – Rodrigo Chiong Oct 30 '18 at 03:52
0

You can use Moment.js library in Google Apps Script by following these steps:

  1. Open the Google Apps Script editor by going to "Tools" > "Script editor" in your Google Sheets/Docs/Slides.

  2. In the script editor, click on "Resources" > "Libraries".

  3. In the "Add a Library" field, enter the following script ID for Moment.js: "15hgNOjKHUG4UtyZl9clqBbl23sDvWMS8pfDJOyIapZk5RBqwL3i-rlCo"

  4. Select the latest version of Moment.js from the "Version" dropdown.

  5. Click "Add" to add the Moment.js library to your script.

  6. You can now use the Moment.js functions in your code. For example, to get the current date and time, you can use the following code:

var now = Moment.moment();
Logger.log(now.format('MMMM Do YYYY, h:mm:ss a'));
Brugolo
  • 4,685
  • 3
  • 22
  • 14