- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 10-29-2017 11:03 AM
This is from an article I wrote for SN Pro Tips. If you like this, feel free to check out the rest!
Dealing with Time Zones in ServiceNow can be a real nightmare. If you want to know what time it is in a different time zone, 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.
Script Include Code
var TimeZoneUtils = Class.create();
TimeZoneUtils.prototype = {
/**
* Upon initialization,
* @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) {
if (gdt) {
this.gdt = gdt;
} else {
this.gdt = new GlideDateTime();
}
},
/**
* 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;
},
/**
*
* @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));
*/
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')
},
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:
Client-Side Code
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. ^_^
This document was generated from the following discussion: Handling Time-Zones in ServiceNow (TimeZoneUtils)
- 13,763 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks for the detailed explanation
Very Good Article
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Use of Packages calls is not permitted in scoped applications.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Everyone,
I had a requirement of converting the Opened time into the local time. I tried the following BR and it worked-
(function executeRule(current, previous /*null when async*/ ) {
var opened = current.opened_at.getGlideObject();
var time = new GlideDateTime(opened);
var zoneName = current.location.time_zone;
// Log the opened time in GMT
gs.log('Opened Time GMT Time: ' + time);
// Set the timezone
var tz = Packages.java.util.TimeZone.getTimeZone(zoneName);
// Calculate the offsets
var systemTimeZoneOffset = time.getTZOffset(); // Assuming this is in milliseconds
time.setTZ(tz);
var localTimeZoneOffset = time.getTZOffset();
// Adjust the time by the timezone offsets
time.addSeconds(-(systemTimeZoneOffset / 1000) + (localTimeZoneOffset / 1000));
// Log the local time
gs.log('Opened Time in Local Timezone (' + zoneName + '): ' + time);
// Store the local time in a custom field
current.u_opened_local_timezone = time; // replace custom_field with name of your date/time custom field
})(current, previous);
Hope it helps someone having similar requirement.
Regards,
Amit