I have already achieved my objective to prevent future date, its just I think something is changing my closed date field after I save and that triggers my client script the second time. To add on, this does not happen in classic UI, just in workspace.

client script:

 

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
   if (isLoading || newValue === '') {
      return;
   }
   var ga = new GlideAjax('...');
    ga.addParam('sysparm_name', 'isFutureDate');
    ga.addParam('sysparm_selectedDate', newValue);
    ga.getXMLAnswer(function(answer) {		
        if (answer == 'true') {
			g_form.clearValue('closed_at');			
            g_form.addErrorMessage('Future date and time not allowed');
			return;
        }
    }); 
}

 


Script include:

 

isFutureDate: function() {

        var sDate = this.getParameter('sysparm_selectedDate');
        var result = false;        
        var selectedDate = new GlideDateTime(this.formatDate(sDate));

        var currentDate = new GlideDateTime();
        currentDate.addSeconds(28800);
        gs.info('test date selectedDate: ' + selectedDate + ' - currentDate: ' + currentDate);

        if (selectedDate > currentDate) {
            result = true;
        }
        return result;
    },

    formatDate: function(sDate) {
        var parts = sDate.split(" ");
        var datePart = parts[0].split("-");
        var timePart = parts[1];

        // Rearrange date components to 'yyyy-dd-MM'
        var formattedDate = datePart[2] + "-" + datePart[1] + "-" + datePart[0] + " " + timePart;
        return formattedDate;
        //var selectedDate = new GlideDateTime(formattedDate.toString());
    },