‹ All essays

ServiceNow

Server-Side Utility APIs You Use Every Day: GlideDateTime, GlideElement, gs Methods

GlideDateTime — Date and time operations

ServiceNow stores dates in UTC internally. GlideDateTime handles timezone conversion, date math, and formatting.

// Current date/time
var now = new GlideDateTime();
gs.info(now.getValue());        // '2026-04-07 14:30:00' (UTC, internal)
gs.info(now.getDisplayValue()); // '2026-04-07 09:30:00' (user's timezone)

// Create from string
var dt = new GlideDateTime('2026-01-15 08:00:00');

// Date math — add/subtract
var dt = new GlideDateTime();
dt.addDaysUTC(7);               // add 7 days
dt.addSeconds(3600);             // add 1 hour
dt.addYearsUTC(1);               // add 1 year

// Compare dates
var dt1 = new GlideDateTime('2026-01-01 00:00:00');
var dt2 = new GlideDateTime('2026-06-01 00:00:00');
gs.info(dt1.before(dt2));       // true
gs.info(dt1.after(dt2));        // false

// Difference between dates
var diff = GlideDateTime.subtract(dt1, dt2);  // returns GlideDuration
gs.info(diff.getDayPart());     // number of days between

// Get parts
gs.info(now.getYearUTC());      // 2026
gs.info(now.getMonthUTC());     // 4
gs.info(now.getDayOfMonthUTC()); // 7

// gs date helpers — quick shortcuts
gs.info(gs.now());              // current date/time in user's format
gs.info(gs.nowDateTime());      // current date/time in system format
gs.info(gs.daysAgo(7));         // date/time 7 days ago
gs.info(gs.beginningOfLastMonth()); // first day of previous month

GlideElement — Field-level operations

When you access gr.field_name on a GlideRecord, you get a GlideElement object, not a raw value. These methods let you inspect and manipulate individual fields.

var gr = new GlideRecord('incident');
gr.get('number', 'INC0010001');

// Check if a field changed (only works in Business Rules with 'previous')
if (gr.priority.changes()) {
    gs.info('Priority changed');
}

// Check if field changed TO a specific value
if (gr.state.changesTo(6)) {
    gs.info('State changed to Resolved');
}

// Check if field changed FROM a specific value
if (gr.state.changesFrom(1)) {
    gs.info('State was previously New');
}

// Check if a field is empty
if (gr.assigned_to.nil()) {
    gs.info('No one assigned');
}
// gs.nil() works the same way
if (gs.nil(gr.getValue('assigned_to'))) {
    gs.info('Also no one assigned');
}

// Get the field's label (not the column name)
gs.info(gr.priority.getLabel());  // 'Priority'

// Get choice list display value
gs.info(gr.priority.getDisplayValue());  // '1 - Critical'

// Reference field helpers
gs.info(gr.caller_id.getRefRecord().getValue('email'));  // get the referenced record

gs (GlideSystem) — System utilities

Logging

gs.info('Informational message');           // System Logs > All
gs.warn('Warning message');                  // shows as warning level
gs.error('Error message');                   // shows as error level
gs.debug('Debug message');                   // only shows when debug enabled

// Log with source context (appears in Source column)
gs.log('message', 'MyScriptInclude');        // legacy, use gs.info instead

User context

gs.info(gs.getUserID());          // sys_id of current user
gs.info(gs.getUserName());        // 'john.smith'
gs.info(gs.getUserDisplayName()); // 'John Smith'
gs.info(gs.getUser().getEmail()); // 'john@example.com'

// Check roles (server-side)
if (gs.hasRole('admin')) { }
if (gs.hasRole('itil')) { }

System properties

// Read a system property
var val = gs.getProperty('glide.servlet.uri');  // instance URL
var custom = gs.getProperty('x_myapp.feature_flag', 'default_value');

// Set a system property (use carefully)
gs.setProperty('x_myapp.last_sync', gs.nowDateTime());

Messages and events

// Show messages to the user (in Business Rules)
gs.addInfoMessage('Record saved successfully.');
gs.addErrorMessage('Cannot close without resolution notes.');

// Queue an event (for notifications, flows, or custom processing)
gs.eventQueue('incident.escalated', current, gs.getUserID(), current.getValue('assignment_group'));

// Prefer instantiating the Script Include (Class.create pattern).
// gs.include('MyUtilityLibrary') still exists for older includes, but
// new MyUtilityLibrary() is the usual modern call site.
var utils = new MyUtilityLibrary();

Data Policy vs UI Policy

Both control field mandatory/read-only behavior, but at different layers:

FeatureUI PolicyData Policy
Runs onClient (browser) onlyServer + Client
Enforced via APINo — only on the formYes — REST, import, scripts all respect it
ConfigurationUI-only, no codeUI-only, no code
Use whenForm-only field behaviorData integrity regardless of entry point

If a field must always be mandatory — even for records created via API or Import Set — use a Data Policy. If it only matters when a user is filling out a form, UI Policy is sufficient.

Common traps

  • GlideDateTime timezone confusion: getValue() returns UTC, getDisplayValue() returns the user's timezone. When comparing dates in scripts, always use getValue() or UTC methods.
  • gr.field.nil() vs gs.nil(): Both work, but gs.nil() also handles null, undefined, and empty strings. Prefer gs.nil(gr.getValue('field')) for defensive coding.
  • gs.getProperty() performance: System properties are cached, so repeated calls are fast. But do not call gs.getProperty() inside a tight loop on a high-volume table — read it once and store it in a variable.
  • gs.addInfoMessage in async Business Rules: Will not display. The user's transaction is already complete. Only works in before/after BRs where the form is still in context.