jeudi 28 janvier 2016

Filtrer sur la partie horaire d'une date en FetchXML

Avez-vous déjà voulu filtrer sur une date en faisant des requêtes FetchXML ? C'est plutôt simple, n'est-ce pas ?
Par contre pour ce qui est de la partie horaire, ça se corse...
Voici un article qui vous expliquera comment faire !
Prenons la requête FetchXML suivante, que retourne-t-elle ?
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="account">
    <attribute name="name" />
    <attribute name="address1_city" />
    <attribute name="primarycontactid" />
    <attribute name="telephone1" />
    <attribute name="accountid" />
    <order attribute="name" descending="false" />
    <filter type="and">
      <condition attribute="statecode" operator="eq" value="0" />
      <condition attribute="modifiedon" operator="on-or-before" value="2016-01-28" />
    </filter>
  </entity>
</fetch>
Réponse : Tous les enregistrements modifiés le ou avant le 28 janvier 2016.

Si vous souhaitez filtrer sur la journée entière cela est suffisant mais si votre besoin est de filtrer avant une heure précise il va falloir s'y prendre d'une manière différente.
Tout d'abord, il ne sera pas possible de passer par la recherche avancée pour la simple et bonne raison que depuis cette dernière il n'est pas possible de renseigner la partie horaire. Et même si vous tentez de remplacer "2016-01-28" par "2016-01-28T15:48:51" la requête renverra tout de même les enregistrements modifiés entre 15h48m51s et 23h59m59s.

La solution est d'utiliser l'opérateur "Inférieur ou égal à" au lieu de l'opérateur "Le ou avant le". C'est un opérateur numérique mais il fonctionne très bien avec les valeurs DateTime.

Voici un exemple :
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="account">
    <attribute name="name" />
    <attribute name="address1_city" />
    <attribute name="primarycontactid" />
    <attribute name="telephone1" />
    <attribute name="accountid" />
    <order attribute="name" descending="false" />
    <filter type="and">
      <condition attribute="statecode" operator="eq" value="0" />
      <condition attribute="modifiedon" operator="le" value="2016-01-28T15:48:51+01:00" />
    </filter>
  </entity>
</fetch>

Bien sûr, les autres opérateur numériques fonctionnent eux aussi !

Bonus : Si vous avez tout suivi vous aurez remarqué le fuseau horaire UTC. Étant donné que je suis en France et que c'est l'heure d'hiver mon fuseau est UTC+1.
Depuis la version 2016 de CRM les champs Date et Heure peuvent avoir un format sans fuseau horaire, renseigner le fuseau dans la requête est donc inutile.
Pour les anciennes version (donc avant 2016) et lorsque que le champ est en Heure locale (pour la version 2016) renseigner le fuseau horaire est très important car la requête y sera sensible !

Vous pouvez regarder sur Wikipédia à propos de la norme ISO 8601 pour le format de la date et de l'heure utilisée dans MS CRM.

Double bonus : Voici la traduction en QueryExpression !
QueryExpression query = new QueryExpression
{
    EntityName = "account",
    ColumnSet = new ColumnSet("name", "address1_city", "primarycontactid", "telephone1"),
    Orders = { new OrderExpression("name", OrderType.Ascending) },
    Criteria =
    {
        Conditions =
        {
            new ConditionExpression("statecode", ConditionOperator.Equal, new OptionSetValue(0)),
            new ConditionExpression("modifiedon", ConditionOperator.LessEqual, new DateTime(2016,01,28,15,48,51, DateTimeKind.Local))
        }
    }
};