jQuery DataTables:有没有一种方法可以自动检测日期格式?

时间:2019-04-01 13:02:53

标签: javascript jquery datatables momentjs

我已经启动并运行了一个数据表,目前我已经通过添加

手动定义了日期格式
$.fn.dataTable.moment("DD.MM.YYYY");

在定义数据表本身之前:

var myTable = $('#authors').DataTable({
    "paging": false,
    "ordering": true,
    "order": [2, "desc"],
    "info": false,
    "stateSave": true,
    "responsive": true,
    "columnDefs": [
        { targets: "noSort", orderable: false }
    ]       
});

如您所见,我们当前正在使用德语日期格式。但是有可能在那年晚些时候我们不得不使用其他格式。

有没有一种方法可以自动检测给定日期列的值,以便我可以对该列进行正确排序?还是我总是必须手动定义日期格式?

我想要的是该行的动态版本

$.fn.dataTable.moment("DD.MM.YYYY");

它应该检测到“哦,该列中的值为'29 .04.2019',这是德语日期格式,定义为dd.mm.yyyy”,并使用该格式进行进一步的排序。

或者,如果值改为“ 04/29/2019”,则应将其识别为美国日期格式,并使用“ mm / dd / yyyy”进行排序。

目前,我不知道该应用程序将支持多少种不同的日期格式。我想它将是5个或更多。但是在一个表中,将仅使用一种格式。

6 个答案:

答案 0 :(得分:4)

建议您将格式数组传递给$.fn.dataTable.moment(...),但这只有当且永远不会发生数据匹配数组中多个格式的情况时,此方法才有效。除非您能保证这一点,否则传递格式数组不是解决方案。

您从DD.MM.YYYYMM/DD/YYYY的示例开始。日期将匹配一种格式或另一种格式,但不能同时匹配两种格式,因为如果日期带有句点定界符,则比第一种格式匹配,但不匹配第二种;如果带有斜杠定界符,则它与第二种格式匹配,但与第一种格式不匹配。但是,通常来说,如果您的约会日期不是美国或德国以外的其他地方,您就会遇到模棱两可的情况。 Matt Johnson提到的日期例如“ 01/04/2019”,可以适合MM/DD/YYYY格式并被解释为“ January 4th 2019”,也可以适合DD/MM/YYYY格式并被解释为“ 2019年4月1日”。

如果您可以使用DD/MM/YYYYMM/DD/YYYY格式的日期并致电$.fn.dataTable.moment(["DD/MM/YYYY", "MM/DD/YYYY"]),则有时会得到不正确的结果。问题在于,实现您要调用的功能的插件会单独查看每个单元格

表1

假设一个表打算使用DD/MM/YYYY格式的日期,并带有以下单元格:

  1. 2019/2/21
  2. 1/4/2019
  3. 24/12/2019

表2

假设一个表打算使用MM/DD/YYYY格式的日期,并带有以下单元格:

  1. 2/21/2019
  2. 4/1/2019
  3. 2019/12/24

两个表实际上包含相同的日期。它们只是代表不同。

假设您使用$.fn.dataTable.moment(["DD/MM/YYYY", "MM/DD/YYYY"])配置了表。表1将被正确解释。但是,表2中的第2行将无法正确解释。日期4/1/2019确实适合数组(DD/MM/YYYY)中的第一种格式,这就是moment对其解释的方式。多少个其他单元格不能容纳DD/MM/YYYY并不重要,因为调用moment的插件不会进行统计分析。它隔离地查看每个单元。这是相关的code(删除了一些空白行):

$.fn.dataTable.moment = function ( format, locale, reverseEmpties ) {
    var types = $.fn.dataTable.ext.type;

    // Add type detection
    types.detect.unshift( function ( d ) {
        if ( d ) {
            // Strip HTML tags and newline characters if possible
            if ( d.replace ) {
                d = d.replace(/(<.*?>)|(\r?\n|\r)/g, '');
            }

            // Strip out surrounding white space
            d = $.trim( d );
        }

        // Null and empty values are acceptable
        if ( d === '' || d === null ) {
            return 'moment-'+format;
        }

        return moment( d, format, locale, true ).isValid() ?
            'moment-'+format :
            null;
    } );

    // Add sorting method - use an integer for the sorting
    types.order[ 'moment-'+format+'-pre' ] = function ( d ) {
        if ( d ) {
            // Strip HTML tags and newline characters if possible
            if ( d.replace ) {
                d = d.replace(/(<.*?>)|(\r?\n|\r)/g, '');
            }

            // Strip out surrounding white space
            d = $.trim( d );
        }

        return !moment(d, format, locale, true).isValid() ?
            (reverseEmpties ? -Infinity : Infinity) :
            parseInt( moment( d, format, locale, true ).format( 'x' ), 10 );
    };
};

您可以翻转参数并调用$.fn.dataTable.moment(["MM/DD/YYYY", "DD/MM/YYYY"])。现在第二张表可以了,但是在第一张表中也会发生同样的问题。


好,那怎么办?

如果后端恰好已经包含UTC时间戳,那么我只是将这些时间戳发送到前端,而不是发送本地化的值。在渲染包含日期的单元格的阶段,我将让前端将UTC日期转换为对用户有意义的格式。 Datatable会根据UTC值进行排序,可以毫无歧义地进行比较。

如果后端未将其日期存储为UTC时间戳,我将对其进行重新设计,使其能够正确执行,然后执行我在上一段中所述的操作。

否则,在数据表尝试呈现和排序之前,可能有一种 在前端对表进行统计分析的方法。因此,您可以发现使用哪种格式,然后将其提供给数据表。但是,这对我来说仍然很脆弱。如果表使用服务器端协议,则一次仅一小部分数据可用。如果仅对服务器的第一个响应进行分析,则覆盖表后半部分的后一个响应可能会反驳初始假设。此外,在某些情况下,数据表中的所有日期可能都不明确。在大型且未经过滤的数据集上,这种情况不太可能发生,但是一旦允许用户过滤数据集以仅显示子集,他们就可能以导致特定子集中的所有日期不明确的方式对其进行过滤。我不会部署一个应用程序,希望它永远不会发生。

答案 1 :(得分:1)

假设您已经使用date sorting plug-in,那么只要指定了所需的格式,您就不必担心任何事情:

$.fn.dataTable.moment(['MM/DD/YYYY', 'DD-MM-YYYY', 'DD-MMM-YYYY']);

必须遵守插件所依赖的moment.js规定的rules

在下面,您可能会找到有效的演示

//table source
const srcData = [
	{name: 'Til Schweiger', date: '19-12-1963'},
	{name: 'Jan Joseph Liefers', date: '08/08/1964'},
	{name: 'Moritz Bleibtreu', date: '13-Aug-1971'},
	{name: 'Thomas Jahn', date: '07/08/1965'}
		
];

$.fn.dataTable.moment(['MM/DD/YYYY', 'DD-MM-YYYY', 'DD-MMM-YYYY']);

//DataTable init
const dataTable = $('#mytable').DataTable({
	dom: 't',
	data: srcData,
	columns: [
		{title: 'name', data: 'name'},
		{title: 'date', data: 'date'}
	]
	
});
<!doctype html>
<html>
<head>
  <script type="application/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script type="application/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
  <script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
  <script type="application/javascript" src="https://cdn.datatables.net/plug-ins/1.10.19/sorting/datetime-moment.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
</head>
<body>
<table id="mytable"></table>
</body>
</html>

但是,如果您拥有日期和月份都在0-12范围内并且'DD / MM / YYYY'和'MM / DD / YYYY'都有效的日期,则可能会发生意外行为,所以我想使用预格式化的日期(使用通用格式)和该格式对应的$.fn.dataTable.moment()设置会更安全。

如果某人(不同于OP)可能需要一种实际的方法来检测日期格式,而不需要冗长的理由说明为什么不做该客户端,那么可以使用以下家庭烘焙格式解析器:

//sample source data
const test1 = ['01-05-2015', '21-06-1982', '13-08-1982', '05-06-2018'];
const test2 = ['05/01/2015', '06/21/1982', '08/13/1982', '06/05/2018'];
const test3 = ['01/05/2015', '21/06/1982', '13/08/1982', '05/06/2018'];
const test4 = ['1-May-2015', '21-Jun-1982', '13-Aug-1982', '5-Jun-2018'];

const dateFormatRecognition = dateArr => {
	//split each date string into parts, delimited by either of ('.', '-', '/')
	let dateParts = dateArr.map(testdate => testdate.split(/[\/\.\-]/));
	//regroup parts so, first, second and third parts values groupped within corresponding array
	dateParts = dateParts[0].map((entry, colindex) => dateParts.map(col => col[colindex]));
	//check each part values against the set of criteria and figure out possible options
	const partsFormat = dateParts.map(parts => ({
		//check whether each part values could be day, month, year
		daysNum: parts.every(part => /^\d+$/.test(part) && part > 0 && part < 32),
		monthAlpha: parts.every(part => ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'].indexOf(part.toLowerCase()) > -1),
		monthNum: parts.every(part => /^\d+$/.test(part) && part > 0 && part < 13),
		yearsNum: parts.every(part => /^\d+$/.test(part) && part > 31),
		//grap min parts length
		minLen: parts.reduce((min,part) => part.length < min ? part.length : min, parts[0].length),
	}));
	//grab first of possible delimiters ('.', '-', '/') and check if those are the same across all values
	const delimiter = dateArr.every(dateEntry => dateEntry.match(/[\.\-\/]/)[0] == dateArr[0].match(/[\.\-\/]/)[0]) ? dateArr[0].match(/[\.\-\/]/)[0] : null;
	//decision making about parts roles
	return partsFormat.reduce((format, partProps) => {
		format.push(partProps.yearsNum ? 'YYYY' :
		partProps.monthNum && format[0] != 'MM' && partProps.minLen == 2 ? 'MM' :
		partProps.monthNum && format[0] != 'MM' && partProps.minLen == 1 ? 'M' :
		partProps.monthAlpha ? 'MMM' :
		partProps.daysNum && partProps.minLen == 2 ? 'DD' :
		partProps.daysNum && partProps.minLen == 1 ? 'D' : null);
		return format;
	}, []).join(delimiter);
};
//output test array formats
console.log(dateFormatRecognition(test1));
console.log(dateFormatRecognition(test2));
console.log(dateFormatRecognition(test3));
console.log(dateFormatRecognition(test4));
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

答案 2 :(得分:1)

您还可以将数组用于多种格式,如momentjs建议here

$.fn.dataTable.moment(['MM/DD/YYYY', 'MM-DD-YYYY', 'MM.DD.YYYY']);

const srcData = [{"name":"Freda Rasmussen","date":"03-01-2015","date2":"03.01.2015","date3":"03/01/2015"},{"name":"Ramsey Blackwell","date":"08-22-2016","date2":"08.22.2016","date3":"08/22/2016"},{"name":"Cameron Leach","date":"11-01-2015","date2":"11.01.2015","date3":"11/01/2015"},{"name":"Foley Porter","date":"04-26-2014","date2":"04.26.2014","date3":"04/26/2014"},{"name":"Corrine Wiggins","date":"04-18-2018","date2":"04.18.2018","date3":"04/18/2018"}]

$.fn.dataTable.moment(['MM/DD/YYYY', 'MM-DD-YYYY', 'MM.DD.YYYY']);


const dataTable = $('#mytable').DataTable({
  data: srcData,
  columns: [{
      title: 'name',
      data: 'name'
    },
    {
      title: 'date',
      data: 'date'
    },
    {
      title: 'date',
      data: 'date2'
    },
    {
      title: 'date',
      data: 'date3'
    }
  ]

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="application/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script type="application/javascript" src="https://cdn.datatables.net/plug-ins/1.10.19/sorting/datetime-moment.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">

<table id="mytable"></table>

答案 3 :(得分:1)

  

或者,如果值改为“ 04/29/2019”,则应将其识别为英国日期格式,使用mm / dd / yyyy进行排序。

这是美国格式。英国日期使用dd / mm / yyyy格式。

因此,您提出的要求是不可能的。考虑“ 01/04/2019”。是1月4日吗?还是4月1日?仅从字符串中就有知道的方法。您必须自己提供语言环境上下文。

另请参见:Date formats by country(在Wikipedia上)。

答案 4 :(得分:1)

在没有更大样本量的情况下,以任何精度检测给定日期的日期格式的范围可能从简单到不可能。

您需要将使用的语言环境传递给任何格式或解析函数,以获取任何准确性,但请记住如果不同用户同时使用多种格式会发生什么情况。

我建议使用UTC格式(YYYY-MM-DD)进行数据存储,因为它是完全明确的,并且可以在输入/显示时从/向用户区域转换。这样,最终用户可以更改显示的格式,而不会对存储的数据的完整性造成任何不利影响。

答案 5 :(得分:0)

据我了解,您想设置默认值?默认位置?为此,请按照以下方式进行操作:函数定义为$ .fn.dataTable.moment = function (format, locale)。您将位置添加为第二个参数。在您的情况下,将使用** moment.js **。默认情况下,Moment.js随附英语(美国)语言环境字符串。如果需要其他位置,可以将它们加载到Moment.js中以供以后使用。

要加载语言环境,请将键和字符串值传递给moment.locale。

有关语言包各部分的更多详细信息,可以在自定义section中找到。