Home  >  Article  >  Web Front-end  >  Detailed analysis of JavaScript custom date formatting function_javascript skills

Detailed analysis of JavaScript custom date formatting function_javascript skills

WBOY
WBOYOriginal
2016-05-16 17:03:521224browse

One of the more common ways we extend JavaScript is to extend Date.prototype. Because we know that the Date class only provides several methods for obtaining date elements, such as getDate(), getMinute()... but there is no formatting method for converting to a specific string. Therefore, we use these subtle methods to encapsulate and combine the date string form we want. Generally speaking, the formatting function can be defined on the prototype of the Date object, or it can be written as a separate method. The operation of defining the prototype method is such as Date.prototype.format = function(date){......}. When using it, just use new Date().format(YYYY:MM:DD), as if it is the native method of the Date object. But defining prototype methods has the disadvantage of "invading" JS prototypes. This issue must be considered when designing the API. My suggestion is that users make decisions based on their own judgment, but the calling method is different and does not affect the logic of the process.

The following example is a JavaScript date formatting function written as an independent function, an independent format function. Returning to the knowledge point of formatting, we examine how it is implemented and what principles are used. Although traditional string splicing such as indexOf() substr() can be implemented, it is obviously not only inefficient, but also has lengthy code. It is still a suitable method to introduce regular expressions. First write the string regular expression and then perform hit matching on the result. Let’s first look at the example from Steven Levithan:

Copy the code The code is as follows:

/**
 * Date Format 1.2.3
 * @credit Steven Levithan Includes enhancements by Scott Trenda and Kris Kowal
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */
dateFormat = (function(){
// Regular notes, 1. token, (?:) indicates non-capturing grouping; /1 back reference (Thinking: Can {1,2} have the same meaning as /1?); According to the meaning here [LloSZ] means that any character in the brackets can be matched. It is very simple, but I don’t understand it yet /L|l|o|S The role of |Z/ in parsing dates; the last two groups of "or" match the quotation marks and the content within the quotation marks (it doesn't matter whether there are double quotation marks or single quotation marks).
var token = /d{1,4}|m{ 1,4}|yy(?:yy)?|([HhMsTt])/1?|[LloSZ]|"[^"]*"|'[^']*'/g,
// 2 , timezone, [PMCEA][SDP] consumes two characters; the reg is a non-capturing group, which can speed up the regularization speed. :Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[- ]/d{4})?)/b/g,
timezoneClip = /[^- /dA-Z]/g,

// Less than two padding characters, or the number of digits can be specified
pad = function (val, len){
val = String(val);
len = len || 2;
while (val.length < len) val = "0" val;
return val;
};
/ / Why return a function? Because the variables explained above are all constants, the parameters returned below are the functions that are actually executed. This is achieved by writing closures, which can speed up
// Regexes and supporting functions are cached through closure
// Parameter description: date: Date parsed date or new date; mask:String template for formatted date; utc:Stirng optional UTC.
return function (date, mask, utc) {
var i18n = dateFormat.i18n;
var masks = dateFormat.masks;
// You can't provide utc if you skip other args ( use the "UTC:" mask prefix)
                                                                          // If there is only one parameter, and the parameter is a string that does not contain numbers, this parameter is regarded as a mask. date is generated by new Date in the next if, then date is the current date.
                                                                                                                                                                                          if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !//d/.test(date)) {
           mask = date;
date = undefined;
}

// Passing date through Date applies Date.parse, if necessary
date = date ? new Date(date) : new Date;
if (isNaN(date)) throw SyntaxError("invalid date");
// Make it clear what the mask is by judging multiple situations, regardless of how it was specified previously. Watch out for the || trick.
         mask = String(masks[mask] || mask || masks["default"]); == "UTC:") {
          mask = mask.slice(4);
         utc = true; Note that members of methods can also be returned via JS literal indexes.
var _ = utc ? "getUTC" : "get",
d = date[_ "Date"](),
D = date[_ "Day"](),
m = date[_ "Month"](),
       y = date[_ "FullYear"](),
          H = date[_ "Hours"](),
                 M = date[_ " Minutes"](),
s = date[_ "Seconds"](),
L = date[_ "Milliseconds"](),
o = utc ? 0 : date.getTimezoneOffset() ,
flags = {
d: d,
dd: pad(d),
ddd: i18n.dayNames[D],
                  dddd: i18n.dayNames[D 7],/ / Bit width: 7, see dateFormat.dayNames.
m: m 1, // Starting from 0 month
mm: pad(m 1),
mmm: i18n.monthNames[m],
mmmm: i18n.monthNames[m 12] , // Bit width: 12, see dateFormat.monthNames
yy: String(y).slice(2),// Usage of string slice()
yyyy: y,
h: H % 12 || 12, // h represents the 12-hour format, h is divided by 12 (because of the twelve decimal system), and the remainder is the 12-hour format.
hh: pad(H % 12 || 12),
H: H,
HH: pad(H),
M: M,
MM: pad(M),
s: s,
ss: pad(s),
l: pad(L, 3), // Max,999ms
L: pad(L > 99 ? Math.round(L / 10) : L),
                                                                                                      ",
T: H < 12 ? "A" : "P",
TT: H < 12 ? "AM" : "PM",
// This step requires timezone. Deal with it.
                                                                                                                                                                                                                                                         . / If not, [""].pop() returns the empty character
     Z:  utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace (TimezoneClip, ""),
// 4-bit TimezoneOffset
O: (O & GT; 0? "- "") pad (math.floor (math.abs (o) / 60) * S : ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
    };
                                                                                                                                                                                                                           That’s it!
                // $0.slice(1, $0.length - 1);? What does it mean? ;
      }); ——How to deal with dates!

In the date string template, we agree to use meaningful symbols such as yyyy/mm/dd to represent a certain element in the date, such as y means year, m means month, and d means day. If a day is in uppercase, pay attention to distinguish it. The uppercase M stands for minutes, and the lowercase m stands for month. In short, this is a convention that we have artificially standardized, which is the so-called "mask" of the above code. According to this convention, input the parameters of the format mode you want to format, and then the date type value can be output as a string that can be printed. As for the date parsing process, first follow all the requirements of Mask to obtain each element of the date one by one (getDate(), getMinute()...can be obtained quickly), and then follow the actual conditions of Mask, that is, Mask The .replace(regular, element) method performs replacement between string templates and elements. The replacement process is still based on the flag as a sign to match the lookup table one by one. As for the regular part, the key is to understand the process of token and replace() functions. Take the above code comments to understand the internal details.

Wouldn’t it be tiring to enter a long Mask string every time? We can reduce our workload by defining constants:




Copy the code

The code is as follows:


dateFormat.masks = {
    "default":      "ddd mmm dd yyyy HH:MM:ss",
    shortDate:      "m/d/yy",
    shortDate2:     "yy/m/d/h:MM:ss",
    mediumDate:     "mmm d, yyyy",
    longDate:       "mmmm d, yyyy",
    fullDate:       "dddd, mmmm d, yyyy",
    shortTime:      "h:MM TT",
    mediumTime:     "h:MM:ss TT",
    longTime:       "h:MM:ss TT Z",
    isoDate:        "yyyy-mm-dd",
    isoTime:        "HH:MM:ss",
    isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
    isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
    // 加入中国类型的日期 @Edit 2010.8.11
    ,ChineseDate   :'yyyy年mm月dd日 HH时MM分'
}

dateFormat.i18n = {
    dayNames: [
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
        "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
    ],
    monthNames: [
        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
        "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
    ]
};

Steve 的 dateFormat 足可以完成大多数日期转化的任务,不过在茫茫代码中,我们找到了更优的解法,不出20行代码,把正则运用得收放自如,就是来自月影前辈的JS !
复制代码 代码如下:

Date.prototype.format = function(format) //author: meizz
{
  var o = {
    "M " : this.getMonth() 1, //month
    "d " : this.getDate(),    //day
    "h " : this.getHours(),   //hour
    "m " : this.getMinutes(), //minute
    "s " : this.getSeconds(), //second
    "q " : Math.floor((this.getMonth() 3)/3),  //quarter
    "S" : this.getMilliseconds() //millisecond
  }
  if(/(y )/.test(format)) format=format.replace(RegExp.$1,
    (this.getFullYear() "").substr(4 - RegExp.$1.length));
  for(var k in o)if(new RegExp("(" k ")").test(format))
    format = format.replace(RegExp.$1,
      RegExp.$1.length==1 ? o[k] :
        ("00" o[k]).substr(("" o[k]).length));
  return format;
}
alert(new Date().format("yyyy-MM-dd hh:mm:ss"));

The principle is similar to Steve's method, but the code is more condensed and combines skill and comprehensiveness. Starting from line 12 of the source code, the test() method not only has the basic function of detecting whether it matches, but also actually memorizes the matching results and generates the RegExp.$1 result group to process the year (at first I thought test() was efficient and will not produce results, which is not the case). Then, use new RegExp to create an instance of the regular expression in the form of a string, which is another clever thing - because it is directly connected to the hash table of o! Then according to the law, first test whether there is a match, and if so, replace it.

In addition, ("00" o[k]).substr(String(o[k]).length) in the code is also interesting. What does it mean to add two in front? The original purpose is to get the last two digits of the array. This is a technique to comprehensively utilize the substr() method. The first parameter of substr is the index to start interception. If the second parameter index is not specified, the string will be retained to the end (str.length). So, how many bits do we add in advance, and the original fixed string length remains unchanged (String(o[k].length)), then how many bits are left. (p.s "00" is equivalent to a placeholder, and it can also be replaced by other string "XX" without any difference)

Still finding this code difficult? We tried to rewrite Yueying's functions into more readable code. The principles are consistent but there are not so many tricks. I believe this can save everyone's time. It is not too late to go back and look at Yueying's code.

Copy code The code is as follows:

date = {
format: function(date, format){
date = new Date(date); // force con.
date = {
year : date.getFullYear()
,month : date.getMonth() 1 // Month , months start from zero
,day : date.getDate()
,hour : date.getHours()
,minute : date.getMinutes()
,second : date.getSeconds()
,milute : date.getMilliseconds()
};
var
match
,reg = /(y )|(Y )|(M )|d |h |m |s |u /g;
while((match = reg.exec(format)) != null){
match = match[0];
if(/y/i.test(match)) {
format = format.replace(match, date.year);
}
if(match.indexOf('M') != -1){
format = format.replace(match , date.month);
}
if(match.indexOf('d') != -1){
format = format.replace(match, date.day);
}  
if(match.indexOf('h') != -1){
format = format.replace(match, date.hour);
}
if(match.indexOf('m' ) != -1){
format = format.replace(match, date.minute);
} }
if(match.indexOf('s') != -1){
format = format.replace(match, date.second);
}
if(match.indexOf('u') != -1){
format = format.replace(match, date.milute) ;
}                                                                                                                                                       
I found the date formatting code from ext 4.0. How to convert a string into a js standard date? See how the new ext is done?



Copy code

The code is as follows:


/**
* Format the date according to a specific format pattern.
* Parse a value into a formatted date using the specified format pattern.
* @param {String/Date} value The value to be formatted (the string must comply with the format requirements of JavaScript date objects, see parse())The value to format (Strings must conform to the format expected by the javascript
* Date object's parse() method)
* @param {String} format (optional) Any date format string. (Defaults to 'm/d/Y') (optional) Any valid date format string (defaults to 'm/d/Y')
* @return {String} Formatted string. The formatted date string
*/
date: function(v, format) {
if (!v) {
return "";
}
if (!Ext.isDate(v)) {
         v = new Date(Date.parse(v));
}


The date constructor can also determine the date by calculating the number of milliseconds since 1970? ——Indeed, this is also possible, ——In other words, by drawing inferences from one example, this question illustrates that the smallest unit of js date is milliseconds.

Final version:


/**
* Date formatting. See the blog article for details: http://blog.csdn.net/zhangxin09/archive/2011/01/01/6111294.aspx
* e.g: new Date().format("yyyy-MM-dd hh:mm :ss")
* @param {String} format
* @return {String}
*/
Date.prototype.format = function (format) {
var $1, o = {
"M ": this.getMonth() 1, // The month starts from 0
"d ": this.getDate(), // Date
"h ": this.getHours(), // Hours
"m ": this.getMinutes(), // Minutes
"s ": this.getSeconds(), // Seconds
" " Quarter
"q ": Math.floor((this.getMonth() 3) / 3),
"S": this .getMilliseconds() // Thousand seconds
};
var key, value;

if (/(y)/.test(format)) { $1 = RegExp.$1,

format = format.replace($1, String(this.getFullYear()).substr(4 - $1));
}

for (key in o) { // If this parameter is not specified, the substring will continue to the end of stringvar.

If (new RegExp("(" key ")").test(format)) {

$1 = RegExp.$1,
value = String(o[key]),
value = $1.length == 1 ? value : ("00" value).substr(value.length),
format = format.replace($1, value);
}
}
return format ;
}


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn