4
votes

jQuery DataTables: existe-t-il un moyen de détecter automatiquement le format de la date?

J'ai un datatable opérationnel et pour le moment j'ai défini le format de date manuellement en ajoutant

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

avant de définir ma datatable elle-même:

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


3 commentaires

Les deux formats que vous mentionnez dans votre question sont-ils les seuls deux formats de date qui seront jamais utilisés? De plus, laquelle des deux possibilités suivantes est vraie dans votre cas? Possibilité 1. Un tableau peut afficher des dates dans différents formats mais dans un seul tableau, un seul format sera utilisé . Possibilité 2. Un tableau peut afficher des dates dans différents formats et un seul tableau peut contenir plusieurs formats à la fois. Ainsi, une ligne utiliserait le format américain et la ligne suivante utiliserait le format allemand. Vous devriez modifier votre question pour clarifier tout cela.


@Louis: Vous avez raison, c'est important ... Dans mon cas, la possibilité 2 est la bonne. Un tableau peut afficher des dates dans divers formats, mais dans un seul tableau, un seul format sera utilisé. Pour le moment, je ne sais pas combien de formats différents l'application prendra en charge. Je suppose 5+. J'ai modifié la question ci-dessus.


@JonSnow: pour éviter la confusion à laquelle Louis se réfère (et que j'ai d'ailleurs mentionnée dans ma réponse il y a environ une semaine), vous pouvez utiliser votre propre analyseur de format de date que j'ai partagé dans le dernier commentaire de ma réponse. Ce script, la plupart du temps, peut distinguer les jours des mois en fonction du contexte (toutes les valeurs possibles pour les deux).


6 Réponses :


3
votes

En supposant que vous utilisez déjà plug-in de tri des dates , vous ne le faites pas vous devez vous soucier de quoi que ce soit tant que le format souhaité est parmi ceux spécifiés:

.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

Suivant règles dictées par moment.js, sur lesquelles repose votre plug-in, doivent être respectées.

Vous trouverez ci-dessous la démo fonctionnelle:

//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));
<!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>

Cependant, un comportement inattendu peut se produire si vous avez des dates où les jours et les mois sont compris entre 0 et 12 et les deux 'JJ / MM / AAAA' et 'MM / JJ / AAAA' sont valides, donc je devinez, qu'il serait plus sûr d'utiliser des dates préformatées (en utilisant un format commun) et le paramètre $ .fn.dataTable.moment () correspondant pour ce format.

Si quelqu'un, contrairement à OP, pourrait avoir besoin d'un moyen réel de détecter le format de la date et de ne pas justifier à long terme pourquoi ne pas le faire côté client, suivre un analyseur de format maison peut être utile:

//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'}
	]
	
});
$.fn.dataTable.moment(['MM/DD/YYYY', 'DD-MM-YYYY', 'DD-MMM-YYYY']);


2 commentaires

Désolé, votre réponse n'a pas aidé, car c'est exactement le problème. JE NE VEUX PAS définir le format explicite. Je veux qu'il soit détecté automatiquement.


Maintenant que vous avez modifié votre réponse, elle est plus utile, merci beaucoup!



2
votes

4 commentaires

Cela semble prometteur! Je vais essayer et revenir plus tard avec un résultat


@JonSnow Je voudrais savoir, utilisez-vous des données de json? et avez-vous le contrôle sur le backend?


Aucun contrôle de mon côté, non. J'obtiens les données «telles quelles»


stackoverflow.com/a/54399108/8053274 Cela vous aiderait si vous pouviez obtenir des millisecondes



2
votes

Ou si la valeur est "04/29/2019" à la place, cela doit être reconnu comme un format de date britannique, en utilisant mm / jj / aaaa pour le tri.

C'est le formatage américain. Les dates britanniques utilisent le format jj / mm / aaaa.

Pour cette raison, ce que vous demandez est impossible. Considérez "01/04/2019". Est-ce que c'est le 4 janvier? Ou est-ce le 1er avril? Il y a un moyen de savoir à partir de la chaîne seule. Vous devez fournir le contexte local vous-même.

Voir aussi: Formats de date par pays sur Wikipedia.


2 commentaires

Vous avez raison. Concernant le format américain. Et aussi en ce qui concerne le problème de comportement inattendu. Mais je sais qu'il n'y aura presque jamais qu'une ou deux dates dans ma datatable, plus souvent 50 et bien plus. Ensuite, il devrait être possible de détecter?


Bien que cela puisse être, je ne le recommande pas. Je n'utiliserais pas une telle méthode dans aucun de mes projets, personnellement.



1
votes

si j'ai bien compris, vous souhaitez définir quelque chose par défaut? Un emplacement par défaut? Pour ce faire, procédez comme suit: la fonction est définie comme $ .fn.dataTable.moment = function (format, locale) . Vous ajoutez votre emplacement comme deuxième paramètre. Dans votre cas, ** moment.js ** est utilisé. Par défaut, Moment.js est fourni avec des chaînes de paramètres régionaux anglais (États-Unis). Si vous avez besoin d'autres emplacements, vous pouvez les charger dans Moment.js pour une utilisation ultérieure.

Pour charger un paramètre régional, transmettez les valeurs de clé et de chaîne à moment.locale.

Vous trouverez plus de détails sur chaque partie du pack de langue dans la section de personnalisation .


0 commentaires

6
votes

Il a été suggéré de passer un tableau pour les formats à $ .fn.dataTable.moment (...) , mais cela fonctionne si et seulement si il ne peut jamais arriver qu'une donnée corresponde plus plus d'un format dans le tableau. À moins que vous ne puissiez garantir cela, transmettre un tableau de formats n'est pas la solution.

Vous avez commencé par l'exemple de JJ.MM.AAAA et MM / JJ / AAAA code >. Une date correspondra à un format ou à l'autre mais pas aux deux car si elle a des délimiteurs de période, elle correspond au 1er format mais pas au 2ème et si elle a des délimiteurs de barres obliques, elle correspond au 2ème format mais pas au 1er. Cependant, en général, si vous avez des dates ailleurs qu'aux États-Unis ou en Allemagne, vous rencontrerez des cas ambigus. Matt Johnson a mentionné par exemple une date comme "01/04/2019" qui peut correspondre au MM / JJ / YYYY et être interprété comme "4 janvier 2019", ou correspondre au format JJ / MM / AAAA et être interprété comme "1er avril 2019".

Si vous pouvez avoir des dates au format JJ / MM / AAAA ou MM / JJ / AAAA et que vous appelez $ .fn.dataTable.moment ([ "JJ / MM / AAAA", "MM / JJ / AAAA"]) alors vous obtiendrez parfois des résultats incorrects. Le problème est que le plugin qui implémente la fonction que vous appelez regarde chaque cellule isolément .

Tableau 1

Supposons un tableau destiné à utiliser des dates au format JJ / MM / AAAA , avec les cellules suivantes:

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

Tableau 2

Supposons un tableau destiné à utiliser des dates au format MM / JJ / AAAA , avec les cellules suivantes:

  1. 21/02/2019
  2. 01/04/2019
  3. 24/12/2019

Les deux tableaux contiennent en fait les mêmes dates. Ils sont simplement représentés différemment.

Supposons que vous ayez configuré votre table avec $ .fn.dataTable.moment (["DD / MM / YYYY", "MM / DD / YYYY"]) code>. Le tableau 1 sera interprété correctement. Cependant, la ligne 2 du tableau 2 ne sera pas interprétée correctement. La date 01/04/2019 correspond au premier format du tableau ( JJ / MM / AAAA ) et c'est ainsi que moment va interpréter il. Peu importe le nombre d'autres cellules qui ne peuvent pas contenir JJ / MM / AAAA car le plugin qui appelle moment ne fait pas d'analyse statistique. Il examine chaque cellule isolément. Voici le code (avec quelques lignes vides supprimées):

$.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 );
    };
};

Vous pouvez retourner les arguments et appeler $ .fn.dataTable.moment (["MM / DD / YYYY", "DD / MM / AAAA "]) . Maintenant, la deuxième table conviendrait, mais le même problème se produirait dans la première table.


Ok, et alors?

Si le backend contient déjà l'heure UTC timbres, alors j'enverrais simplement ces horodatages au frontal au lieu d'envoyer des valeurs localisées. Au stade du rendu d'une cellule qui contient une date, je demanderais au frontal de convertir la date UTC dans un format qui a du sens pour l'utilisateur. Datatable ferait un tri sur la base des valeurs UTC, qui peuvent être comparées sans ambiguïté.

Si le backend ne stocke pas ses dates sous forme d'horodatages UTC, je le redéfinirais pour qu'il le fasse, puis faites ce que j'ai décrit dans le paragraphe précédent.

Sinon, il y a peut un moyen de faire dans le front-end une analyse statistique de votre table avant que Datatables ne tente de rendre et de classer il. Ainsi, vous pouvez découvrir quel format est utilisé, puis le transmettre à Datatables. Cependant, cela me semble encore fragile. Si la table utilise le protocole côté serveur, seule une petite partie des données est disponible à la fois. Si vous effectuez une analyse uniquement sur la première réponse du serveur, une réponse ultérieure couvrant une partie ultérieure du tableau peut réfuter l'hypothèse initiale. De plus, il peut y avoir des cas où toutes les dates d'un datatable sont ambiguës. Sur un ensemble de données volumineux et non filtré, cela peut être peu probable, mais dès que les utilisateurs sont autorisés à filtrer l'ensemble de données pour n'afficher qu'un sous-ensemble, ils peuvent le filtrer de manière à ce que toutes les dates d'un sous-ensemble spécifique soient ambiguës. Je ne déploierais pas d'application dans l'espoir que cela n'arrivera jamais.


1 commentaires

très bien décrit ... maintenant je n'ai pas de solution comme je l'avais espéré, mais un bon sous-sol pour une discussion avec mes collègues backend. À la fin, votre solution rendra le code frontal plus lisible (et je suis sûr que plus rapidement aussi). Merci pour le temps que vous avez investi.



1
votes

Sans une taille d'échantillon plus importante, la détection du format de date d'une date donnée avec une précision quelconque peut aller du simple à l'impossible.

Vous devrez transmettre les paramètres régionaux utilisés à n'importe quelle fonction de formatage ou d'analyse pour obtenir une précision quelconque, mais gardez à l'esprit ce qui pourrait se produire si plusieurs formats devaient être utilisés par différents utilisateurs en même temps.

Ce que je recommande, c'est d'utiliser le format UTC (AAAA-MM-JJ) pour le stockage des données, car il est totalement non ambigu et convertit de / vers les paramètres régionaux de l'utilisateur lors de l'entrée / de l'affichage. De cette façon, le format affiché peut être modifié par l'utilisateur final sans aucun effet négatif sur l'intégrité des données stockées.


0 commentaires