UNPKG

cejs

Version:

A JavaScript module framework that is simple to use.

1,594 lines (1,437 loc) 136 kB
/** * @name CeL function for date / time operations. * @fileoverview 本檔案包含了 date / time 的功能。 * * TODO: http://momentjs.com/ * @see Moment.js https://momentjs.com/ * @since */ 'use strict'; // 'use asm'; // More examples: see /_test suite/test.js // -------------------------------------------------------------------------------------------- // 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。 typeof CeL === 'function' && CeL.run({ // module name name : 'data.date', // includes() @ CeL.data.code.compatibility. require : 'data.code.compatibility.' // for gettext() // + '|application.locale' + '|data.native.set_bind|data.code.parse_escape|data.native.pad', // 設定不匯出的子函式。 // no_extend : '*', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { // requiring var set_bind = this.r('set_bind'), parse_escape = this.r('parse_escape'), pad = this .r('pad'); /** * null module constructor * * @class date objects 的 functions */ var _// JSDT:_module_ = function() { // null module constructor }; /** * for JSDT: 有 prototype 才會將之當作 Class */ _// JSDT:_module_ .prototype = {}; if (false) { (function() { /* * opposite of toUTCString() 尚不成熟!假如是type=='date',不如用new Date()! * string大部分可用new Date(Date.parse(str))代替! * http://www.comsharp.com/GetKnowledge/zh-CN/TeamBlogTimothyPage_K742.aspx */ var UTCDay, UTCMonth; set_Object_value('UTCDay', 'Sun,Mon,Tue,Wed,Thu,Fri,Sat', 1); set_Object_value('UTCMonth', 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec', 1); // 0:[Mon, 9 Aug 2004 12:05:00 GMT],1:[Thu Sep 30 // 18:12:08 UTC+0800 2004],2:[Sat Jun 26 18:19:46 2004] var fromUTCStringFormat = [ [ 0, 3, 2, 1, 4 ], [ 0, 5, 1, 2, 3 ], [ 0, 4, 1, 2, 3 ] ]; function fromUTCString(str, format) { var s = '' + str, f; if (!s) return; if (typeof format == 'undefined') if (f = Date.parse(s)) return new Date(f); else return 'Unknown format!';// format=0; if (!isNaN(format) && format < fromUTCStringFormat.length) f = fromUTCStringFormat[format]; else return 'Yet support this kind of format[' + format + ']!\nWe support to ' + fromUTCStringFormat.length + '.'; if (!f[0]) f[0] = ' '; s = s.replace(new RegExp(f[0] + '+', 'g'), f[0]).split(f[0]); if (s.length < f.length) return 'The item length of data: ' + s.length + ' is less then format[' + format + ']: ' + f.length + '!\n' + s.join(',');// new // Date if (f.length == 5) s[f[4]] = s[f[4]].split(':'); else if (f.length == 7) s[f[4]] = [ s[f[4]], s[f[5]], s[f[6]] ]; else return 'Illegal date format!'; if (format == 1 && s[4].match(/([+\-]\d{2})/)) s[f[4]][0] = parseInt(s[f[3]][0]) + parseInt(RegExp.$1); library_namespace.debug(str + '\n' + s[f[1]] + ',' + s[f[2]] + '(' + UTCMonth[s[f[2]]] + '),' + s[f[3]] + ',' + s[f[4]][0] + ',' + s[f[4]][1] + ',' + s[f[4]][2]); // check, 可以包括星期 if (!(s[f[2]] = UTCMonth[s[f[2]]]) || !(s = new Date(s[f[1]], s[f[2]], s[f[3]], s[f[4]][0], s[f[4]][1], s[f[4]][2]))) // Date.UTC() s = 'Input data error!'; return s; } }); } // ----------------------------------------------------------------------------------------------------------------------------------------------------------// var is_Date = library_namespace.is_Date, // UTC_PATTERN = /UTC(?:\s*([+\-]?\d{1,2})(:\d{1,2})?)?/i, // assert: isNaN(DEFAULT_TIME_ZONE) === true // isNaN(Object.create(null)) will throw @ Chrome/36 // (Cannot convert object to primitive value), // therefore we can't use Object.create(null) here. DEFAULT_TIME_ZONE = { timezone : 'default' }; // 嘗試 UTC+08:00 之類的標準表示法。 function get_minute_offset(date_string) { var matched = date_string.match(UTC_PATTERN); if (matched) { return 60 * (matched[1] | 0) + (matched[2] | 0); } } // ----------------------------------------------------------------------------------------------------------------------------------------------------------// // basic constants. 定義基本常數。 /** {Number}一整天的 time 值。should be 24 * 60 * 60 * 1000 = 86400000. */ var ONE_DAY_LENGTH_VALUE = new Date(0, 0, 2) - new Date(0, 0, 1), // ONE_DAY_LENGTH_VALUE = CeL.date.to_millisecond('1D') // 3 * ONE_DAY_LENGTH_VALUE === CeL.date.to_millisecond('3D') /** {Number}一分鐘的 time 值(in milliseconds)。should be 60 * 1000 = 60000. */ ONE_MINUTE_LENGTH_VALUE = new Date(0, 0, 1, 0, 2) - new Date(0, 0, 1, 0, 1), /** {Number}一整時辰的 time 值。should be 2 * 60 * 60 * 1000 = 7200000. */ ONE_時辰_LENGTH_VALUE = new Date(0, 0, 0, 2) - new Date(0, 0, 0, 0), // e.g., UTC+8: -8 * 60 = -480 present_local_minute_offset = (new Date).getTimezoneOffset() || 0; // ----------------------------------------------------------------------------------------------------------------------------------------------------------// // for Julian date. 期能不使用內建 Date 以快速計算日期。 // @see https://en.wikipedia.org/wiki/Julian_day#Calculation // 適用範圍: 4717/3/1 BCE 0:0 之後。 /** * Get Julian day number (JDN) of date.<br /> * If type of date is Date, we'll treat date as local date.<br /> * 因為得出的是 UTC+0 12:0 之 JDN,UTC+0 0:0 之 JD = JDN - .5。 * * JDN = Math.round(JD); * * @param {String|Date|Number}date * {Date}date or date value * @param {Boolean}type * calendar type. true: Gregorian, false: Julian, 'CE': Common * Era * @param {Boolean}no_year_0 * no year 0 * @param {Boolean}get_remainder * Will return [ {Number} Julian day number (JDN), {Number} * remainder ].<br /> * remainder / ONE_DAY_LENGTH_VALUE = day. * * @returns {Number} Julian day number (JDN) */ function Julian_day(date, type, no_year_0, get_remainder) { if (typeof date === 'string') { var matched = date // parse '1/1/1' .match(/(-?\d{1,4})[\/\-](\d{1,2})[\/\-](\d{1,2})/); if (matched) { return Julian_day.from_YMD(matched[1] | 0, matched[2] | 0, matched[3] | 0, type, no_year_0); } if (-4716 < date && date < 9999) { // treat as year return Julian_day.from_YMD(date | 0, 1, 1, type, no_year_0); } if (matched = date.match(/(-?\d{1,4})[\/\-](\d{1,2})/)) { return Julian_day.from_YMD(matched[1] | 0, matched[2] | 0, 1, type, no_year_0); } // throw new Error('Julian_day: Cannot parse [' + date + ']'); if (library_namespace.is_debug(2)) { library_namespace.error('Julian_day: 無法解析 [' + date + ']!'); } return; } if (!isNaN(date)) { // offset: convert local to UTC+0. var offset; if (is_Date(date)) { offset = date.getTimezoneOffset() * ONE_MINUTE_LENGTH_VALUE; date = date.getTime(); } else { offset = Julian_day.default_offset; } // treat ((date)) as date value. So it's Gregorian. type = true; date -= offset // epoch 為 12:0,需要將之減回來以轉成 midnight (0:0)。 + Julian_day.epoch - ONE_DAY_LENGTH_VALUE / 2; var remainder; if (get_remainder) { remainder = date % ONE_DAY_LENGTH_VALUE; if (remainder < 0) { remainder += ONE_DAY_LENGTH_VALUE; } } date = Math.floor(date / ONE_DAY_LENGTH_VALUE); return get_remainder ? [ date, remainder ] : date; } if (Array.isArray(date)) { var JD = Julian_day.from_YMD(date[0], date[1], date[2], type, no_year_0); return get_remainder ? [ JD, date.length > 3 // ? Julian_day.from_HMS(date[3], date[4], date[5]) - .5 : 0 ] : JD; } } /** * Get JDN of (year, month, date).<br /> * input MUST latter than -4716/3/1 (4717/3/1 BCE)!! * * <code> JDN = CeL.Julian_day.from_YMD(year, month, date, 'CE'); </code> * * @param {Integer}year * year >= -4716 * @param {Natural}month * 1–12 * @param {Natural}date * 1–31 * @param {Boolean}type * calendar type. true: Gregorian, false: Julian, 'CE': Common * Era * @param {Boolean}no_year_0 * no year 0 * * @returns {Number} JDN */ Julian_day.from_YMD = function(year, month, date, type, no_year_0) { if (Array.isArray(year)) { return Array.isArray(month) // month = [H,M,S] ? Julian_day.from_YMD(year[0], year[1], year[2], date, type) - .5 + Julian_day.from_HMS(month[0], month[1], month[2]) // : Julian_day.from_YMD(year[0], year[1], year[2], month, date); } if (no_year_0 && year < 0) { // no year 0. year: -1 → 0 year++; } if (type === 'CE') { type = year > 1582 // Julian calendar(儒略曆)1582年10月4日的下一日為 // Gregorian calendar(格里高利曆)1582年10月15日。 || year == 1582 && (month > 10 || month == 10 && date >= 15); } // method: 自 3月起算。 if (false) { if (month < 3) { year = +year + 4800 - 1 | 0; month = +month + 12 - 3 | 0; } else { year = +year + 4800 | 0; month = +month - 3 | 0; } } // a=1: 1–2月, a=0: 3–12月 // var a = (14 - month) / 12 | 0; var a = month < 3 ? 1 : 0; year = +year + 4800 - a | 0; month = +month + 12 * a - 3 | 0; // assert: year, month are integers. month >= 0 // 3–7月:153日 return +date + ((153 * month + 2) / 5 | 0) // + 365 * year + Math.floor(year / 4) - // for Gregorian calendar (type ? 32045 + Math.floor(year / 100) - Math.floor(year / 400) // for Julian calendar : 32083); }; /** * Get day value from hour, minute, second.<br /> * TODO: microsecond µs, nanosecond ns * * @param {Number}[hour] * hour * @param {Number}[minute] * minute * @param {Number}[second] * second * @param {Number}[millisecond] * millisecond * * @returns {Number}day value */ Julian_day.from_HMS = function(hour, minute, second, millisecond) { // initialization, milliseconds to seconds var time = millisecond ? millisecond / 1000 : 0; if (second) { time += +second; } // to minutes time /= 60; if (minute) { time += +minute; } // to hours → to days return (time / 60 + (+hour || 0)) / 24; }; /** * Get (year, month, date) of JDN. * * @param {Number}JDN * Julian date number * @param {Boolean}type * calendar type. true: Gregorian, false: Julian, 'CE': Common * Era. * @param {Boolean}no_year_0 * no year 0 * * @returns {Array} [ year, month, date ] * * @see https://en.wikipedia.org/wiki/Julian_day#Julian_or_Gregorian_calendar_from_Julian_day_number * algorithm by Richards 2013 */ Julian_day.to_YMD = function(JDN, type, no_year_0) { var f = JDN + 1401 | 0; if (type && (type !== 'CE' || JDN >= Gregorian_reform_JDN)) { // to proleptic Gregorian calendar f += ((((4 * JDN + 274277) / 146097 | 0) * 3) / 4 | 0) - 38; } else { // to proleptic Julian calendar with year 0 } var e = 4 * f + 3 | 0, // g = (e % 1461) / 4 | 0, // h = 5 * g + 2, // date = ((h % 153) / 5 | 0) + 1, // month = (((h / 153 | 0) + 2) % 12) + 1, // year = (e / 1461 | 0) - 4716 + ((12 + 2 - month) / 12 | 0); if (no_year_0 && year < 1) { // no year 0. year: 0 → -1 year--; } // TODO: time return [ year, month, date ]; }; /** * JD to YMDHMS. Get (year, month, date, hour, minute, second) of JD. * * @param {Number}JD * Julian date * @param {Number}zone * local time zone. 0 if is UTC+0 (default), 8 if is UTC+8. * @param {Boolean}type * calendar type. true: Gregorian, false: Julian, 'CE': Common * Era. * @param {Boolean}no_year_0 * no year 0 * * @returns {Array} [ year, month, date, hour, minute, second ] */ Julian_day.to_YMDHMS = function(JD, zone, type, no_year_0) { // +.5: input JD instead of JDN // 1e-16 (days): for error. e.g., CeL.Julian_day.to_YMDHMS(.6, 8) // 2451544.5 is 2000/1/1 0:0 UTC+12, 1999/12/31 12:0 UTC+0 // → 2451545 is 2000/1/1 12:0 UTC+0 // 0 is -4712/1/1 12:0 UTC+0, -4712/1/2 0:0 UTC+12 var JDN = Julian_day.to_YMD(JD += .5 + 1e-16 + (zone | 0) / 24, type, no_year_0); // to local time JDN.push((JD = JD.mod(1) * 24) | 0, (JD = (JD % 1) * 60) | 0, (JD = (JD % 1) * 60) | 0); // milliseconds 去除 error。 // 4e-11: // 1e-16*86400 ≈ 1e-11 // (-Math.log10((1/2-1/3-1/6)*86400)|0) → 1e-11 // So we use 1e-11 + 1e-11 = 2e-11. // But for CeL.Julian_day.to_YMDHMS(.6, 8), it seems still not enough. // We should use 4e-11 at least. if ((JD %= 1) > 4e-11) { // 8.64e-9 = 1e-16 * 86400000: 將之前加的 error 修正補回來。 // 約精確到 1e-7 ms JDN.push(JD * 1000 - 8.64e-9); } else { // 當作 error。 } return JDN; }; /** * Get the local midnight date of JDN.<br /> * 傳回 local midnight (0:0)。 * * <code> date = CeL.Julian_day.to_Date(JDN); </code> * * @param {Integer}JDN * input {Integer}JDN or {Number}JD. * @param {Boolean}is_JD * The JDN is JD. * @param {Boolean}get_value * get {Number} date value instead of {Date}. * * @returns {Date} local midnight date */ Julian_day.to_Date = function(JDN, is_JD, get_value, offset) { if (!is_JD) { // epoch 為 12:0,需要將之減回來以轉成 midnight (0:0)。 JDN -= .5; } JDN = JDN * ONE_DAY_LENGTH_VALUE + Julian_day.epoch // + (isNaN(offset) ? Julian_day.default_offset : offset); return get_value ? JDN : new Date(JDN); }; Julian_day.YMD_to_Date = function(year, month, date, type, get_value, no_year_0) { var JDN = Julian_day.from_YMD(year, month, date, type, no_year_0); // 當作 JD 才方便 date.format() 得到正確結果。 return Julian_day.to_Date(JDN, true, get_value); }; /** * Get Julian date (JD) of date. * * @param {String|Date|Number}date * date or date value * @param {Boolean}type * calendar type. true: Gregorian, false: Julian, 'CE': Common * Era * @param {Boolean}no_year_0 * no year 0 * * @returns {Number} Julian date */ Julian_day.JD = function(date, type, no_year_0) { if (is_Date(date)) return Date_to_JD(date); date = Julian_day(date, type, no_year_0, true); return date[0] + date[1] / ONE_DAY_LENGTH_VALUE; }; /** * default offset (time value) * * @type {Integer} */ Julian_day.default_offset = present_local_minute_offset * ONE_MINUTE_LENGTH_VALUE; // Get the epoch of Julian date, i.e., -4713/11/24 12:0 (function() { var date = new Date(0), // [ -4713, 11, 24 ] JD0 = Julian_day.to_YMD(0, true); // set the date value of Julian date 0 date.setUTCHours(12, 0, 0, 0); date.setUTCFullYear(JD0[0] | 0, (JD0[1] | 0) - 1, JD0[2] | 0); Julian_day.epoch = date.getTime(); // Julian_day.epoch = -210866760000000; })(); /** * Gregorian reform JDN. * * @type {Integer} */ var Gregorian_reform_JDN = Julian_day.from_YMD(1582, 10, 15); /** * Get weekday index of JD. * * @param {Number}JD * Julian date * @param {Boolean}to_ISO * to ISO type. * * @returns {Integer} weekday index. * * @see https://en.wikipedia.org/wiki/Zeller's_congruence */ Julian_day.weekday = function(JD, to_ISO) { return to_ISO ? (Math.floor(JD) % 7) + 1 // Sunday: 0, Monday: 1, ... : (Math.floor(JD) + 1) % 7; }; _.Julian_day = Julian_day; // ----------------------------------------------------------------------------------------------------------------------------------------------------------// // Unix time (a.k.a. POSIX time or Epoch time) function Unix_time(date) { return ((date || Date.now()) - Unix_time.epoch) / 1000; } _.Unix_time = Unix_time; // Unix epoch '1970-01-01T00:00:00Z', 0 @ most systems Unix_time.epoch = Date.parse('1970/1/1 UTC'); Unix_time.to_Date = function(time_value) { return new Date(1000 * time_value + Unix_time.epoch); }; // ----------------------------------------------------------------------------------------------------------------------------------------------------------// // The 1900 Date System // 序列值 1 代表 1/1/1900 12:00:00 a.m。 // 數字 32331.06 代表日期和時間 7/7/1988年 1:26:24 a.m。 var Excel_1900_epoch = Date.parse('1900/1/1') - ONE_DAY_LENGTH_VALUE, // The 1904 Date System // 依預設,Excel for Mac 使用 1904 日期系統,而 Excel for Windows 使用 1900 日期系統。這表示當您在 // Excel for Mac 中輸入序列值 1 並將其格式化為日期,Excel 會將其顯示為 1/2/1904 12:00 a.m。Excel // for Windows 則會將序列值 1 顯示為 1/1/1900 12:00 a.m。 // Date.parse('1904/1/2') - ONE_DAY_LENGTH_VALUE Excel_1904_epoch = Date.parse('1904/1/1'); function Excel_Date(date_value, is_Mac, get_value) { // 0會被轉成 1900/1/0,已經不正常。 if (date_value >= 1) { // 這邊採用與 function Date_to_Excel() 相對應的判別式。 if (!is_Mac && !(date_value < 60)) { date_value--; } date_value = (is_Mac ? Excel_1904_epoch : Excel_1900_epoch) + ONE_DAY_LENGTH_VALUE * date_value; } else { date_value = NaN; } return get_value ? date_value : new Date(date_value); } _.Excel_Date = Excel_Date; if (false) { Excel_Date.error_value = { valueOf : function() { return NaN; }, toString : function() { return '#VALUE!'; } }; } // Excel 2010 會將錯誤值顯示為'#VALUE!',但負數或過大值則會以'#'填滿格子(e.g., "#########")。 Excel_Date.error_value = '#VALUE!'; if (false) { // to show Excel date (date = date.to_Excel()) && date.toFixed(2) || CeL.Excel_Date.error_value; } // http://support.microsoft.com/kb/214094 // Excel for Mac uses the 1904 date system and // Excel for Windows uses the 1900 date system. function Date_to_Excel(date, is_Mac) { date = date.getTime() - (is_Mac ? Excel_1904_epoch : Excel_1900_epoch); return date >= 1 ? // Excel 有 1900/2/29 (60),但現實中沒有這天。因此一般轉換時,不應出現60之值。 // Mac 系統以 1904 CE 起始,迴避了這個問題。 // 0: 1900/1/0 // https://en.wikipedia.org/wiki/Year_1900_problem // 60: 1900/2/29 (nonexistent date) // 61: 1900/3/1 (date /= ONE_DAY_LENGTH_VALUE) < 60 || is_Mac ? date : date + 1 // or use Excel_Date.error_value : NaN; } // ---------------------------------------------------------------------------- // 2016/8/22 22:1:51 /** * <code> https://msdn.microsoft.com/en-us/library/system.datetime.tofiletime.aspx (long) DateTime.ToFileTime Method () A Windows file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed since 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC). Windows uses a file time to record when an application creates, accesses, or writes to a file. </code> */ VS_file_time.epoch = Date.parse('1601-01-01T00:00:00Z'); // https://msdn.microsoft.com/en-us/library/system.datetime.fromfiletime.aspx // DateTime.FromFileTime Method (Int64) // Converts the specified Windows file time to an equivalent local time. function VS_file_time(file_time, return_Date) { var date_value = VS_file_time.epoch + file_time / (1e-3 / (1e-9 * 100)); return return_Date ? new Date(date_value) : date_value; } _.VS_file_time = VS_file_time; // ----------------------------------------------------------------------------------------------------------------------------------------------------------// // ISO 8601 var PATTERN_ISO_DATE = /^-?\d{4,8}-[01]\d-[0-3]\d(T[012]\d:[0-6]\d:[0-6]\d(\.\d{3})?(Z|[+\-][012]\d:\d{2}))?$/; /** * convert the string to Date object. * * TODO: parse /([今昨明]|大?[前後])天/, '01-03' (相對於當前), * /\d+(分[鐘钟]?|小[時时]|毫?秒|[日天週年]|星期|[禮礼]拜|[個个]月)([前後])/; 相對於 * options.base_date . also see indicate_date_time() * * @example <code> * '2003/1-4 12:53:5.45PM'.to_Date('CST').format(); * '12:53:5.45PM 2003/1-4'.to_Date('CST').format(); * </code> * * @param {String}date_string * date string * @param {Object}options { * <br /> * {String|RegExp}format: the format used.<br /> * {Function}parser: the parser used. if set to unrecognized * (e.g., null) parser, it will use Date.parse() ONLY.<br /> * {String|Number}zone: 設定 date_string 之 time zone or country * (e.g., 'CST', 'TW') || 時差 in hour (例如 TW: UTC+8 → 8, 可使用.5).<br /> * {Date}reform: 對某些特殊 paser,如 CE,需要特別設定改曆日期時用。<br /> * <br /> * {Boolean}period_end:<br /> * 將指定內容視為一時段,並取得此期間之結束(終結)時間,因此 parse 後將得到第一個不屬於此範圍之時刻。<br /> * e.g., '2000/5' → 2000/6/1 0:0:0<br /> * e.g., '5/3' → 5/4 0:0:0<br /> * e.g., '5/3 12:' → 5/4 13:0:0<br /> * e.g., '5/3 12:50' → 5/4 12:51:0<br /> } * * @returns {Date} new Date * @since 2012/3/22 23:58:38 重構並測試。 * @see <a href="http://msdn.microsoft.com/zh-tw/library/t5580e8h.aspx" * accessdate="2012/3/23 23:26">JScript Date 物件</a> * @see wikitext: {{#time:Y年n月j日|+1 day}} */ function String_to_Date(date_string, options) { // 檢測輸入引數(arguments),將之正規化(normalization),處理、轉換為規範之標準型態。 library_namespace.debug('parse (' + typeof date_string + ') [' + date_string + ']', 3, 'String_to_Date'); if (typeof date_string === 'date') { // 應對在 Excel 等外部程式會出現的東西。 return new Date(date_string); } if (is_Date(date_string)) { return date_string; } date_string = date_string.trim(); if (!date_string) { // this.toString(); // date_string = this.valueOf(); return; } if (PATTERN_ISO_DATE.test(date_string)) { // 對於有明確指定之 UTC date 如 .toISOString() 之產出或 ISO 8601, // 應當不管 time zone 如何設定,直接回傳。 return new Date(date_string); } var tmp, matched, minute_offset; if (library_namespace.is_RegExp(options)) { // 將 options 當作 pattern。 options = { pattern : options }; } else if (!library_namespace.is_Object(options)) { // 前置處理。 tmp = options; options = Object.create(null); if (tmp) { if (tmp in String_to_Date.parser) { options.parser = String_to_Date.parser[tmp]; } else if ((tmp in String_to_Date.zone) || !isNaN(tmp)) { options.zone = tmp; } else { // 判斷是否為正規 format。 options.format = tmp; } } } // console.trace(date_string); if (library_namespace.is_RegExp(options.pattern) // && (matched = date_string.match(options.pattern))) { // 依照 matched 匹配的來正規化/設定年月日。 // e.g., new Date('1234/5/6') // === '1234年5月6日'.to_Date(/(\d+)年(\d+)月(\d+)日/) // === // '5/6/1234'.to_Date({pattern:/(\d+)\/(\d+)\/(\d+)/,pattern_matched:[3,1,2]}) tmp = Array.isArray(options.pattern_matched) ? options.pattern_matched : [ 1, 2, 3 ]; date_string = tmp.map(function(processor) { return typeof processor === 'function' // ? processor(matched) : matched[processor]; }).join( // 長度3時當作年月日,否則當作自訂處理。 tmp.length === 3 ? '/' : ''); } // console.trace(date_string); // 設定指定 time zone 之 offset in minutes. tmp = options.zone; library_namespace.debug('設定 time zone / offset hours: ' + tmp, 2); // TODO: for Daylight Saving Time (DST) time zones, etc. if (tmp in String_to_Date.zone) { tmp = String_to_Date.zone[tmp]; } if (library_namespace.is_Function(tmp)) { tmp = tmp(); } if (typeof tmp !== 'string' || isNaN(minute_offset = get_minute_offset(tmp))) { minute_offset = // 測試純數字小時。 -12 <= tmp && tmp <= 14 ? 60 * tmp // 再測試純數字分鐘。 : isNaN(tmp) // ? DEFAULT_TIME_ZONE : +tmp; } library_namespace.debug('最終設定 offset ' + (minute_offset === DEFAULT_TIME_ZONE ? '(default = ' + String_to_Date.default_offset + ')' : minute_offset) + ' minutes.', 2); // 判別 parser。 tmp = library_namespace.is_Function(tmp = options.parser) ? tmp : String_to_Date.parser[tmp] || String_to_Date.default_parser; if (library_namespace.is_Function(tmp)) { library_namespace.debug('use customize parser to parse (' + typeof date_string + ') [' + date_string + '].', 2); // console.trace(date_string); if (tmp = tmp(date_string, // assert: parser 亦負責 parse time zone offset. minute_offset, options)) { return tmp; } } library_namespace.debug('無法以 parser 判別。use Date.parse() to parse.', 2); if (tmp = Date.parse(date_string)) { // TODO: period_end 無效。 // native parser 會處理 time zone offset. tmp = new Date(tmp); if (!isNaN(minute_offset) && minute_offset !== DEFAULT_TIME_ZONE) { tmp.setMinutes(tmp.getMinutes() - tmp.getTimezoneOffset() - minute_offset); } return tmp; } } // 本地之 time zone / time offset (UTC offset by minutes)。 // e.g., UTC+8: 8 * 60 = +480 // e.g., UTC-5: -5 * 60 // 亦為 Date.parse(date_string) 與 new Date() 會自動附上的當地時間差距。 // assert: String_to_Date.default_offset 為整數。 String_to_Date.default_offset = -present_local_minute_offset; /** * <code> 主要指是否計算 0 year。 .no_year_0 = true: 將 astronomical year numbering 轉成一般紀年法(1 BCE→1 CE)。 僅用於計算 Gregorian calendar, Julian calendar。 normal astronomical 2 2 1 1 -1 0 -2 -1 -3 -2 </code> */ String_to_Date.no_year_0 = Date_to_String.no_year_0 = true; var stem_branch_date_pattern, // 精密度: 千紀,世紀,年代,年,月,日,時,分,秒,毫秒 index_precision = 'millennium,century,decade,year,month,day,hour,minute,second,microsecond' .split(','); (function() { // e.g., for '公元前720年2月22日' var start_pattern = '^[^\\d:\\-−‐前.]*', // with weekday 星期 mid_pattern = '(?:\\s*\\(?[日月火水木金土一二三四五六]\\)?)?(?:\\s+', // e.g., for '1616年2月壬午', '7時' end_pattern = ')?[^\\d日時]*$', // pattern of date. 當今會準確使用的時間, // 為 -47xx BCE (Julian day 0) 至 2xxx CE。 date_pattern = /(?:([\-−‐前]?(?:[0-4]?\d{3}|\d{1,3}))[\/.\-年 ])?\s*([01]?\d)(?:[\/.\-月 ]\s*([0-3]?\d)日?)?/.source, // pattern of time. [0-6]: 支持閏秒 time_pattern = /([0-2]?\d)[:時时]\s*(?:([0-6]?\d)[:分]?\s*(?:([0-6]?\d)(?:\.(\d+))?)?)?秒?\s*(?:([PA])M)?/i.source; // 日期先: date [time] String_to_Date_default_parser.date_first = new RegExp(start_pattern + date_pattern + mid_pattern + time_pattern + end_pattern, 'i'); // 時間先: time [date] String_to_Date_default_parser.time_first = new RegExp(start_pattern + time_pattern + mid_pattern + date_pattern + end_pattern, 'i'); // 將於下方作初始化。 stem_branch_date_pattern = date_pattern; })(); // [ all, start month, end month, year, misc ] var PATTERN_EN_MONTH_YEAR = /^(?:([a-z]{3,9})\s*[.\/\-–-—─~~〜﹣])?\s*([a-z]{3,9}),?\s+(\d{1,4})( +\D.*)?$/i, // [ all, year, start month, end month, misc ] PATTERN_EN_YEAR_MONTH = /^(\d{1,4})\s+(?:([a-z]{3,9})\s*[.\/\-–-—─~~〜﹣])?\s*([a-z]{3,9})( +\D.*)?$/i, // U+2212 '−': minus sign // 為了 calendar 測試,年分需要能 parse 0–9999。 // [ all, .*年, \d+, [百千] ] PATTERN_YEAR_ONLY = /^[^\d\/:\-−‐前日月年]*(\d{3,4}|([\-−‐前]?\d{1,4})([百千]?)年|[\-−‐前]\d{1,4})[^\d\/:\-−‐前日月年]*$/, // PATTERN_BCE = /(?:^|[^a-z.])B\.?C\.?E?(?:[^a-z.]|$)/i, time_boundary = new Date( 0, 0, 1); time_boundary.setFullYear(0); time_boundary = time_boundary.getTime(); /** * parse date_string and return the new Date. * * @param {String}date_string * date string. * @param {Integer}minute_offset * (指定 time zone 之) offset in minutes. * @param {Object}options { * {Boolean}period_end:<br /> * 將指定內容視為一時段,並取得此期間之結束(終結)時間,因此 parse 後將得到第一個不屬於此範圍之時刻。<br /> * e.g., '2000/5' → 2000/6/1 0:0:0<br /> * e.g., '5/3' → 5/4 0:0:0<br /> * e.g., '5/3 12:' → 5/4 13:0:0<br /> * e.g., '5/3 12:50' → 5/4 12:51:0<br /> } * * @returns {Date} new Date * @see <a href="http://php.net/manual/en/function.date.php" * accessdate="2012/3/23 20:51">PHP: date - Manual</a> */ function String_to_Date_default_parser(date_string, minute_offset, options) { // console.trace(date_string); if (is_Date(date_string)) { return date_string; } // 前置處理。 if (!library_namespace.is_Object(options)) { options = Object.create(null); } var date_data, // 精密度 precision, period_end = options.period_end, // matched string matched, tmp, // no_year_0 = 'no_year_0' in options ? options.no_year_0 : String_to_Date.no_year_0; date_string = date_string.trim() // 注意:"紀"會轉換成結束時間。 .replace(/世[紀纪]/g, '百年').replace(/千[紀纪]/g, '千年'); // ------------------------------------------------ // [ all, start month, end month, year, misc ] matched = date_string.match(PATTERN_EN_MONTH_YEAR); if (!matched && (matched = date_string.match(PATTERN_EN_YEAR_MONTH))) { matched.splice(4, 0, matched[1]); matched.splice(1, 1); } if (matched) { // e.g., 'May–June 1998', 'June 1998 UTC+6' // console.trace(period_end, matched); var date_value = Date.parse( // (!period_end && matched[1] || matched[2]) + ' ' + matched[3] // matched[4]: e.g., 'UTC+8' + (matched[4] || '')); if (isNaN(date_value)) { // Cannot parse "month year" library_namespace.debug('無法 parse: [' + date_string + ']', 2, 'String_to_Date_default_parser'); return; } if (!/UTC(?:\W|$)/.test(matched[4]) // && !isNaN(minute_offset) && minute_offset !== DEFAULT_TIME_ZONE) { date_value -= (present_local_minute_offset + minute_offset) * ONE_MINUTE_LENGTH_VALUE; } date_value = new Date(date_value); if (period_end) { date_value.setMonth(date_value.getMonth() + 1); } else if (false && matched[1]) { library_namespace.warn('Cannot handle date range: ' + date_string); } // .precision 將會影響 function wikidata_datavalue() @ // CeL.application.net.wiki.data date_value.precision = 'month'; return date_value; } // ------------------------------------------------ if (isNaN(minute_offset) && !isNaN(tmp = get_minute_offset(date_string))) { minute_offset = tmp; // 留下此 pattern 在 match 時會出錯。 date_string = date_string.replace(UTC_PATTERN, '').trim(); } // TODO: // e.g., '10.12', '10/12' // e.g., '10/12, 2001' // e.g., '10 12, 2001' // e.g., '2001 10 12' if (matched = date_string.match(PATTERN_YEAR_ONLY)) { // 僅有 xxx/1xxx/2xxx 年(year) 時。 precision = matched[3] === '百' ? 'century' : matched[3] === '千' ? 'millennium' // 注意:這邊不會檢查如"2016年代"之合理性(應當為"2010年代") : date_string.includes('年代') ? 'decade' : 'year'; date_string = (matched[2] || matched[1]).replace(/^[−‐前]/, '-000'); if (period_end) { if (matched[3]) { // 將於後面才作位數處理。 ++date_string; } else { // 作位數處理。 matched = date_string.includes('00'); if (!++date_string) { // 預防 前1年 → 0年。 date_string = no_year_0 ? '0001' : '0000'; } else if (matched && (date_string = '00' + date_string).length < 4) { date_string = '0' + date_string; } } // 已處理過 period_end,因此除去此 flag。 period_end = false; } if (matched[3]) { date_string = date_string // 轉換到正確的年份。 * (precision === 'century' ? 100 : 1000); // 作位數處理。 if (0 < date_string && date_string < 1000) { date_string = '0' + date_string; } else if (date_string === 0) { date_string = '000'; } } // 添加月份以利parse。 date_string += '/1'; } else { // 依照中文之習慣,日期 + 時間中間可不加空格。 date_string = date_string.replace(/日(\d)/, '日 $1'); } if (false && // 速度似乎差不多。 (date_data = date_string.match(/^(-?\d{1,4})\/(\d{1,2})\/(\d{1,2})$/))) { // library_namespace.debug('輸入格式: 日期', 2); date_data.shift(); } else if ((date_data = date_string .match(String_to_Date_default_parser.date_first)) && isNaN(date_data[0])) { // library_namespace.debug('輸入格式: 日期 (+ 時間)', 2); date_data.shift(); } else if (date_data = date_string .match(String_to_Date_default_parser.time_first)) { // library_namespace.debug('輸入格式: 時間 (+ 日期): 未匹配或僅有一數字', 2); // [ 1, 2, 3, 4, 5, 6, 7, 8 ] // → [ 6, 7, 8, 1, 2, 3, 4, 5 ] date_data.shift(); date_data.unshift(date_data[5], date_data[6], date_data[7]); date_data.length = 8; } else { library_namespace.debug('無法 parse: [' + date_string + ']', 2, 'String_to_Date_default_parser'); return; } if (!precision) { // 這邊僅處理年以下的單位。 date_data.some(function(value, index) { if (!value) { // value should be undefined. if (index > 0) { precision = index_precision[index + 2]; } return true; } }); } // ---------------------------------------------------- // date_data: index: [ year, month, month_day (Day of // the month), hour, minute, second, milliseconds, Ante // meridiem or Post meridiem ] library_namespace.debug(date_data.join('<br />'), 2, 'String_to_Date_default_parser'); tmp = date_data.length === 8 && date_data.pop(); if (tmp === 'P' || tmp === 'p') { // is PM (else: AM or 24-hour format) date_data[3] = 12 + (date_data[3] | 0); } var year = +date_data[0]; if (isNaN(year) && /^前/.test(date_data[0])) { year = -date_data[0].slice(1); } // fix browser Date.parse() bug for BCE date. else if (year > 0 && PATTERN_BCE.test(date_string)) { year = -year; if (!('no_year_0' in options)) { // default: no year 0 no_year_0 = true; } } // 確定正確年份: 若無 year 0 則所有負的年份皆 +1, // 轉成<a // href="http://en.wikipedia.org/wiki/Astronomical_year_numbering" // accessdate="2013/2/11 15:40" title="Astronomical year // numbering">天文年號</a>。 // (BCE) -1 → 0, -2 → -1, -3 → -2, ... if (year < 0) { if (no_year_0) { year++; } } else if (year < 100 && date_data[0].length < 3 // year padding: 0–99 的年份會加上此年份。 && (tmp = isNaN(options.year_padding) // ? String_to_Date_default_parser.year_padding : options.year_padding)) { year += tmp; } date_data[0] = year; if (period_end) { tmp = date_data.length; // 由小至大,將第一個有指定的加一即可。 while (tmp-- > 0) { // IE 中,String.prototype.match() 未成功時會回傳 '', // 而 !isNaN('')===true,因此無法正確判別。 if (!isNaN(date_data[tmp]) && date_data[tmp] !== '') { date_data[tmp]++; break; } } year = date_data[0]; } if (!(0 < (date_data[2] = +date_data[2]))) { date_data[2] = 1; } if (typeof options.post_process === 'function') { options.post_process(date_data); } year = +year || 0; // time zone. tmp = +date_data[4] || 0; var base_on_UTC = !isNaN(minute_offset) && minute_offset !== DEFAULT_TIME_ZONE; // 若是未設定,則當作 local time zone。 if (base_on_UTC) { // 否則基於本機當前的時區來調整成基於 UTC 之 `minute_offset` // local time + .getTimezoneOffset() = UTC tmp -= present_local_minute_offset + minute_offset; } if (year < 100 && year >= 0) { // 僅使用 new Date(0) 的話,會含入 timezone offset (.getTimezoneOffset)。 // 因此得使用 new Date(0, 0)。 date_value = new Date(0, 0); // 先設定小單位,再設定大單位:設定小單位時會影響到大單位。反之不然。 // 下兩者得到的值不同。 // (d=new Date(0, 0)).setFullYear(0, 0, -1, 0, 480, 0, 0); // d.toISOString() // // (d=new Date(0, 0)).setHours(0, 480, 0, 0); // d.setFullYear(0, 0, -1);d.toISOString() date_value.setHours(+date_data[3] || 0, tmp, +date_data[5] || 0, +date_data[6] || 0); date_value.setFullYear( // new Date(10, ..) === new Date(1910, ..) year, date_data[1] ? date_data[1] - 1 : 0, date_data[2]); } else { date_value = new Date(year, date_data[1] ? date_data[1] - 1 : 0, date_data[2], +date_data[3] || 0, tmp, +date_data[5] || 0, +date_data[6] || 0); } if (base_on_UTC && date_value.getTimezoneOffset() !== present_local_minute_offset) { /** * 當基於本機當前的時區來調整成UTC時間時,若是 time zone 和預設的 * `present_local_minute_offset` 不同,就必須在以 new Date() 設定時間後,才調整 time * zone。 */ date_value.setMinutes(date_value.getMinutes() + present_local_minute_offset - date_value.getTimezoneOffset()); } // 測試僅輸入時刻的情況。e.g., '7時' if (options.near && date_value.getFullYear() === 0 && date_value - time_boundary > 0) { // 判別未輸入時預設年份設對了沒:以最接近 options.near 的為基準。 tmp = is_Date(options.near) ? options.near : new Date; date_string = tmp.getFullYear(); matched = new Date(date_value.getTime()); date_value.setFullYear(date_string); matched.setFullYear(date_value - tmp > 0 ? date_string - 1 : date_string + 1); if (date_value - tmp > 0 && date_value - tmp > tmp - matched || date_value - tmp < 0 && date_value - tmp < tmp - matched) { date_value = matched; } } if (precision) { date_value.precision = precision; } return date_value; } // 0–99 的年份會加上此年份 (1900)。 String_to_Date_default_parser.year_padding = (new Date(0, 0, 1)) .getFullYear(); String_to_Date.default_parser = String_to_Date_default_parser; // date_string.match(String_to_Date.parser_PATTERN) // === [, parser name, date string ] // e.g., "Âm lịch" String_to_Date.parser_PATTERN = /^\s*(?:([^:]+):)?\s*(.+)/i; String_to_Date.parser = { Julian : Julian_String_to_Date, // Common Era / Before the Common Era, CE / BCE. 公元/西元. CE : function(date_string, minute_offset, options) { if (!library_namespace.is_Object(options)) { options = Object.create(null); } if (!('no_year_0' in options)) { options.no_year_0 = true; } var date_value = String_to_Date_default_parser(date_string, minute_offset, options); return date_value - Gregorian_reform_of(options.reform) < 0 // ? Julian_String_to_Date(date_string, minute_offset, options) : date_value; }, // <a href="http://php.net/manual/en/function.date.php" // accessdate="2012/3/23 20:51">PHP: date - Manual</a> PHP : function() { // TODO throw new Error('String_to_Date.parser.PHP: Not Yet Implemented!'); }, // <a href="http://www.freebsd.org/cgi/man.cgi?query=strftime" // accessdate="2012/3/23 20:59">strftime</a>, // <a href="http://hacks.bluesmoon.info/strftime/" accessdate="2012/3/23 // 21:9">strftime: strftime for Javascript</a> strftime : function() { // TODO throw new Error( 'String_to_Date.parser.strftime: Not Yet Implemented!'); } }; // 時區縮寫。 // <a href="http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations" // accessdate="2012/12/2 13:0" title="List of time zone abbreviations">time // zone abbreviations</a> and offset in hour. // TODO: Daylight Saving Time (DST). // @see CeL.application.locale.time_zone_of_language() String_to_Date.zone = { // UTC+08:00 // China Standard Time CST : 8, Z中國 : 8, JST : 9, Z日本 : 9, EST : -5, PST : -8, // Greenwich Mean Time GMT : 0, // Coordinated Universal Time UTC : 0 }; _// JSDT:_module_ .String_to_Date = String_to_Date; // --------------------------------------------------------- /** * test if the year is leap year. has year 0!<br /> * * @param {Integer}year * @param type * calendar type: true: use Julian calendar, false: use Gregorian * calendar, 'CE': use CE * * @returns {Boolean} */ function is_leap_year(year, type) { if (type === 'CE') { if (reform_year < year) { type = false; } else if (year < 0) { year++; } } // Julian calendar return type ? year % 4 === 0 // Gregorian calendar : year % 400 === 0 || year % 100 !== 0 && year % 4 === 0; } _.is_leap_year = is_leap_year; /** * test if in the year, Gregorian calendar and Julian calendar have * different intercalation. * * @param {Integer}year * @returns {Boolean} 當年 Julian 與 UTC 為不同閏年規定: Gregorian 當年沒有閏日,但 Julian 有。 */ function is_different_leap_year(year) { return year % 100 === 0 && year % 400 !== 0; } _.is_different_leap_year = is_different_leap_year; /** * 計算 Gregorian 與 Julian 的日數差距。 the secular difference between the two * calendars.<br /> * 會將 date_data: Julian → Gregorian. * * @param {Array}date_data * Julian date [year, month, date] * * @returns {Number} Julian → Gregorian 時,需要減去的日數。(除少數特例外,即 Gregorian → * Julian 時,需要加上的日數。) * * @see https://en.wikipedia.org/wiki/Gregorian_calendar#Difference_between_Gregorian_and_Julian_calendar_dates */ function Julian_shift_days(date_data) { var year = +date_data[0]; // 測試是否為有差異的當年 if (is_different_leap_year(year) // 測試是否為閏日。 // 閏日前(before Julian calendar leap day)還要算是上一階段。 && date_data[1] < 3) { year--; } // 計算 Gregorian 與 Julian 的 different days。 // 2: 0年時,差了2日。 // -701: 8, -700: 7; -601: 7, -600: 6; 99: 2, 100: 1; year = 2 + Math.floor(year / 400) - Math.floor(year / 100); // 這演算法對差異大至 31+28 日的時段不適用。 date_data[2] -= year; return year; } _.Julian_shift_days = Julian_shift_days; /** * parse proleptic Julian calendar date_string and return the new Date.<br /> * * 借用系統內建的計時機制。其他 arguments 見 String_to_Date_default_parser()。 * * @param {String}date_string * Julian calendar date string. * * @returns {Date} new Date * * @see http://en.wikipedia.org/wiki/Old_Style_and_New_Style_dates * @see http://eclipse.gsfc.nasa.gov/SEhelp/julian.html */ function Julian_String_to_Date(date_string, minute_offset, options) { if (!library_namespace.is_Object(options)) { options = Object.create(null); } options.post_process = Julian_shift_days; return String_to_Date_default_parser(date_string, minute_offset, options); } // ----------------------------------------------------------------------------------------------------------------------------------------------------------// function parse_English_date(date) { date = date.trim().replace(/.+\[(?:\d{1,2}|note \d+)\]$/i, ''); var accuracy = date.match(/^(?:before|after)[\s ](.+)$/i), matched; if (accuracy) { date = accuracy[1].trim(); accuracy = accuracy[0]; } if (accuracy = date.match(/^ca?.(.+)$/)) { date = accuracy[1].trim(); accuracy = accuracy[0]; } if (/^[a-z]{3,9}\s+-?\d+$/i.test(date)) { date = '1 ' + date; accuracy = date; } if (date.includes('?')) { accuracy = date; date = date.replace(/\?/g, ''); } if (!isNaN(date) || /^\d+\/\d+$/.test(date)) { accuracy = date; } else if (!isNaN(matched = Date.parse(date))) { date = new Date(matched + String_to_Date.default_offset * ONE_MINUTE_LENGTH_VALUE).toISOString() // .match(/^\d+-\d+-\d+/)[0].replace(/^0+/, '').replace(/(\d)-0*/g, '$1\/'); } else { library_namespace.warn(date); return; } return [ date, accuracy ]; } _.parse_English_date = parse_English_date; // ----------------------------------------------------------------------------------------------------------------------------------------------------------// /** * 顯示格式化日期時間 string:依照指定格式輸出日期與時間。<br /> * TODO:<br /> * 各 locale 有不同 format 與 time zone offset. * * @param {Date}date_value * 要轉換的 date, TODO? 值過小時當作時間, <0 轉成當下時間. * @param {Object|String|Function}options * 選擇性功能: {<br /> * {String|Function}parser: 格式字串分析器 'strftime',<br /> * {String}format: 格式字串 '%Y/%m/%d %H:%M:%S.%f',<br /> * {String}locale: 地區語系設定<br /> } * * @returns {String} 依照指定格式格式化後輸出的日期與時間. * * @see<br /> * <a href="http://blog.csdn.net/xzknet/article/details/2278101" * accessdate="2012/3/24 15:11" title="如何使用Javascript格式化日期显示 - * 虫二的专栏~~在路上~~~ - 博客频道 - CSDN.NET">JsJava中提供了專用的類,專門對日期進行指定格式的字符串輸出</a>,<br /> * <a href="http://www.merlyn.demon.co.uk/js-date8.htm" * accessdate="2012/3/25 1:42">Merlyn - JSDT 8 : Enhancing the Object - * J R Stockton</a>,<br /> * U.S. Naval Observatory <a * href="http://aa.usno.navy.mil/data/docs/JulianDate.php" * accessdate="2012/3/25 1:42">Julian Date Converter</a><br /> * ISO 8601:2004(E) * * @_memberOf _module_ */ function Date_to_String(date_value, options) { // 前置處理。 if (typeof options === 'string') { options = options in Date_to_String.parser ? { parser : Date_to_String.parser[options] } : { format : options }; } else if (typeof options === 'function') { options = { parser : options }; } else if (!library_namespace.is_Object(options)) { options = Object.create(null); } if (false) { if (options.parser && !library_namespace.is_Function(options.parser) && !library_namespace .is_Function(String_to_Date.parser[options.parser])) { library_namespace.warn('Date_to_String: 無法判斷 parser [' + options.parser + ']!'); } } // if (!date_value) date_value = new Date; if (date_value && !is_Date(date_value) // String_to_Date() 會幫忙做正規化。 ? String_to_Date(date_value) : date_value) { return (library_namespace.is_Function(options.parser) ? options.parser : Date_to_String.parser[options.parser] || Date_to_String.default_parser)(date_value, options.format, library_namespace.gettext.to_standard // ? library_namespace.gettext.to_standard(options.locale) : options.locale, options); } library_namespace.warn('Date_to_String: 無法判斷 date value [' + date_value + ']!'); } // default parser. Date_to_String.default_parser = strftime; Date_to_String.parser = { // <a href="http://php.net/manual/en/function.date.php" // accessdate="2012/3/23 20:51">PHP: date - Manual</a> PHP : function(date_value, format, locale) { // TODO throw new Error('Date_to_String.parser.PHP: Not Yet Implemented!'); }, // ISO 8601:2004(E) ISO8601 : function(date_value, format, locale) { // TODO throw new Error( 'Date_to_String.parser.ISO8601: Not Yet Implemented!'); }, // .NET standard format string (standard date and time format string) <a // href="http://msdn.microsoft.com/zh-tw/library/az4se3k1.aspx" // accessdate="2012/3/24 17:43">標準日期和時間格式字串</a> SFS : function(date_value, format, locale) { // TODO throw new Error('Date_to_String.parser.SFS: Not Yet Implemented!'); }, // <a href="http://www.freebsd.org/cgi/man.cgi?query=strftime" // accessdate="2012/3/23 20:59">strftime</a>, // <a href="http://hacks.bluesmoon.info/strftime/" accessdate="2012/3/23 // 21:9">strftime: strftime for Javascript</a> strftime : strftime, Gregorian : Date_to_Gregorian, Julian : Date_to_Julian, // Common Era / Before the Common Era, CE / BCE. CE : function(date_value, format, locale, options) { // 前置處理。 if (!library_namespace.is_Object(options)) { options = Object.create(null); } if (!('no_year_0' in options)) { options.no_year_0 = true; } return (date_value - Gregorian_reform_of(options.reform) < 0 // ? Date_to_Julian : Date_to_Gregorian)(date_value, format, locale, options); }, // Turn to RFC 822 date-time // <a // href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toUTCString" // accessdate="2012/3/24 8:5" title="toUTCString - MDN">The most common // return value is a RFC-1123 formatted date stamp, which is a slightly // updated version of RFC-822 date stamps.</a> // Date_to_RFC822[generateCode.dLK]='String_to_Date'; RFC822 : function(date_value) { // e.g., "Wed, 14 Jun 2017 07:00:00 GMT" return date_value.toUTCString().replace(/UTC/gi, 'GMT'); } }; _// JSDT:_module_ .Date_to_String = Date_to_String; // --------------------------------------------------------- /** * 依照指定 strftime 格式輸出日期與時間。 * * @param {Date}date_value * 要格式化的日期。 * @param {String}format * 輸出的格式字串。 * @param {String}locale * 輸出的地區語系設定。 * @param {Object}options * 選擇性功能。 * * @returns {String} 依照指定格式輸出的日期與時間。 * * @see<br /> * <a href="http://www.freebsd.org/cgi/man.cgi?query=strftime" * accessdate="2012/3/23 20:59">strftime</a>,<br /> * <a href="http://hacks.bluesmoon.info/strftime/" * accessdate="2012/3/23 21:9">strftime: strftime for Javascript</a>, */ function strftime(date_value, format, locale, options) { // 前置處理。 if (!library_namespace.is_Object(options)) { options = Object.create(null); } var original_Date = options.original_Date || date_value, /** * 支援的 conversion specifications (轉換規格). */ conversion = strftime.conversion[locale] || strftime.conversion[strftime.null_domain], /** * 所須搜尋的 conversion specifications (轉換規格) pattern.<br /> * .search_pattern */ search = strftime.search[locale] || strftime.search[strftime.null_domain]; // 也可以使用 options.zone 設定要轉換成的時區(timezone)。 if (isNaN(options.offset) && !isNaN(options.zone)) { options.offset = options.zone * 60; } // console.log(options); // library_namespace.debug('options.offset = ' + options.offset, 0); // to this minute offset. UTC+8: 8 * 60 = +480 // or using options.minute_offset? if (!isNaN(options.offset)) { date_value = new Date(date_value.getTime() + ONE_MINUTE_LENGTH_VALUE * (options.offset - String_to_Date.default_offset)); } function convertor(s) { return s.repla