com.smartclient.debug.public.sc.client.language.Date.js Maven / Gradle / Ivy
Show all versions of smartgwt Show documentation
/*
* Isomorphic SmartClient
* Version SC_SNAPSHOT-2011-08-08 (2011-08-08)
* Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
* "SmartClient" is a trademark of Isomorphic Software, Inc.
*
* [email protected]
*
* http://smartclient.com/license
*/
//> @class DateUtil
// Static singleton class containing APIs for interacting with Dates.
// @visibility external
// @treeLocation Client Reference/System
// @visibility external
//<
isc.defineClass("DateUtil");
//> @class Date
//
// Extensions to the Date class, including added static methods on the Date object, and
// additional instance methods available on all date instances.
//
// @treeLocation Client Reference/System
// @visibility external
//<
//> @classMethod isc.timeStamp()
// Shorthand for new Date().getTime();
, this returns a timeStamp - a large number
// which is incremented by 1 every millisecond. Can be used to generate unique identifiers,
// or perform timing tasks.
//
// @visibility external
// @return (number) a large integer (actually the number of milliseconds since 1/1/1970)
//<
isc.addGlobal("timeStamp", function () {
return new Date().getTime()
});
// synonym
isc.addGlobal("timestamp", isc.timeStamp);
//>DEBUG
// This lets us label methods with a name within addMethods
Date.prototype.Class = "Date";
Date.Class = "Date";
// @classMethod Date.newInstance()
// Cover function for creating a date in the 'Isomorphic-style',
// eg: Date.newInstance(args)
// rather than new Date(args)
// @return (Date) Date object
// @deprecated As of SmartClient 5.5, use +link{Date.create}.
//<
newInstance : function (arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
return new Date(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
},
//> @classMethod Date.create()
// Create a new Date
object - synonym for new Date(arguments)
// @return (Date) Date object
// @visibility external
//<
create : function (arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
// handle being passed a subset of parameters
// Note that passing undefined into new Date() results in an invalid date where
// getTime() returns NaN
var undef;
if (arg1 === undef) return new Date();
if (arg2 === undef) return new Date(arg1);
if (arg3 === undef) arg3 = 0;
if (arg4 === undef) arg4 = 0;
if (arg5 === undef) arg5 = 0;
if (arg6 === undef) arg6 = 0;
if (arg7 === undef) arg7 = 0;
return new Date(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
},
//> @classMethod Date.createLogicalDate()
// Create a new Date to represent a logical date value (rather than a specific datetime value),
// typically for display in a +link{DataSourceField.type,date type field}. The generated
// Date value will have year, month and date set to the specified values
// (in browser native local time).
// @param year (integer) full year
// @param month (integer) month (zero based, so 0 is January)
// @param date (integer) date within the month
// @return (Date) new javascript Date object representing the Date in question
// @visibility external
//<
// For logical dates, the only requirement for the "time" component value is that the
// date shows up correctly in local time.
createLogicalDate : function (year, month, date, suppressConversion) {
var d = new Date();
d.setHours(12);
d.setMinutes(0);
d.setSeconds(0);
d.setMilliseconds(0);
if (date != null) d.setDate(1);
if (year != null) d.setYear(year);
if (month != null) d.setMonth(month);
if (date != null) d.setDate(date);
if (suppressConversion) {
// If the 'suppressConversion' flag was passed, we will want to return null to indicate
// we were passed an invalid date if the values passed in had to be converted
// (For example a month of 13 effecting the year, etc)
var isValid = (d.getFullYear() == year &&
d.getMonth() == month &&
d.getDate() == date );
if (!isValid) return null;
}
d.logicalDate = true;
return d;
},
//> @classMethod Date.createLogicalTime()
// Create a new Date object to represent a logical time value (rather than a specific datetime
// value), typically for display in a +link{DataSourceField.type,time type field}. The generated
// Date value will have year, month and date set to the epoch date (Jan 1 1970), and time
// elements set to the supplied hour, minute and second (in browser native local time).
// @param hour (integer) hour (0-23)
// @param minute (integer) minute (0-59)
// @param second (integer) second (0-59)
// @return (Date) new Javascript Date object representing the time in question
// @visibility external
//<
// This is a synonym for Time.createLogicalTime();
createLogicalTime : function (hour, minute, second, millisecond) {
return isc.Time.createLogicalTime(hour,minute,second,millisecond);
},
createDatetime : function (year, month, date, hours, minutes, seconds, milliseconds, suppressConversion) {
var hasHours = hours != null,
hasMinutes = minutes != null,
hasSeconds = seconds != null;
// Handle being passed strings
if (isc.isA.String(hours)) hours = parseInt(hours || 12, 10);
if (isc.isA.String(minutes)) minutes = parseInt(minutes || 0, 10);
if (isc.isA.String(seconds)) seconds = parseInt(seconds || 0, 10);
var newDate;
if (!isc.Time._customTimezone) {
newDate = new Date(year, month, date);
if (hasHours) {
if (hours != null) newDate.setHours(hours);
if (minutes != null) newDate.setMinutes(minutes);
if (seconds != null) newDate.setSeconds(seconds);
if (milliseconds != null) newDate.setMilliseconds(milliseconds);
}
if (!suppressConversion) return newDate;
// If the 'suppressConversion' flag was passed, we will want to return null to indicate
// we were passed an invalid date if the values passed in had to be converted
// (For example a month of 13 effecting the year, etc)
var isValid = (newDate.getFullYear() == year &&
newDate.getMonth() == month &&
newDate.getDate() == date &&
(!hasHours || newDate.getHours() == hours) &&
(!hasMinutes || newDate.getMinutes() == minutes) &&
(!hasSeconds || newDate.getSeconds() == seconds)
);
return (isValid ? newDate : null);
} else {
// We need a date where the UTCTime is set such that when we apply our
// custom timezone offset we get back the local time.
// Do this by creating a new date with UTC time matching this custom display time
// and then shifting that date by the inverse of our display timezone offset.
if (hours == null) hours = 0;
if (minutes == null) minutes = 0;
if (seconds == null) seconds = 0;
if (milliseconds == null) milliseconds = 0;
newDate = new Date(Date.UTC(year, month, date, hours, minutes, seconds, milliseconds));
// If the 'suppressConversion' flag was passed, we will want to return null to indicate
// we were passed an invalid date if the values passed in had to be converted
// (For example a month of 13 effecting the year, etc)
// Easiest to check this against the date before we apply the offset to correct for
// our timezone
if (suppressConversion) {
var isValid = (newDate.getUTCFullYear() == year &&
newDate.getUTCMonth() == month &&
newDate.getUTCDate() == date &&
(!hasHours || newDate.getUTCHours() ==hours) &&
(!hasMinutes || newDate.getUTCMinutes() == minutes) &&
(!hasSeconds || newDate.getUTCSeconds() == seconds)
);
if (!isValid) newDate = null;
}
if (newDate != null) {
newDate._applyTimezoneOffset(
-isc.Time.getUTCHoursDisplayOffset(newDate),
-isc.Time.getUTCMinutesDisplayOffset(newDate)
);
}
return newDate;
}
},
//> @classMethod Date.compareDates()
// Compare two dates; returns 0 if equal, -1 if the first date is greater (later), or 1 if
// the second date is greater.
// @param date1 (date) first date to compare
// @param date2 (date) second date to compare
// @return (number) 0 if equal, -1 if first date > second date, 1 if second date > first date
// @visibility external
//<
compareDates : function (a, b) {
var aval = (a != null ? a.getTime() : 0),
bval = (b != null ? b.getTime() : 0);
return aval > bval ? -1 : (bval > aval ? 1 : 0);
},
//> @classMethod Date.compareLogicalDates()
// Compare two dates, normalizing out the time elements so that only the date elements are
// considered; returns 0 if equal, -1 if the first date is greater (later), or 1 if
// the second date is greater.
// @param date1 (date) first date to compare
// @param date2 (date) second date to compare
// @return (number) 0 if equal, -1 if first date > second date, 1 if second date >
// first date. Returns false if either argument is not a date
// @visibility external
//<
compareLogicalDates : function (a, b) {
if (a == b) return 0; // same date instance
if (!isc.isA.Date(a) || !isc.isA.Date(b)) return false; // bad arguments, so return false
var aYear = a.getFullYear(),
aMonth = a.getMonth(),
aDay = a.getDate(),
bYear = b.getFullYear(),
bMonth = b.getMonth(),
bDay = b.getDate();
var aval = aYear * 10000 + aMonth * 100 + aDay,
bval = bYear * 10000 + bMonth * 100 + bDay;
return aval > bval ? -1 : (bval > aval ? 1 : 0);
},
//> @type DateInputFormat
// 3 character string containing the "M"
, "D"
and "Y"
// characters to indicate the format of strings being parsed into Date instances via
// Date.parseInput()
.
//
// As an example - an input format of "MDY" would parse "01/02/1999" to Jan 2nd 1999
//
// Note: In addition to these standard formats, a custom date string parser function may be
// passed directly to +link{Date.setInputFormat()} or passed into +link{Date.parseInput()} as
// the inputFormat parameter.
// @visibility external
//<
//> @classMethod Date.setInputFormat()
// Sets up the default system-wide input format for strings being parsed into dates via
// Date.parseInput()
. This will effect how SmartClient components showing editable
// date or datetime fields parse user-entered values into live Date objects.
//
// The input format can be specified as a +link{type:DateInputFormat} - a 3 character string like
// "MDY"
indicating the order of the Month, Day and Year components of date strings.
//
// As an example - an input format of "MDY" would parse "01/02/1999" to Jan 2nd 1999
// This standard parsing logic will also handle date-time strings such as "01/02/1999 08:45", or
// "01/02/1999 16:21:05".
//
// Notes:
//
// - If the inputFormat is not explicitly set,the system automatically determines
// the standard input format will be based on the specified +link{Date.shortDisplayFormat}
// wherever possible.
// For example if the short display format has been set to "toEuropeanShortDate" the input
// format will default to "DMY".
// - The default date parsing functionality built into SmartClient will handle dates presented
// with any separator string, and can 1 or 2 digit day and month values and 2 or 4 digit year
// values. This means that in many cases custom date display formats can be parsed back to
// Date values without the need for a custom parser function. However if more sophisticated
// parsing logic is required, a function may be passed into this method. In this case the
// parser function should be able to handle parsing date and datetime values formatted
// via +link{Date.toShortDate()} and +link{Date.toShortDateTime()}.
// - Date parsing and formatting logic may be overridden at the component level by setting
// properties directly on the component or field in question.
//
// @param format (DateInputFormat | function) Default format for strings to be parsed into Dates.
// If this method is passed a function, it is expected to take a single parameter
// (the formatted date string), and return the appropriate Javascript Date object (or null if
// appropriate).
// @see Date.parseInput()
// @example dateFormat
// @example customDateFormat
// @visibility external
//<
setInputFormat : function (format) {
this._inputFormat = format;
},
//> @classMethod Date.getInputFormat()
// Retrieves the default format for strings being parsed into dates via
// Date.parseInput()
// @see Date.setInputFormat()
// @visibility external
//<
getInputFormat : function () {
if (this._inputFormat != null) return this._inputFormat;
return this.mapDisplayFormatToInputFormat("toShortDate");
},
// Given a display format return the associated input format
_inputFormatMap:{
toUSShortDate:"MDY",
toUSShortDateTime:"MDY",
toUSShortDatetime:"MDY",
toEuropeanShortDate:"DMY",
toEuropeanShortDateTime:"DMY",
toEuropeanShortDatetime:"DMY",
toJapanShortDate:"YMD",
toJapanShortDateTime:"YMD",
toJapanShortDatetime:"YMD"
},
mapDisplayFormatToInputFormat : function (displayFormat) {
if (displayFormat == "toShortDate") {
displayFormat = Date.prototype._shortFormat;
} else if (displayFormat == "toNormalDate") {
displayFormat = Date.prototype.formatter;
}
if (isc.isA.Function(displayFormat)) {
isc.Log.logInfo("Unable to determine input format associated with display format " +
"function - returning default input format", "Date");
return this._inputFormat || "MDY";
}
var inputFormat = this._inputFormatMap[displayFormat];
// Note: isA.String check is necessary - all objects have toString / toLocaleString
// present on them and we definitely don't want to return those native object formatters
// as what will become a dateString parsing function!
if (inputFormat != null && isc.isA.String(inputFormat)) return inputFormat;
// a couple of special cases where we actually return functions.
if (displayFormat == "toSerializeableDate") return this.parseSchemaDate;
// Otherwise you're on your own - assume you've set up input foramt, or overridden this method
isc.Log.logInfo("Unable to determine input format associated with display format " +
displayFormat + " - returning default input format", "Date");
return this._inputFormat || "MDY";
},
//> @classMethod Date.parseInput()
// Parse a date passed in as a string, returning the appropriate date object.
// @group dateFormatting
//
// @param dateString (string) date value as a string
// @param [format] (DateInputFormat) Format of the date string being passed.
// If not passed, the default date input format as set up
// via setInputFormat() will be used.
// @param [centuryThreshold] (number) For date formats that support a 2 digit
// year, if parsed year is 2 digits and less than this
// number, assume year to be 20xx rather than 19xx
// @param [suppressConversion] (boolean)
// If the string passed in was not a valid date, in some cases we can convert to a
// valid date (for example incrementing the year if the month is greater than 12).
// This optional parameter will suppress such conversions - anything that doesn't
// parse directly to a valid date will simply return null.
// @return (Date) date value, or null if the string could not be parsed to a valid date.
// @visibility external
//<
// Note: undocumented isDatetime parameter. Are we creating a logical "date" value or a standard
// datetime type value where the time component is important? If ommitted assume datetime.
// Implementation-wise, if isDatetime is explicitly false, we will use the system local timezone
// rather than any timezone specified via Time.setDisplayTimezone().
parseInput : function (dateString, format, centuryThreshold, suppressConversion,
isDatetime)
{
var logicalDate = (isDatetime == false);
if (isc.isA.Date(dateString)) return dateString;
if (!isc.isA.String(dateString) || isc.isAn.emptyString(dateString)) {
return null;
}
// Default to the standard input format
if (format == null) format = this.getInputFormat();
// If the format passed in is the name of a function on the Date class, or an
// explicit function, assume its a parser and call it directly
if (isc.isA.Function(Date[format])) format = Date[format];
if (isc.isA.Function(format)) {
return format(dateString, centuryThreshold, suppressConversion);
}
// use the helper method _splitDateString() to get an array of values back
// (representing year / month / day, etc.)
// If null is returned, this was not a valid date - just return null.
// Otherwise make the month zero-based, by reducing by one, and pass construct a new date
// from the values returned.
var array = this._splitDateString(dateString, format);
if (array != null) {
var year = array[0];
if (year && year.length <= 2) {
year = parseInt(year, 10);
if (year < centuryThreshold) year += 2000;
else year += 1900
array[0] = year;
}
if (logicalDate) {
return Date.createLogicalDate(array[0], array[1], array[2], suppressConversion);
} else {
return Date.createDatetime(array[0], array[1], array[2],
array[3], array[4], array[5], null, suppressConversion);
}
} else {
return null;
}
},
// Parse a date or datetime value from a dataset or specified in code.
// NB: unlike parseInput, this method should not change behavior in different locales, or dates
// coming over the wire or specified in code will suddenly break!
//
// For Datetime, XML Schema uses "2005-08-01T21:35:48.350Z", see
// http://www.w3.org/TR/xmlschema-2/#dateTime
// SmartClient Server parses "yyyy-mm-dd" format
parseSchemaDate : function (value) {
if (isc.isA.Date(value)) return value;
if (!isc.isA.String(value)) value = (value.toString ? value.toString() : value + "");
// Notes on regex:
// - result[4] is the optional timestamp including the T and colon separators
// - result[8] would be the optional milliseconds including the ".", whereas
// result[9] is just the numeric part
// results[10] is the timezone - either "Z" (zulu time or GMT) or +/- HH:MM
var result = value.match(/(\d{4})[\/-](\d{2})[\/-](\d{2})([T ](\d{2}):(\d{2}):(\d{2}))?(\.(\d+))?([+-]\d{2}:\d{2}|Z)?/);
//isc.Log.logWarn("isDate: '" + value + "', regex match: " + result);
if (result == null) return null;
var dateValue;
// NOTE: pass only the relevant arguments as Moz does not like being passed nulls
if (!result[4]) { // no time
dateValue = Date.createLogicalDate(result[1], result[2] - 1, result[3]);
} else if (!result[9]) { // no ms
dateValue = new Date(Date.UTC(result[1], result[2] - 1, result[3],
result[5], result[6], result[7]));
} else {
var ms = result[9];
// XML Schema says any number of fractional digits can be specified. new Date() is
// expecting a whole number of milliseconds (and further precision would be ignored).
// Multiply by a power of ten based on the number of digits provided, such that ".9"
// becomes 900 and ".98367" becomes 984.
if (ms.length != 3) {
var multiplier = Math.pow(10,3-ms.length);
ms = Math.round(parseInt(ms,10) * multiplier);
}
//isc.Log.logWarn("ms is: " + ms);
dateValue = new Date(Date.UTC(result[1], result[2] - 1, result[3],
result[5], result[6], result[7], ms));
}
// Handle timezone offset from GMT
if (result[10] && result[10].toLowerCase() != "z") {
var HM = result[10].split(":"),
H = HM[0],
negative = H && H.startsWith("-"),
M = HM[1];
H = parseInt(H, 10);
M = parseInt(M, 10);
var dateTime = dateValue.getTime();
// Note no need to account for negative on hours since the "+" or "-" prefix was picked up
// in parseInt
if (isc.isA.Number(H)) dateTime -= (3600000 * H);
if (isc.isA.Number(M)) dateTime -= (60000 * M * (negative ? -1 : 1));
dateValue.setTime(dateTime);
}
return dateValue
},
//>!BackCompat 2005.11.3
// parseDate() was old name for parseInput
parseDate : function (dateString, format, centuryThreshold, suppressConversion) {
return this.parseInput(dateString, format, centuryThreshold, suppressConversion);
},
// For completeness also support parseDatetime()
parseDateTime : function (dateString, format, centuryThreshold, suppressConversion) {
return this.parseDatetime(dateString,format,centuryThreshold,suppressConversion);
},
parseDatetime : function (dateString, format, centuryThreshold, suppressConversion) {
return this.parseInput(dateString, format, centuryThreshold, suppressConversion);
},
//Safari12
if (isc.Browser.isSafari && isc.Browser.safariVersion <= 312) {
var splitDate = this._splitDateViaSubstring(string, monthIndex, dayIndex, yearIndex);
year = splitDate[0];
month = splitDate[1];
day = splitDate[2];
hour = splitDate[3];
minute = splitDate[4];
second = splitDate[5];
// For browsers that support RegExp properly, use regexp pattern matching to get the result
// (This has the advantage that we can deal with dates of the form 1/1/1999, and attempt to
// convert MM/YY/DD -- though we're relying on the native browser handling for the
// Date constructor being passed a 2 digit year)
} else {
// Safari12
}
// @type DateDisplayFormat
// Valid display formats for dates. These strings are the names of formatters which can be
// passed to Date.setNormalDisplayFormat()
or Date.setShortDisplayFormat()
// and will be subsequently used as default long or short formatters for date objects by
// SmartClient components.
// Default set of valid display formats is as follows:
//
// @value toString
// Default native browser 'toString()' implementation. May vary by browser.
// Example: Fri Nov 04 2005 11:03:00 GMT-0800 (Pacific Standard Time)
// @value toLocaleString
// Default native browser 'toLocaleString()' implementation. May vary by browser.
// Example: Friday, November 04, 2005 11:03:00 AM
// @value toUSShortDate Short date in format MM/DD/YYYY.
// Example: 11/4/2005
// @value toUSShortDatetime Short date with time in format MM/DD/YYYY HH:MM
// Example: 11/4/2005 11:03
// @value toEuropeanShortDate Short date in format DD/MM/YYYY.
// Example: 4/11/2005
// @value toEuropeanShortDatetime Short date with time in format DD/MM/YYYY HH:MM
// Example: 4/11/2005 11:03
// @value toJapanShortDate Short date in format YYYY/MM/DD.
// Example: 2005/11/4
// @value toJapanShortDatetime Short date with time in format YYYY/MM/DD HH:MM
// Example: 2005/11/4 11:03
// @value toSerializeableDate Date in the format YYYY-MM-DD HH:MM:SS
// Example: 2005-11-04 11:09:15
// @value toDateStamp Date in the format <YYYYMMDD>T<HHMMSS>Z
// Example: 20051104T111001Z
//
//
// Note: In addition to these standard formats, custom formatting can be set by passing
// a function directly to +link{Date.setNormalDisplayFormat()} et al. This
// function will then be executed whenever the appropriate formatter method is called [eg
// +link{date.toNormalDate()}], in the scope of the date instance in question.
//
// @visibility external
//<
//> @classMethod Date.setNormalDisplayFormat()
// Set the default formatter for date objects to the method name passed in. After calling this
// method, subsequent calls to +link{Date.toNormalDate()} will return a string formatted
// according to this format specification. Note: this will be the standard long date format used
// by SmartClient components.
// The format
parameter may be either a +link{DateDisplayFormat} string, or
// a function. If passed a function, this function will be executed in the scope of the Date
// and should return the formatted string.
// Initial default normalDisplayFormat is "toLocaleString"
// @group dateFormatting
// @param format (DateDisplayFormat | function) new formatter
// @visibility external
//<
setNormalDisplayFormat : function (format) {
// if a valid formatter was passed in, set our .formatter property
if (isc.isA.Function(Date.prototype[format]) || isc.isA.Function(format)) {
Date.prototype.formatter = format;
}
},
//> @classMethod Date.setShortDisplayFormat()
// Set the default short format for dates. After calling this method, subsequent calls to
// +link{Date.toShortDate()} will return a string formatted according to this format
// specification. Note that this will be the standard short date format used by
// SmartClient components.
//
// The format
parameter may be either a +link{DateDisplayFormat} string, or
// a function. If passed a function, this function will be executed in the scope of the Date
// and should return the formatted string.
//
// Initial default shortDateFormat is "toUSShortDate"
. This property
// is commonly modified for localization of applications. See
// +externalLink{http://en.wikipedia.org/wiki/Date_format_by_country}
// for a useful overview of standard date formats per country.
//
// @group dateFormatting
// @param format (DateDisplayFormat | function) new formatter
// @example dateFormat
// @example customDateFormat
// @visibility external
//<
setShortDisplayFormat : function (format) {
if (isc.isA.Function(Date.prototype[format]) || isc.isA.Function(format)) {
Date.prototype._shortFormat = format;
}
},
//> @classMethod Date.setDefaultDateSeparator
// Sets a new default separator that will be used when formatting dates. By default, this
// is a forward slash character: "/"
// @group dateFormatting
// @param separator (string) separator to use in dates
// @visibility external
//<
setDefaultDateSeparator : function (separator) {
Date.prototype._shortDateTemplate = [,,,,separator,,,,,separator,,,,null];
Date.prototype._separator = separator;
},
//> @classMethod Date.getDefaultDateSeparator
// gets the default date separator string
// @group dateFormatting
// @return(string) the default date separator
// @visibility external
//<
getDefaultDateSeparator : function (separator) {
if (Date.prototype._separator) return Date.prototype._separator;
else return "/";
},
//> @classMethod Date.setShortDatetimeDisplayFormat()
// Set the default short format for datetime values. After calling this method, subsequent calls to
// +link{Date.toShortDateTime()} will return a string formatted according to this format
// specification. Note that this will be the standard datetime format used by
// SmartClient components.
//
// The format
parameter may be either a +link{DateDisplayFormat} string, or
// a function. If passed a function, this function will be executed in the scope of the Date
// and should return the formatted string.
//
// Initial default format is "toUSShortDatetime"
. See
// +externalLink{http://en.wikipedia.org/wiki/Date_format_by_country}
// for a useful overview of standard date formats per country.
//
// @group dateFormatting
// @param format (DateDisplayFormat | function) new formatter
// @example dateFormat
// @example customDateFormat
// @visibility external
//<
setShortDatetimeDisplayFormat : function (format) {
if (isc.isA.Function(Date.prototype[format]) || isc.isA.Function(format)) {
Date.prototype._shortDatetimeFormat = format;
}
},
//>!BackCompat 2005.11.3
// -- Older depracated synonym of setNormalDisplayFormat
//> @classMethod Date.setFormatter()
// Set the formatter for all date objects to the method name passed in. After this call
// all theDate.toNormalDate()
calls will fall through to this formatter function to
// return the date as a string.
// @group dateFormatting
// @param functionName (string) name of a date formatter method on this Date
// @visibility internal
//<
setFormatter : function (formatter) {
Date.setNormalDisplayFormat(formatter);
},
// @classMethod Date.setLocaleStringFormatter() (A)
// Set default the +link{Date.iscToLocaleString()} formatter for all date instances.
//
// @param format (DateDisplayFormat | function) new formatter for iscToLocaleString()
// @group dateFormatting
// @visibility internal
//<
setLocaleStringFormatter : function (functionName) {
if (isc.isA.Function(Date.prototype[functionName]) || isc.isA.Function(functionName))
Date.prototype.localeStringFormatter = functionName;
},
// Localizing dayName / monthNames
//> @classAttr Date.shortDayNames (Array : null : IRWA)
// This property may be set to an array of names of days of the week.
// For example:
//
// ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
//
// The appropriate day name will then be returned from +link{date.getShortDayName()}, and may
// be used whenever SmartClient components display day-names (for example in the
// +link{class:DateItem, DateItem class}).
// Note: For US based applications the first item in the array should be the name for Sunday,
// then Monday, Tuesday, etc. For browsers with different locales this may vary.
// To determine the first day for some locale, you can run the following code:
//
// alert(new Date(2000, 0, 2).getDay());
//
// You should see an alert with a number between zero and 6. This represents the numerical
// 'day' value for Sunday for your browser's locale, since Jan 2nd 2000 was a Sunday.
// Therefore if this code alerted the number 6, Sunday should appear last in your list
// of day-names, and Monday first.
// @group i18nMessages
// @visibility external
//<
//> @classAttr Date.shortMonthNames (Array : null : IRWA)
// This property may be set to an array of names of months.
// For example:
//
// ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
//
// The appropriate month name will then be returned from +link{date.getShortMonthName()},
// and may be used whenever SmartClient components display month-names (for example in the
// +link{class:DateItem, DateItem class}).
// @group i18nMessages
// @visibility external
//<
//> @method date.getShortMonthNames() (A)
// Return an array of the short names of each month, suitable for us in a selection list, etc.
// If +link{Date.shortMonthNames} is specified, this list will be used. Otherwise the value
// will be derived from the native browser date formatters.
// @group dateFormatting
//
// @param length (number) Number of characters for each day (Defaults to 3, can't be
// longer than 3)
// @return (string[]) array of short month names
//<
getShortMonthNames : function (length) {
length = length || 3;
var rawNames = Date.shortMonthNames;
if (rawNames == null) rawNames = Date._derivedShortMonthNames;
if (rawNames == null) {
var list = Date._derivedShortMonthNames = [];
for (var i = 0; i < 12; i++) {
// Changed the day in this synthetic date to 2 in order to derive the
// correct month in timezones that are ahead of GMT (if you convert
// midnight on the first of a month to UTC in such timezones, you
// get the previous month...)
var date = Date.createLogicalDate(2000,i,2);
list[i] = date.deriveShortMonthName();
}
rawNames = Date._derivedShortMonthNames;
}
var names = [];
for (var i =0; i< 12; i++) {
names[i] = rawNames[i].substring(0,length);
}
return names;
},
//> @method date.getShortDayNames() (A)
// Return an array of the short names of each day, suitable for us in a selection list, etc.
// Day names will be picked up from +link{Date.shortDayNames} if specified - otherwise derived
// from the native browser date string.
// @group dateFormatting
//
// @param length (number) Number of characters for each day (Defaults to 3, can't be
// longer than 3)
// @return (string[]) array of short day names
//<
getShortDayNames : function (length) {
length = length || 3;
var rawNames = Date.shortDayNames;
if (rawNames == null) rawNames = Date._derivedShortDayNames;
if (rawNames == null) {
Date._derivedShortDayNames = [];
var dateObj = new Date();
dateObj.setDate(1);
if (dateObj.getDay() > 0) dateObj.setDate(dateObj.getDate() + (7-dateObj.getDay()));
var startDate = dateObj.getDate();
for (var i = 0; i < 7; i++) {
dateObj.setDate(startDate + i);
Date._derivedShortDayNames[i] = dateObj.deriveShortDayName();
}
rawNames = Date._derivedShortDayNames;
}
var names = [];
for (var i = 0; i < 7; i++) {
names[i] = rawNames[i].substring(0,length);
}
return names;
},
//> @classAttr Date.weekendDays (Array of int : [0, 6] : IR)
// Days that are considered "weekend" days. Values should be the integers returned by the
// JavaScript built-in Date.getDay(), eg, 0 is Sunday and 6 is Saturday. Override to
// accommodate different workweeks such as Saudi Arabia (Saturday -> Wednesday) or Israel
// (Sunday -> Thursday).
//
// @visibility external
//<
//> @classMethod Date.getWeekendDays()
// Return an array of days that are considered "weekend" days. Values will be the integers
// returned by the JavaScript built-in Date.getDay(), eg, 0 is Sunday and 6 is Saturday.
// Override +link{date.weekendDays} to accommodate different workweeks such as Saudi Arabia
// (Saturday -> Wednesday) or Israel (Sunday -> Thursday).
// @group dateFormatting
//
// @return (int[]) array of weekend days
//<
getWeekendDays : function () {
var daysArr = Date.weekendDays;
if (daysArr == null) daysArr = Date._derivedWeekendDays;
if (daysArr == null) {
daysArr = Date._derivedWeekendDays = [0, 6];
}
return daysArr;
},
getFormattedDateRangeString : function (fromDate, toDate) {
if (fromDate != null && !isc.isA.Date(fromDate)) {
fromDate = null;
}
if (toDate != null && !isc.isA.Date(toDate)) {
toDate = null;
}
var fromMonth = fromDate ? fromDate.getMonth() : null,
fromMonthName = fromDate ? fromDate.getShortMonthName() : null,
fromYear = fromDate ? fromDate.getFullYear() : null,
fromDay = fromDate ? fromDate.getDate() : null,
toMonth = toDate ? toDate.getMonth() : null,
toMonthName = toDate ? toDate.getShortMonthName() : null,
toYear = toDate ? toDate.getFullYear() : null,
toDay = toDate ? toDate.getDate() : null,
result = ""
;
if (fromDate && toDate) {
if (fromYear == toYear) {
// dates are in the same year - check the months
if (fromMonth == toMonth) {
// dates are in the same month - check the day-numbers
if (fromDay == toDay) {
// dates are the same - use just the one date
result = fromMonthName + " " + fromDate.getDate() + ", " + fromYear;
} else {
// day-numbers are different, use "month start - end, year"
result = fromMonthName + " " + fromDate.getDate() + " - " +
toDate.getDate() + ", " + fromYear;
}
} else {
// dates are in different months, use "month start - month end, year"
result = fromMonthName + " " + fromDate.getDate() + " - " +
toMonthName + " " + toDate.getDate() + ", " + fromYear;
}
} else {
// different years - use "month start year - month end year"
result = fromMonthName + " " + fromDate.getDate() + ", " + fromYear + " - " +
toMonthName + " " + toDate.getDate() + ", " + toYear;
}
} else if (fromDate) {
// only a fromDate provided use "month start - end, year"
result = fromMonthName + " " + fromDate.getDate() + ", " + fromYear;
} else if (toDate) {
// only a toDate provided use "month start - end, year"
result = toMonthName + " " + toDate.getDate() + ", " + toYear;
}
return result;
}
});
//
// add methods to the Date.prototype for additional formatting options
//
isc.addMethods(Date.prototype, {
//> @method date.duplicate() (A)
// Copy the value of this date into a new Date() object for independent manipulation
// @visibility external
//<
duplicate : function () {
var newDate = new Date();
newDate.setTime(this.getTime());
newDate.logicalDate = this.logicalDate;
newDate.logicalTime = this.logicalTime;
return newDate;
},
//> @method date.clearTimeFields() (A)
// Zero-out the time fields for a date.
// @group dateFormatting
//<
clearTimeFields : function () {
this.setHours(0);
this.setMinutes(0);
this.setSeconds(0);
this.setMilliseconds(0);
return this;
},
// Determine the day name from this.toString()
deriveShortDayName : function (length) {
var string = this.toString();
if (length == null || length <=0 || length > 3) length = 3;
return string.substring(0,length);
},
//> @method date.getShortDayName()
// Return the abbreviated (up to 3 chars) day of week name for this date (Mon, Tue, etc).
// To modify the value returned by this method, set +link{Date.shortDayNames}
//
// @group dateFormatting
// @param length (number) Number of characters to return (Defaults to 3, can't be
// longer than 3)
// @return (string) Abbreviated day name
// @visibility external
//<
getShortDayName : function () {
return Date.getShortDayNames()[this.getDay()];
},
// deriveShortMonthNames() - figure out the names of months from the native browser
// date formatting methods.
deriveShortMonthName : function (length) {
// Use this.toUTCString - to work around Opera's different toString format
var string = this.toUTCString();
var start = 8; // The correct start point if we have a 2-digit day portion in the date
if (length == null || length < 0 || length > 3) length = 3;
if (string.substring(6, 7) == ' ') { // we have a single-digit day number - only IE
// does this, the others put a leading 0 in
start = 7;
}
return string.substring(start, (start+length));
},
//> @method date.getShortMonthName()
// Return the abbreviated (up to 3 chars) name of the month for this date (Jan, Feb, etc)
// To modify the value returned by this method, set +link{Date.shortMonthNames}
// @group dateFormatting
// @param length (number) Number of characters to return (Defaults to 3, can't be
// longer than 3)
// @return (string) Abbreviated month name (3 character string)
// @visibility external
//<
getShortMonthName : function () {
return Date.getShortMonthNames()[this.getMonth()];
},
//> @method date.getShortYear()
// Return a 2 digit year for this date.
// @group dateFormatting
// @return (string) year number, padded to 2 characters
// @visibility external
//<
getShortYear : function () {
var year = this.getFullYear();
return (year % 100).stringify(2);
},
//> @method date.getWeek()
// Returns an integer containing the week number
// @group dateFormatting
// @return (integer) week number, starting with 1
// @visibility external
//<
getWeek : function() {
var yearstart = new Date(this.getFullYear(),0,1);
return Math.ceil((((this - yearstart) / 86400000) + yearstart.getDay())/7);
},
//
// Date Formatters (toNormalDate(), toShortDate(), etc.)
//
// Date formatters are applied to date objects to convert them into strings for display.
// Dates are intended to be localizable.
// For localization, a developer would typically set either the shortDateFormatter or
// normalDateFormatter, as well as the inputDateFormat, and then call
// "toNormalDate()" / "toShortDate()" and "parseInput()" as normal.
//> @method date.toDateStamp()
// Return this date in the format (UTC timezone):
// YYYYMMDDTHHMMSS[Z]
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
//<
toDateStamp : function () {
return this.getUTCFullYear()
+ (this.getUTCMonth()+1).stringify()
+ this.getUTCDate().stringify()
+ "T"
+ this.getUTCHours().stringify()
+ this.getUTCMinutes().stringify()
+ this.getUTCSeconds().stringify()
+ "Z";
},
//> @method date.toNormalDate()
// Returns the date as a formatted string using the format set up via the
// setNormalDisplayFormat()
method. Note that the default formatter for this
// method is "toLocaleString"
.
// @group dateFormatting
// @param format (DateDisplayFormat) Optional Format for the date returned
// @return (string) formatted date string
// @visibility external
//<
// This method is used by our data components such as ListGrid to display long format dates.
// @param useCustomTimezone (boolean) If true, format the date using the timezone
// setDefaultDisplayTimezone() rather than the native browser locale.
// Defaults to true.
// Has no effect if no custom timezone applied
// * Note that the native browser formatters including toLocaleString won't respect the
// developer specified timezone of course. We could workaround this (create a new date, shift
// by offset between specified timezone and native timezone, and call the native formatter on that)
// but we currently don't.
toNormalDate : function (formatter, useCustomTimezone) {
if (!formatter) formatter = this.formatter;
// fire the formatter in the scope of this date, so date is available as 'this'
if (isc.isA.Function(formatter)) {
return formatter.apply(this, [useCustomTimezone])
} else if (this[formatter]) {
return this[formatter](useCustomTimezone);
}
},
//> @method date.toShortDate()
// Returns the date as a formatted string using the format set up via the
// setShortDisplayFormat()
method.
// @group dateFormatting
// @param format (DateDisplayFormat) Optional Format for the date returned
// @param [useCustomTimezone] (boolean) If a custom timezone has been set via
// Time.setDefaultDisplayTimezone(), by default date formatters will respect this timezone.
// to suppress this behavior, this parameter should be set to false.
// @return (string) formatted date string
// @visibility external
//<
toShortDate : function (formatter, useCustomTimezone) {
if (!formatter) formatter = this._shortFormat;
if (isc.isA.Function(formatter)) return formatter.apply(this, [useCustomTimezone]);
else if (isc.isA.Function(this[formatter])) return this[formatter](useCustomTimezone);
isc.logWarn("Date.toShortDate() specified formatter not understood:" + formatter);
return this.toUSShortDate();
},
//> @method date.toShortDateTime()
// Returns the datetime as a formatted string using the format set up via the
// setShortDatetimeDisplayFormat()
method.
// @group dateFormatting
// @param format (DateDisplayFormat) Optional Format for the date returned
// @param [useCustomTimezone] (boolean) If a custom timezone has been set via
// Time.setDefaultDisplayTimezone(), by default date formatters will respect this timezone.
// to suppress this behavior, this parameter should be set to false.
// @return (string) formatted date string
// @visibility external
//<
toShortDateTime : function (formatter, useCustomTimezone) {
return this.toShortDatetime(formatter,useCustomTimezone);
},
toShortDatetime : function (formatter, useCustomTimezone) {
if (!formatter) formatter = this._shortDatetimeFormat;
return this.toShortDate(formatter, useCustomTimezone);
},
//> @method date.setDefaultDateSeparator
// Sets a new default separator that will be used when formatting dates. By default, this
// is a forward slash character: "/"
// @group dateFormatting
// @param separator (string) separator to use in dates
// @visibility external
//<
setDefaultDateSeparator : function (separator) {
this._shortDateTemplate = [,,,,separator,,,,,separator,,,,null];
this._separator = separator;
},
//> @method date.getDefaultDateSeparator
// gets the default date separator string
// @group dateFormatting
// @return(string) the default date separator
// @visibility external
//<
getDefaultDateSeperator : function (separator) {
if (this._separator) return this._separator;
else return "/";
},
_shortDateTemplate:[,,,,"/",,,,,"/",,,,null],
_$MDY:"MDY",
_$DMY:"DMY",
_$YMD:"YMD",
_$MDY:"MDY",
// _applyTimezoneOffset()
// shift a date by some arbitrary number of hours/minutes
// third parameter allows you to specify the starting date time [result of date.getTime()]
// to offset from
_applyTimezoneOffset : function (hourOffset, minuteOffset, dateTime) {
if (dateTime == null) dateTime = this.getTime();
if (isc.isA.Number(hourOffset)) dateTime += (3600000 * hourOffset);
if (isc.isA.Number(minuteOffset)) dateTime += (60000 * minuteOffset);
this.setTime(dateTime);
},
// _getTimezoneOffsetDate()
// This is a helper method - given a date with a certain UTC time, apply an explicit timezone
// offset to return a date where the UTC time is offset by the specified hours/minutes.
// We'll use this when formatting dates for display in arbitrary local times [so we can't just
// use the native browser local timezone methods like getHours()]
_getTimezoneOffsetDate : function (hourOffset, minuteOffset) {
var offsetDate = Date._timezoneOffsetDate;
if (offsetDate == null) offsetDate = Date._timezoneOffsetDate = new Date();
offsetDate._applyTimezoneOffset(hourOffset, minuteOffset, this.getTime());
return offsetDate;
},
// _toShortDate()
// Internal method to give us a shortDate - either DD/MM/YYYY, MM/DD/YYYY or YYYY/MM/DD.
// this will be passed "MDY" / "DYM" / etc. as a format parameter.
// useCustomTimezone parameter: use the hour and minute offset specified by
// Time.setDefaultDisplayTimezone() rather than the native browser local timezone
_$zero:"0",
_toShortDate : function (format, useCustomTimezone) {
// if this is a "logical date", don't use the developer-specified custom timezone when
// formatting. Typically handled by DBC's passing in the useCustomTimezone parameter, but
// we can also check for the logical date marker
if (useCustomTimezone == null) {
useCustomTimezone = !this.logicalDate;
}
var template = this._shortDateTemplate,
month,day,year;
// Browser native locale timezone
if (!useCustomTimezone || !isc.Time._customTimezone) {
month = this.getMonth()+1;
day = this.getDate();
year = this.getFullYear();
// Developer specified custom timezone
} else {
var offsetDate = this._getTimezoneOffsetDate(
isc.Time.getUTCHoursDisplayOffset(this),
isc.Time.getUTCMinutesDisplayOffset(this)
);
month = offsetDate.getUTCMonth() + 1;
day = offsetDate.getUTCDate();
year = offsetDate.getUTCFullYear();
}
var monthIndex, dayIndex, yearIndex;
if (format == this._$MDY) {
monthIndex = 0;
dayIndex = 5;
yearIndex = 10;
} else if (format == this._$DMY) {
dayIndex = 0;
monthIndex = 5;
yearIndex = 10;
} else if (format == this._$YMD) {
yearIndex = 0;
monthIndex = 5;
dayIndex = 10
// Unlikely - don't bother avoiding string alloc's for every one of these options
} else {
dayIndex = format.indexOf("D")*5;
yearIndex = format.indexOf("Y")*5;
monthIndex = format.indexOf("M")*5;
}
// Note: each number has 4 slots so it can accommodate a full year
// For month/day - if we need a leading zero, fill the first slot with it
// Use fillNumber to fill 3 slots even though we have a max of 2 digits to ensure
// the last slot gets cleared out if it was populated by a year already.
template[dayIndex] = day < 10 ? this._$zero : null
isc._fillNumber(template, day, dayIndex+1, 3);
template[monthIndex] = month < 10 ? this._$zero : null
isc._fillNumber(template, month, monthIndex+1, 3);
isc._fillNumber(template, year, yearIndex, 4);
return template.join(isc.emptyString);
},
//> @method date.toUSShortDate()
// Return this date in the format: MM/DD/YYYY
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
//<
toUSShortDate : function (useCustomTimezone) {
return this._toShortDate(this._$MDY, useCustomTimezone);
},
// _toShortTime - returns the time portion of the date in HH:MM
_timeTemplate:[null,null],
_toShortTime : function (useCustomTimezone) {
return isc.Time.toShortTime(this, "toShortPadded24HourTime");
},
//> @method date.toUSShortDateTime()
// Return this date in the format: MM/DD/YYYY HH:MM
//
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
//<
toUSShortDateTime : function (useCustomTimezone) {
return this.toUSShortDatetime(useCustomTimezone);
},
toUSShortDatetime : function (useCustomTimezone) {
return this.toUSShortDate(useCustomTimezone) + " " + this._toShortTime(useCustomTimezone);
},
//> @method date.toEuropeanShortDate()
// Return this date in the format: DD/MM/YYYY
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
//<
toEuropeanShortDate : function (useCustomTimezone) {
return this._toShortDate(this._$DMY, useCustomTimezone);
},
//> @method date.toEuropeanShortDateTime()
// Return this date in the format: DD/MM/YYYY HH:MM
.
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
//<
toEuropeanShortDateTime : function (useCustomTimezone) {
return this.toEuropeanShortDatetime();
},
toEuropeanShortDatetime : function (useCustomTimezone) {
return this.toEuropeanShortDate(useCustomTimezone) + " " +
this._toShortTime(useCustomTimezone);
},
//> @method date.toJapanShortDate()
// Return the date in this format: YYYY/MM/DD
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
//<
toJapanShortDate : function (useCustomTimezone) {
return this._toShortDate(this._$YMD, useCustomTimezone);
},
//> @method date.toJapanShortDateTime()
// Return this date in the format: YYYY/MM/DD HH:MM:SS
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
//<
toJapanShortDateTime : function (useCustomTimezone) {
return this.toJapanShortDatetime(useCustomTimezone);
},
toJapanShortDatetime : function (useCustomTimezone) {
return this.toJapanShortDate(useCustomTimezone) + " " + this._toShortTime(useCustomTimezone);
},
//> @method date._serialize() (A)
// Serialize this date to a string in a format that can be reinstantiated back into a date.
// $$DATE$$:YYYY-MM-DD
// @group dateFormatting
// @return (string) formatted date string
// @visibility internal
//<
_serialize : function () {
if (isc.Comm._legacyJSMode) {
// legacy mode: add $$DATE$$ that only our server-side JS parser understands
return isc.SB.concat('"' + this.toDBDate(), '"');
} else {
// any other caller: return code that would reconstruct the same Date in a JS
// interpreter
return isc.SB.concat("new Date(", this.getTime(), ")");
}
},
//> @groupDef dateFormatAndStorage
// The SmartClient system has the following features for handling Date and Time type values
// within DataSources and databound components.
//
// DataSources and databound components may define fields of type date
,
// time
, or datetime
.
//
//
"date" handling
//
// Fields of type +link{type:FieldType,date} are considered to be logical Dates with no time
// value, such as a holiday or birthday. In the browser, values for "date" fields are stored
// as Date objects, but when formatted for display to the user, they are typically displayed
// without any time information.
//
// When using the SmartClient server framework, "date" values are automatically transmitted
// with year, month and day preserved and time value ignored.
//
// When sent or received in XML or JSON, date field values should be serialized in the
// XML Schema date format -
// YYYY-MM-DD
- are expected to be received in the same format. Any time value
// present for a "date" field is ignored.
//
// System wide formatting for dates may be controlled via the
// +link{Date.setNormalDisplayFormat()} and +link{Date.setShortDisplayFormat()} methods.
//
//
"datetime" handling
//
// Fields of type +link{type:FieldType,datetime} are dates with full time information.
// In the browser, values for datetime fields are stored as Date objects.
//
// When using the SmartClient server framework, "datetime" values are automatically transmitted
// such that the resulting Date object has the same GMT/UTC timestamp (milliseconds since
// epoch).
//
// When sent or received in XML or JSON, datetime field values should be serialized out as full
// datetimes using the standard
// XML Schema date format
// (EG:2006-01-10T12:22:04-04:00
). If no timezone offset is supplied, the value
// is assumed to be GMT/UTC.
//
// System wide formatting for datetimes may be controlled via the
// +link{Date.setShortDatetimeDisplayFormat()} method. Datetimes will be displayed to the user
// in browser local time by default (see also timezone notes below).
//
//
"time" handling
//
// Fields of type +link{type:FieldType,time} are time values in the absence of a day, such as
// the beginning of the workday (9:00). In the browser, values for "time" fields are stored as
// Date objects with the time in browser local time. The date information has no meaning and
// only the time information is displayed to the user.
//
// Time formatting is handled by the +link{Time} class APIs.
//
// When using the SmartClient server framework, "time" values are automatically transmitted
// such that the resulting Date object has the same hour, minute and second values in local
// time, and year/month/day is ignored.
//
// When sent or received in XML or JSON, date field values should be serialized as hours,
// minutes and seconds using the standard
// XML Schema time
// format - "22:01:45"
. Timezone is not relevant and should be omitted.
//
//
Timezone settings and Daylight Savings Time
//
// By default, "datetime" values will be shown to the user in browser local time, as derived
// from the native browser locale. Developers may modify this behavior by specifying an
// explicit display timezone via +link{Time.setDefaultDisplayTimezone()}.
//
// Note that depending on the specific date being displayed, a Daylight Savings Time offset may
// also be applied based on the browser locale. To disable this behavior set
// +link{isc.Time.adjustForDST} to false.
//
// If a custom timezone is specified, it will be respected by all +link{TimeDisplayFormat}s, and
// by the standard short +link{DateDisplayFormat}s when formatting dates representing datetime
// type values. However native JavaScript Date formatters,
// including toLocaleString()
will not respect the specified timezone. Developers
// specifying a custom timezone may therefore wish to modify the +link{Date.setNormalDisplayFormat()}
// to avoid using a native JS Date formatter function.
//
// Note that in addition to the system-wide date, datetime and time-formatting settings described
// above, databound components also support applying custom display formats for date values.
// Typically this can be achieved via a custom dateFormatter
or
// timeFormatter
at the field level (see +link{dataSourceField.dateFormatter},
// +link{dataSourceField.timeFormatter} and for example +link{listGridField.dateFormatter}).
// Date formatting may also be configured at the component level by setting the
// dateFormatter
, datetimeFormatter
and timeFormatter
// attributes (See for example +link{listGrid.dateFormatter}, +link{listGrid.timeFormatter},
// and +link{listGrid.datetimeFormatter}).
//
// @title Date and Time Format and Storage
// @treeLocation Concepts
// @visibility external
//<
_xmlSerialize : function (name, type, namespace, prefix) {
return isc.Comm._xmlValue(name, this.toSchemaDate(),
type || (this.logicalDate ? "date" :
(this.logicalTime &&
!isc.DataSource.serializeTimeAsDatetime ? "time" : "datetime")),
namespace, prefix);
},
// logicalType parameter - option to specify "date" vs "datetime" vs "time" which impacts
// how this date instance should be serialized out.
// Alternatively logicalDate / logicalTime attributes may be hung onto the date objet
// directly.
// Used by DataSources when serializing dates out
toSchemaDate : function (logicalType) {
// logical date values have no meaningful time
// Note that they also have "no meaningful timezone" - we display native browser locale time
// to the user and when we serialize to send to the server we serialize in that same
// local timezone.
if ((logicalType == "date") || this.logicalDate) {
return isc.SB.concat(
this.getFullYear().stringify(4),
"-",
(this.getMonth() + 1).stringify(2), // getMonth() is zero-based
"-",
this.getDate().stringify(2)
);
};
// logical times are serialized as truncated schema strings (HH:MM:SS) by default
if ((!isc.DataSource || !isc.DataSource.serializeTimeAsDatetime) &&
(logicalType == "time" || this.logicalTime))
{
return isc.SB.concat(
this.getHours().stringify(2), ":",
this.getMinutes().stringify(2), ":",
this.getSeconds().stringify(2)
);
}
// represent date time values in UTC
return isc.SB.concat(
this.getUTCFullYear().stringify(4),
"-",
(this.getUTCMonth() + 1).stringify(2), // getMonth() is zero-based
"-",
this.getUTCDate().stringify(2),
"T",
this.getUTCHours().stringify(2),
":",
this.getUTCMinutes().stringify(2),
":",
this.getUTCSeconds().stringify(2)
);
},
//> @method date.toSerializeableDate() (A)
// Return this date in 'serialized' format YYYY-MM-DD HH:MM:SS
// @group dateFormatting
// @return (String) formatted date string
// @visibility external
//<
toSerializeableDate : function (omitTime) {
var output = isc.SB.create();
output.append(
this.getFullYear().stringify(4),
"-",
(this.getMonth() + 1).stringify(2), // getMonth() is zero-based
"-",
this.getDate().stringify(2)
);
if (!omitTime) output.append(
(isc.Comm.xmlSchemaMode ? "T" : " "),
this.getHours().stringify(2),
":",
this.getMinutes().stringify(2),
":",
this.getSeconds().stringify(2)
);
return output.toString();
},
//> @method date.toDBDate() (A)
// Return this date in the format the database can parse as a datetime:
// $$DATE$$:YYYY-MM-DD HH:MM:SS
// @group dateFormatting
//
// @return (string) formatted date string
// @visibility internal
//<
// Leave this internal for now
toDBDate : function () {
return isc.StringBuffer.concat(
"$$DATE$$:",
this.toSerializeableDate()
);
},
//> @method date.toDBDateTime() (A)
// Return this date in the format the database can parse as a dateTime:
// $$DATE$$:YYYY-MM-DD HH:MM:SS
// @group dateFormatting
//
// @return (string) formatted date string
// @visibility internal
//<
toDBDateTime : function () { return this.toDBDate(); },
//> @method date.setFormatter()
// Set the formatter for this date object to the method name passed in. After this call
// wherever appropriate SmartClient components will use this formatter function to return
// the date as a string.
// @group dateFormatting
// @param functionName (string) name of a date formatter method on this Date
// @visibility external
// @deprecated As of SmartClient 5.5 use the static methods
// +link{classMethod:Date.setNormalDisplayFormat} and
// +link{classMethod:Date.setShortDisplayFormat} to set default formatters for all dates
//<
setFormatter : function (formatter) {
this.setNormalDisplayFormat(formatter);
},
//> @method date.setLocaleStringFormatter() (A)
// Set the iscToLocaleString()
formatter for a specific date object.
// After this call, all theDate.toLocaleString()
calls will yield a string
// in this format.
//
// @param functionName (string) name of a dateFormatting function
// @group dateFormatting
// @visibility internal
// @deprecated As of SmartClient 5.5 use the static method
// +link{classMethod:Date.setLocaleStringFormatter} instead
//<
setLocaleStringFormatter : function (functionName) {
if (isc.isA.Function(this[functionName]) || isc.isA.Function(functionName))
this.localeStringFormatter = functionName;
},
// ------------------------Advanced Date Comparison -------------------------------------------
// (currently undocd)
isBeforeToday : function (dateObj) {
var today = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0).getTime();
if (dateObj.getTime() < today) return true;
else return false;
},
isToday : function (dateObj) {
if (this.getFullYear() == dateObj.getFullYear() && this.getMonth() == dateObj.getMonth()
&& this.getDate() == dateObj.getDate())
return true;
else return false;
},
isTomorrow : function (dateObj) {
var tomorrowStart = new Date(this.getFullYear(), this.getMonth(), this.getDate() + 1, 0);
var tomorrowEnd = new Date(this.getFullYear(), this.getMonth(), this.getDate() + 1, 23);
var dateTime = dateObj.getTime();
if (dateTime >= tomorrowStart.getTime() && dateTime <= tomorrowEnd.getTime()) {
return true;
} else {
return false;
}
},
isThisWeek : function (dateObj) {
var weekStart = new Date(this.getFullYear(), this.getMonth(), this.getDate() - this.getDay(), 0);
var weekEnd = new Date(this.getFullYear(), this.getMonth(), this.getDate() + (7 - this.getDay()), 23);
var dateTime = dateObj.getTime();
if (dateTime >= weekStart.getTime() && dateTime <= weekEnd.getTime()) {
return true;
} else {
return false;
}
},
isNextWeek : function (dateObj) {
var weekStart = new Date(this.getFullYear(), this.getMonth(), (this.getDate() - this.getDay()) + 7, 0);
var weekEnd = new Date(this.getFullYear(), this.getMonth(), (this.getDate() - this.getDay()) + 14, 23);
var dateTime = dateObj.getTime();
if (dateTime >= weekStart.getTime() && dateTime <= weekEnd.getTime()) {
return true;
} else {
return false;
}
},
isNextMonth : function (dateObj) {
var monthStart = new Date(this.getFullYear(), this.getMonth());
monthStart.setMonth(monthStart.getMonth() + 1);
if (monthStart.getFullYear() == dateObj.getFullYear() && monthStart.getMonth() == dateObj.getMonth()) {
return true;
} else {
return false;
}
}
});
//> @method date.toBrowserString()
// Native date.toString()
provided by the browser for Date objects
// @group dateFormatting
// @visibility internal
// @deprecated As of SmartClient 5.5
//<
// Note that the default formatter varies by browser/platform so it's not that useful.
// This was exposed in 5.2 so we're keeping it around for back-compat only
Date.prototype.toBrowserString = Date.prototype.toString;
//> @method date.toBrowserLocaleString() (A)
// Synonym for date.toLocaleString()
provided by the browser for Date objects
// @group dateFormatting
// @visibility internal
// @deprecated As of SmartClient 5.5
//<
Date.prototype.toBrowserLocaleString = Date.prototype.toLocaleString;
// set the standard formatter for the date prototype to the native browser string
// so everything works as normal until it is overridden.
if (!Date.prototype.formatter) Date.prototype.formatter = "toLocaleString"
// set the standard toShortDate() formatter to US Short Date
if (!Date.prototype._shortFormat) Date.setShortDisplayFormat("toUSShortDate");
if (!Date.prototype._shortDatetimeFormat) Date.setShortDatetimeDisplayFormat("toUSShortDatetime");
//> @method date.iscToLocaleString() (A)
// Customizeable toLocaleString() type method.
// This method is called when isc.iscToLocaleString(date) is called.
//
// @group dateFormatting
// @return (string) formatted date string
// @visibility internal
//<
// Leave this internal - we don't really expect this to be called directly or overridden by
// the developer
Date.prototype.iscToLocaleString = function () {
var formatter = this.localeStringFormatter;
if (isc.isA.Function(formatter)) return formatter.apply(this);
else if (this[formatter]) return this[formatter]();
}
// By default have iscToLocaleString() call date.toLocaleString()
if (!Date.prototype.localeStringFormatter)
Date.prototype.localeStringFormatter = "toLocaleString";
//>Safari12
isc.addMethods(Date, {
// Simple substring matching for splitting up a date string to avoid using unsupported
// string.match() method in early Safari
// Note - somewhat flawed: we're assuming well never be handed a single digit month or day
_splitDateViaSubstring : function (string, monthIndex, dayIndex, yearIndex) {
// We know that year may be after month and/or day - allow 3 chars ("DD/") for each
var yearCharIndex = yearIndex * 3,
year = string.substring(yearCharIndex, yearCharIndex +4) ;
// If we have a 2 char year, this may effect the position of the day/month in the string
var twoCharYear = (parseInt(year) != year);
if (twoCharYear) year = year.substring(0,2);
var monthCharIndex = 0,
dayCharIndex = 0;
if (monthIndex > dayIndex) monthCharIndex += 3;
else dayCharIndex += 3;
if (monthIndex > yearIndex) monthCharIndex += (twoCharYear?3 : 5);
if (dayIndex > yearIndex) dayCharIndex += (twoCharYear ? 3 : 5);
// Note: Month is zero based rather than 1 based.
var month = string.substring(monthCharIndex, monthCharIndex + 2) -1;
var day = string.substring(dayCharIndex, dayCharIndex +2);
// Hour minute second are not expected to change orders
var hourCharIndex = twoCharYear ? 9 : 11,
hour = (string.substring(hourCharIndex,hourCharIndex + 2) || 0),
minute = (string.substring(hourCharIndex + 3, hourCharIndex + 5) || 0),
second = (string.substring(hourCharIndex + 6, hourCharIndex + 8) || 0);
return[year,month,day,hour,minute,second];
}
});
// !BackCompat 2005.11.3
isc.addMethods(Date.prototype, {
//> @method date.toPrettyString()
// Return this date in the format: MM/DD/YY HH:MM
// @group dateFormatting
// @return (string) formatted date string
// @visibility external
// @deprecated As of SmartClient 5.5 use +link{date.toShortDate()} instead
//<
toPrettyString : function () {
return this.toUSShortDatetime();
}
});
isc.addMethods(Date, {
// --- Parsing functions --- :
// In 5.2 the paradigm was to provide formatters and complimentary parsers, like
// 'toEuropeanShortDate' and 'parseEuropeanShortDate'.
// We've moved away from this to instead use a single 'parseInput' function which takes a
// 'format' parameter specifying "MDY" / "DMY", etc.
// This is appropriate since we do not plan to provide parsing functions for every date formatter
// format.
// Leaving the older explicit parsing functions in place for back-compat only.
//> @classMethod Date.parseStandardDate()
// Parse a date passed in as a string of format:
// YYYY-MM-DD HH:MM:SS
or YYYY-MM-DD
// Returning a new Date
object with the appropriate value.
//
// @group dateFormatting
//
// @param dateString (string) date value as a string
//
// @return (date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseStandardDate : function (dateString) {
if (!isc.isA.String(dateString)) return null;
// Note: we could be using a regexp here rather than substring matches
var year = dateString.substring(0,4),
month = dateString.substring(5,7)-1,
day = dateString.substring(8,10),
hour = dateString.substring(11, 13),
minute = dateString.substring(14, 16),
second = dateString.substring(17, 19);
// If they all are numbers, construct a new date
// NOTE: If year - month - day gives a number then they
// are all numbers, or strings that implicitly convert to numbers.
// We could also use this syntax:
// if(parseInt(year) == year && parseInt(month) == month ...)
// but this is slower in both Moz and IE
if (dateString.length < 19) {
if (!isc.isA.Number(year - month - day)) return null;
} else {
if (!isc.isA.Number(year - month - day - hour - minute - second)) return null;
}
return new Date(year, month, day, hour, minute, second);
},
//> @classMethod Date.parseSerializeableDate()
// Parse a date passed in as a string of format:
// YYYY-MM-DD HH:MM:SS
or YYYY-MM-DD
// Returning a new Date
object with the appropriate value.
// This is a synonym for Date.parseStandardDate()
//
// @group dateFormatting
// @param dateString (string) date value as a string
// @return (Date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseSerializeableDate : function (dateString) {
// synonym for parseStandardDate
return this.parseStandardDate(dateString);
},
//> @classMethod Date.parseDBDate()
// Parse a date passed in as a string of format:
// $$DATE$$:YYYY-MM-DD HH:MM:SS
// Returning a new Date
object with the appropriate value.
//
// @group dateFormatting
// @param dateString (string) date value as a string
// @return (date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseDBDate : function (dateString) {
// remove the leading "$$DATE$$:"
if (isc.isA.String(dateString) && dateString.startsWith("$$DATE$$:")) {
dateString = dateString.substring(9)
return this.parseStandardDate(dateString);
}
return null;
},
//> @classMethod Date.parseDateStamp()
//
// Parse a dateStamp of the format: YYYYMMDDTHHMMSS[Z]
//
// @group dateFormatting
// @param dateString (string) String to parse
// @return (Date) Date object, or null if not parsed correctly.
//
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseDateStamp : function (string) {
if (string == null || isc.isA.Date(string)) return string;
var date = new Date( Date.UTC(
string.substring(0,4), // year
parseInt(string.substring(4,6), 10)-1, // mon
string.substring(6,8), // day
// omit this character (T)
string.substring(9,11), // hour
string.substring(11,13), // min
string.substring(13,15)
// Technically we should look at the last character - if its something other
// than "z" the timezone would be something other than UTC.
));
if (isc.isA.Date(date)) return date;
else return null;
},
//> @classMethod Date.parseShortDate()
// Parse a date passed in as a string of format: MM/DD/YYYY
//
// @group dateFormatting
// @param dateString (string) date value as a string
// @param [centuryThreshold] (number) if parsed year is 2 digits and less than this
// number, assume year to be 20xx
//
// @return (date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseShortDate : function (string, centuryThreshold) {
return this.parseInput(string, "MDY", centuryThreshold);
},
//> @classMethod Date.parseShortDateTime()
// Parse a date passed in as a string of format: MM/DD/YYYY HH:MM:SS
//
// @group dateFormatting
// @param dateString (string) date value as a string
// @param [centuryThreshold] (number) if parsed year is 2 digits and less than this
// number, assume year to be 20xx
//
// @return (date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseShortDateTime : function (string, centuryThreshold) {
// synonym for parseShortDate - included for completeness and to provide the appropriate
// compliment to date.toShortDateTime()
return this.parseShortDate(string, centuryThreshold);
},
//> @classMethod Date.parsePrettyString()
// Parse a date passed in as a string of format: MM/DD/YY HH:MM:SS
//
// @group dateFormatting
// @param dateString (string) date value as a string
// @param [centuryThreshold] (number) if parsed year is less than this
// number, assume year to be 20xx rather than 19xx
//
// @return (date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parsePrettyString : function (string, centuryThreshold) {
// this is just the same as a short date with a 2 digit year.
return this.parseShortDate(string, centuryThreshold);
},
//> @classMethod Date.parseEuropeanShortDate()
// parse a date passed in as a string of format: DD/MM/YYYY
// @group dateFormatting
// @param dateString (string) date value as a string
// @param [centuryThreshold] (number) if parsed year is 2 digits and less than this
// number, assume year to be 20xx
//
// @return (date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseEuropeanShortDate : function (string, centuryThreshold) {
return this.parseInput(string, "DMY", centuryThreshold);
},
//> @classMethod Date.parseEuropeanShortDateTime()
// parse a date passed in as a string of format: DD/MM/YYYY HH:MM:SS
// @group dateFormatting
// @param dateString (string) date value as a string
// @param [centuryThreshold] (number) if parsed year is 2 digits and less than this
// number, assume year to be 20xx
//
// @return (date) date value
// @visibility internal
// @deprecated As of SmartClient 5.5 use +link{date.parseInput} instead
//<
parseEuropeanShortDateTime : function (string, centuryThreshold) {
return this.parseInput(string, "DMY", centuryThreshold);
},
// Helper to set the time to zero for a datetime
setToZeroTime : function (date) {
if (date == null || !isc.isA.Date(date)) return date;
// Clear the "logicalDate" flag so when we run through formatters we respect
// developer specified timezone rather than displaying time in the browser native timezone
var wasLogicalDate = date.logicalDate;
date.logicalDate = false;
var timestamp = date.getTime();
// Apply the timezone offset such that if the default system-wide formatter is used
// and applies the display timezone offset, 00:00 will be seen.
var hourOffset = isc.Time.getUTCHoursDisplayOffset(date),
minuteOffset = isc.Time.getUTCMinutesDisplayOffset(date),
utcHours = hourOffset > 0 ? 24-hourOffset : 0-hourOffset,
utcMins = minuteOffset > 0 ? 60-minuteOffset : 0-minuteOffset;
var oldDisplayDate;
if (wasLogicalDate) {
oldDisplayDate = date.getDate();
} else {
var offsetDate = date._getTimezoneOffsetDate(hourOffset, minuteOffset);
oldDisplayDate = offsetDate.getUTCDate();
}
date.setUTCHours(utcHours);
var displayOffsetDate = date._getTimezoneOffsetDate(hourOffset, minuteOffset),
displayDate = displayOffsetDate.getUTCDate(),
adjustedUTCHours = utcHours;
if (displayDate != oldDisplayDate) {
// Cant just check for displayDate > oldDisplayDate since it might be the first or
// last of a month...
var moveForward = date.getTime() < timestamp;
adjustedUTCHours += moveForward ? 24 : -24;
date.setUTCHours(adjustedUTCHours);
}
if (date.getUTCHours() != utcHours) {
date.setTime(timestamp);
date.setUTCHours(adjustedUTCHours+1);
if (date.getUTCHours() != utcHours+1) {
date.setTime(timestamp);
date.setUTCHours(adjustedUTCHours+2);
}
}
date.setUTCMinutes(utcMins);
// No need to return the date - we updated it directly.
}
});
//