Dealing with Time Zones in ServiceNow can be a real nightmare. The only documented way to set the time-zone of a GlideDateTime object, is the setTZ() method. Unfortunately, this method requires a "TimeZone" object be passed in, in order to set the time-zone.
What is a TimeZone object? It's an object that doesn't exist in JavaScript, and the only way to get hold of one using the ServiceNow API is to use gs.getSession().getTimeZone(), which just gets the current user's time-zone. If you want to know what time it is in a different time zone though, you're out of luck, chump!
At least, you were. Sufficiently annoyed by this problem, I finally decided to write a Script Include to handle this for me, and now that I've been using (and testing) it for a while now, I'm comfortable publishing it for general consumption. Hopefully you'll find each method of the Script Include well documented, so I'll skip the usual explanation about what's going on in each function. If you want to know how it works, just have a look at the comments.
Note: If you'd prefer that, even for well-documented scripts, I still provide a detailed explanation, leave a comment and let me know!
var TimeZoneUtils = Class.create(); TimeZoneUtils.prototype = { /** * Upon initialization, you can pass in a GlideDateTime object you've already created and set to a specific time. * The reference to this object will be used, and your GDT will be modified in-place. Alternatively, you may choose * not to specify a parameter upon initialization, and a new GlideDateTime object will be created, used, and returned * with the current time in the specified time-zone. * * @param {GlideDateTime} [gdt] - A reference to the (optional) GlideDateTime object to be modified IN-PLACE. * If not specified, a new one will be generated, and a reference returned. */ initialize: function(gdt) { this.gdt = (typeof gdt == 'undefined') ? (new GlideDateTime()) : gdt; }, /** * Get the GlideDateTime object (as a reference). * This will return a *reference* to the GlideDateTime object. Note that because of JavaScript's * pass-by-reference jive, you should expect that if you set a variable using this method, then * call another method which modifies the GDT object referenced in this class, you will be modifying * the object to which your variable is a reference! In other words, your variable will be modified *in-place*. * @returns {*|GlideDateTime} */ getGDT: function() { return this.gdt; }, /** * Get the number representing the current GDT object's offset from UTC, in hours. * If the GlideDateTime object is in the Pacific time zone for example, this method will return either * "8" or "7" (depending on DST). * @returns {number} */ getOffsetHours: function() { return ((Number(this.gdt.getTZOffset() / 1000) / 60) / 60); }, /** * Note that you can specify time-zones in a number of formats, like "US/Pacific", * "US\\Eastern", or by short name (such as "mountain"). * * Currently, this utility only understands a few time-zones by short name. You can print out a list of * pre-defined these supported short-names by printing out the keys in the timeZones property. * Example: gs.print(Object.keys(new TimeZoneUtils().timeZones)); * * You can reference any time-zone using the following (case-sensitive) format: * <Region>\<Zone> * Example: "Pacific\Guam", or "America\Puerto_Rico" * * @param {Packages.java.util.TimeZone|string} tz - The TimeZone object to use to set the time-zone of * the current GlideDateTime object. * @returns {*|GlideDateTime} */ setTimeZone: function(tz) { /* FYI: http://twiki.org/cgi-bin/xtra/tzdatepick.html Click any of the locations there, and on the corresponding page, find the "Timezone" value. These are the valid values for the time-zone parameter. */ //ensure we've got a string and that it's lower-case. tz = (typeof tz === 'string') ? tz : tz.toString(); //Validate the TZ string, and get a TimeZone Java object from it. tz = this._getTimeZoneFromString(tz); this.gdt.setTZ(tz); return this.gdt; }, /** * Gets the display value of the current GlideDateTime object. * If a time-zone was specified by calling .setTimeZone(), this will return the time in that time-zone. * If the GDT's time value was set prior to passing it into TimeZoneUtils, this will return that date/time * in the specified time-zone. * @returns {string} The current time, in the specified time-zone. */ getDisplayValue: function() { return this.gdt.getDisplayValue(); }, /** * @returns {string} The current value, in SYSTEM time, of the GlideDateTime object. */ getValue: function() { return this.gdt.getValue(); }, /** * * @param {Packages.java.util.TimeZone|string} tz - The TimeZone object to use to set the time-zone of * @returns {*} The TimeZone object, OR false if an invalid time-zone was passed in. * @private */ _getTimeZoneFromString: function(tz) { //If it's a valid time-zone coming in, bob's our uncle. if (this._isValidTimeZone(tz)) { if (this.timeZones.hasOwnProperty(tz.toLowerCase())) { return this.timeZones[tz.toLowerCase()]; } else { return Packages.java.util.TimeZone.getTimeZone(tz); } } //Otherwise, check if it matches one of our timeZone object properties. var shortTZ = this._getShortTimeZoneName(tz); if (this._isValidTimeZone(shortTZ)) { return this.timeZones[shortTZ.toLowerCase()]; } //If nothing else has returned by now, it means the time zone isn't valid. gs.warn('Invalid time zone specified. Time zone: ' + tz, 'TimeZoneUtils Script Include, _getTimeZoneFromString method'); return false; }, /** * Checks if the passed string is a valid time zone string. * @param {string} tz - The TimeZone string to use to set the time-zone of * @returns {boolean} * @private */ _isValidTimeZone: function(tz) { var tzObj = Packages.java.util.TimeZone.getTimeZone(tz); //If the tz string wasn't valid, then getID will return the string "GMT", //which - unless the user specified GMT as the time-zone, will not match the string argument. //However, if it does match, OR if the arg is found in the timeZones object, then we're good to go. return ((String(tzObj.getID()) === tz) || this.timeZones.hasOwnProperty(tz.toLowerCase())); }, /** * Try another way of getting the proper time-zone. This is used when to look for a time-zone based only on the short-name. * @param {string} tz - The time-zone name we're looking at, at a string. * @returns {string} The time-zone, or a valid version of it if it needs validation, in lower-case. * @private */ _getShortTimeZoneName: function(tz) { //Check if the string contains a forward-slash, back-slash, or underscore. if (tz.indexOf('\\') >= 0 || tz.indexOf('/') >= 0 || tz.indexOf(' ') >= 0) { /* If it contains a "/" or "\", grab everything after that character. Trim the resulting (sub-)string. If the remainder contains a space, replace it with an underscore. */ tz = tz.slice(tz.indexOf('\\') + 1).slice(tz.indexOf('/') + 1).trim().replace(/ /g, '_'); } return tz.toLowerCase(); }, /** * Just a reference to the setTimeZone method. * @param {Packages.java.util.TimeZone|string} tz - The TimeZone object to use to set the time-zone of the current GlideDateTime object. * @returns {*} */ setTZ: function(tz) { return this.setTimeZone(tz); }, /** * These are the pre-defined short-names for certain common time-zones. * Feel free to expand upon this object. * Currently, this utility only understands a few pre-defined time-zones by short name. * You can print out a list of these supported short-names by printing out the keys in the timeZones property. * Example: gs.print(Object.keys(new TimeZoneUtils().timeZones)); * In a future update, this list will update itself with values from the sys_choice table, here: * https://YOUR_INSTANCE.service-now.com/sys_choice_list.do?sysparm_query=nameINjavascript%3AgetTableExtensions('sys_user')%5Eelement%3Dtime_zone */ timeZones: { alaska: Packages.java.util.TimeZone.getTimeZone('US/Alaska'), eastern: Packages.java.util.TimeZone.getTimeZone('US/Eastern'), central: Packages.java.util.TimeZone.getTimeZone('US/Central'), mountain: Packages.java.util.TimeZone.getTimeZone('US/Mountain'), hawaii: Packages.java.util.TimeZone.getTimeZone('US/Hawaii'), pacific: Packages.java.util.TimeZone.getTimeZone('US/Pacific'), arizona: Packages.java.util.TimeZone.getTimeZone('US/Arizona'), guam: Packages.java.util.TimeZone.getTimeZone('Pacific/Guam'), puerto_rico: Packages.java.util.TimeZone.getTimeZone('America/Puerto_Rico'), india: Packages.java.util.TimeZone.getTimeZone('Asia/Kolkata'), utc: Packages.java.util.TimeZone.getTimeZone('UTC') }, type: 'TimeZoneUtils' };
Example usage
var gdt = new GlideDateTime(); //Get the current time in the Pacific time-zone gs.print(gdt.getDisplayValue()); //Print the current time in the user's time-zone. var tzu = new TimeZoneUtils(gdt); //Specify a GDT object is optional. If unspecified, a newly initialized GDT with the current date/time will be used. tzu.setTimeZone('US/Eastern'); //Sets the time-zone to US/Eastern (GMT-5) gs.print(gdt.getDisplayValue()); //Prints out the current time in the Eastern time-zone
You may simply copy and paste that into your instance, and you'll have a server-side utility for handling time-zones! And as for a client-side version, well here's a quick GlideAjax one for you:
var ClientTimeZoneUtils = Class.create(); ClientTimeZoneUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, { getCurrentTimeInTimeZone: function() { var tz = this.getParameter('sysparm_tz'); var tzu = new TimeZoneUtils(); var gdt = tzu.setTimeZone(tz); return gdt.getDisplayValue(); }, type: 'ClientTimeZoneUtils' });
Example Usage
var gaTZ = new GlideAjax('ClientTimeZoneUtils'); //initialize GA gaTZ.addParam('sysparm_name', 'getCurrentTimeInTimeZone'); //specify the function to run gaTZ.addParam('sysparm_tz', 'guam'); //specify the time-zone as one of the predefined shorthand values gaTZ.getXML(gaCallback); function gaCallback(response) { var answer = response.responseXML.documentElement.getAttribute('answer'); console.log('The current time in the time-zone you specified, is ' + answer); }
It's probably a good idea to add a "get specified time in specified time-zone" sort of function into the client-callable Script Include, but I didn't get around to it. Perhaps I'll update this article later once I get around to writing one. ^_^
-
March 2024
- Mar 28, 2024 How to Identify Duplicate Records by Multiple Fields in ServiceNow Mar 28, 2024
- Mar 7, 2024 How to Merge Personal & Company ServiceNow Accounts Mar 7, 2024
-
February 2024
- Feb 12, 2024 5 Lessons About Programming From Richard Feynman Feb 12, 2024
-
July 2023
- Jul 5, 2023 Managing Instance-Specific System Properties for Dev/Test/Prod in ServiceNow Jul 5, 2023
-
May 2023
- May 11, 2023 5 Ways to Check your ServiceNow Instance for DANGEROUS CODE in Less Than 5 minutes May 11, 2023
-
April 2023
- Apr 28, 2023 Your ACLs and Business Rules are Broken (Here's How to Fix Them) Apr 28, 2023
-
December 2022
- Dec 13, 2022 ServiceNow Developers: BE THE GUIDE! Dec 13, 2022
-
October 2022
- Oct 19, 2022 A Faster, More Efficient Client-side GlideRecord (Free tool!) Oct 19, 2022
- Oct 9, 2022 Animated Loading Message & Collapsible Details on ServiceNow Form or Field (Client-side) Oct 9, 2022
-
August 2022
- Aug 23, 2022 Using .addJoinQuery() & How to Query Records with Attachments in ServiceNow Aug 23, 2022
- Aug 18, 2022 Free, Simple URL Shortener for ServiceNow Nerds (snc.guru) Aug 18, 2022
- Aug 16, 2022 How to Get and Parse ServiceNow Journal Entries as Strings/HTML Aug 16, 2022
- Aug 14, 2022 New tool: Get Latest Version of ServiceNow Docs Page Aug 14, 2022
-
March 2022
- Mar 4, 2022 How to Set or Change ServiceNow Application's Repository URL, Credentials, or SSH Key Mar 4, 2022
-
February 2022
- Feb 7, 2022 How to return a CSV file from a Scripted REST API (SRAPI) in ServiceNow Feb 7, 2022
-
May 2021
- May 3, 2021 Adding a Guided Setup to Your ServiceNow Application May 3, 2021
-
April 2021
- Apr 27, 2021 Use Automated Tests to Validate "Guided Setup" Completion & Functionality. Apr 27, 2021
-
February 2021
- Feb 11, 2021 "Processors", SRAPIs, and How to Run a Script and Redirect a User From a URL in ServiceNow Feb 11, 2021
-
November 2020
- Nov 17, 2020 SN Guys is now part of Jahnel Group! Nov 17, 2020
-
September 2020
- Sep 14, 2020 Better ServiceNow Notifications (& Another FREE Tool!) Sep 14, 2020
-
July 2020
- Jul 31, 2020 Debugging Client & Catalog Client Scripts in ServiceNow Jul 31, 2020
-
January 2020
- Jan 20, 2020 Getting Help from the ServiceNow Community Jan 20, 2020
-
December 2019
- Dec 18, 2019 Can ServiceNow Script Includes Use the "current" Variable? Dec 18, 2019
-
November 2019
- Nov 18, 2019 Handling 'text/plain' and Other Unsupported Content Types in ServiceNow Scripted REST APIs Nov 18, 2019
-
April 2019
- Apr 21, 2019 Understanding Attachments in ServiceNow Apr 21, 2019
- Apr 10, 2019 Using Custom Search Engines in Chrome to Quickly Navigate ServiceNow Apr 10, 2019
- Apr 4, 2019 Set Catalog Variables from URL Params (Free tool) Apr 4, 2019
- Apr 1, 2019 Outlook for Android Breaks Email Approvals (+Solution) Apr 1, 2019
-
March 2019
- Mar 11, 2019 GlideFilter is Broken - Free Tool: “BetterGlideFilter” Mar 11, 2019
-
February 2019
- Feb 27, 2019 Making Update Sets Smarter - Free Tool Feb 27, 2019
-
November 2018
- Nov 29, 2018 How to Learn ServiceNow Nov 29, 2018
- Nov 6, 2018 ServiceNow & ITSM as a Career? Nov 6, 2018
-
October 2018
- Oct 19, 2018 Asynchronous onSubmit Catalog/Client Scripts in ServiceNow Oct 19, 2018
- Oct 11, 2018 How to do Massive, Slow Database Operations Efficiently With Event-Driven Recursion Oct 11, 2018
-
September 2018
- Sep 18, 2018 Broken Queries & Query Business Rules in ServiceNow Sep 18, 2018
- Sep 7, 2018 JournalRedactor - Easily Redact or Delete Journal Entries in ServiceNow! Sep 7, 2018
-
July 2018
- Jul 23, 2018 Admin Duty Separation with a Single Account Jul 23, 2018
-
June 2018
- Jun 19, 2018 Improving Performance on Older Instances with Table Rotation Jun 19, 2018
- Jun 4, 2018 New Free Tool: Login Link Generator Jun 4, 2018
-
May 2018
- May 29, 2018 Learning ServiceNow: Second Edition! May 29, 2018
-
April 2018
- Apr 17, 2018 Upgrading From Express to Enterprise: What's Missing Apr 17, 2018
- Apr 12, 2018 If a Genie Gave Me Three Wishes, I'd Use Them All to "Fix" Scope Apr 12, 2018
-
March 2018
- Mar 19, 2018 Service Catalog "Try in Portal" button Mar 19, 2018
- Mar 15, 2018 Video: Custom Output Transition Conditions From a Single Workflow (Script) Activity Mar 15, 2018
-
February 2018
- Feb 11, 2018 We have a new book! Feb 11, 2018
-
November 2017
- Nov 6, 2017 Requiring Attachments (& Other Miracles) in Service Portal Nov 6, 2017
-
September 2017
- Sep 12, 2017 Handling TimeZones in ServiceNow (TimeZoneUtil) Sep 12, 2017
-
July 2017
- Jul 27, 2017 How to Enable DOM Manipulation in ServiceNow Service Portal Catalog Client Scripts Jul 27, 2017
-
June 2017
- Jun 25, 2017 What's New in ServiceNow: Jakarta (Pt. 1) Jun 25, 2017
- Jun 4, 2017 Powerful Scripted Text Search in ServiceNow Jun 4, 2017
-
May 2017
- May 9, 2017 Work at Lightspeed: ServiceNow's Plan for World Domination May 9, 2017
-
April 2017
- Apr 9, 2017 Avoiding Pass-By-Reference Using getValue() & setValue() Apr 9, 2017
- Apr 4, 2017 "Learning ServiceNow" is Now Available for Purchase! Apr 4, 2017
-
March 2017
- Mar 12, 2017 reCAPTCHA in ServiceNow CMS/Service Portal Mar 12, 2017
-
December 2016
- Dec 20, 2016 Pro Tip: Use updateMultiple() for Maximum Efficiency! Dec 20, 2016
- Dec 2, 2016 We're Writing a Book! Dec 2, 2016
-
November 2016
- Nov 10, 2016 Chrome Extension: Load in ServiceNow Frame Nov 10, 2016
-
September 2016
- Sep 7, 2016 Force-Include Any Record Into an Update Set Sep 7, 2016
- Sep 1, 2016 GlideRecord Pagination - Page through your GlideRecord query Sep 1, 2016
-
July 2016
- Jul 17, 2016 Granting Temporary Roles/Groups in ServiceNow Jul 17, 2016
- Jul 15, 2016 Scripted REST APIs & Retrieving RITM Variables via SRAPI Jul 15, 2016
-
May 2016
- May 17, 2016 What's New in Helsinki? May 17, 2016
-
April 2016
- Apr 27, 2016 Customizing UI16 Through CSS and System Properties Apr 27, 2016
- Apr 5, 2016 ServiceNow Versions: Express Vs. Enterprise Apr 5, 2016
-
March 2016
- Mar 28, 2016 Update Set Collision Avoidance Tool: V2 Mar 28, 2016
- Mar 18, 2016 ServiceNow: What's New in Geneva & UI16 (Pt. 2) Mar 18, 2016
-
February 2016
- Feb 22, 2016 Reference Field Auto-Complete Attributes Feb 22, 2016
- Feb 6, 2016 GlideRecord & GlideAjax: Client-Side Vs. Server-Side Feb 6, 2016
- Feb 1, 2016 Make Your Log Entries Easier to Find Feb 1, 2016
-
January 2016
- Jan 29, 2016 A Better, One-Click Approval Jan 29, 2016
- Jan 25, 2016 Quickly Move Changes Between Update Sets Jan 25, 2016
- Jan 20, 2016 Customize the Reference Icon Pop-up Jan 20, 2016
- Jan 7, 2016 ServiceNow: Geneva & UI16 - What's new Jan 7, 2016
- Jan 4, 2016 Detect/Prevent Update Set Conflicts Before They Happen Jan 4, 2016
-
December 2015
- Dec 28, 2015 SN101: Boolean logic and ServiceNow's Condition Builder Dec 28, 2015
- Dec 17, 2015 Locate any record in any table, by sys_id in ServiceNow Dec 17, 2015
- Dec 16, 2015 Detecting Duplicate Records with GlideAggregate Dec 16, 2015
- Dec 11, 2015 Array.indexOf() not working in ServiceNow - Solution! Dec 11, 2015
- Dec 2, 2015 Understanding Dynamic Filters & Checking a Record Against a Filter Using GlideFilter Dec 2, 2015
-
October 2015
- Oct 20, 2015 Bookmarklet: Load the current page in the ServiceNow frame Oct 20, 2015
-
August 2015
- Aug 27, 2015 Easily Clone One User's Access to Another User Aug 27, 2015