Convertir les TimeStamp Java en DateTime C#

En bidouillant un peu les données récupérées par l’API Wow, je me suis rendu compte que certaines données sont en fait des TimeStamp Java. Le TimeStamp est une unité de mesure du temps utilisé principalement en Java.

Afin de pouvoir utiliser ces données dans mes traitements C#, j’ai fait quelques recherches sur le sujet…

Le TimeStamp Java s’appuie sur une date de référence. Cette date est fixée au 1 janvier 1970 à minuit. En java, lorsque l’on utilise un TimeStamp, il s’agit en réalité du nombre de millième de secondes écoulés depuis la date de référence (1 000 unités TimeSpamp donnent une seconde). En C# en revanche, l’unité de mesure est le tick (1 seconde vaut 10 000 000 ticks sous Windows). Cf. http://msdn.microsoft.com/en-us/library/system.datetime.ticks.aspx pour le détail d’un DateTime C#.

Et bien maintenant c’est tout simple ! Ce n’est qu’un peu de math… Je passe les détails du calcul, mais il suffit de multiplier par 10 000 le TimeStamp Java pour obtenir un nombre de Ticks Windows. Une fois ce nombre calculé, je n’ai plus qu’à l’ajouter à ma date de référence (01/01/1970) et le tour est joué !

Ah si, une dernière remarque : Il faut toujours penser aux fuseaux-horaires quand on joue avec les dates… Donc pour obtenir la vraie date dans le fuseau-horaire courant, il faut appliquer la fonction « ToLocaleTime ». Cette dernière se charge d’appliquer les décalages horaires adéquat en fonction de la machine sur laquelle s’exécute le traitement. Ainsi, plus besoin de s’enquiquiner avec l’ajout d’une ou deux heures…

Allez, un petit bout de code pour le fun :

/// <summary>
/// Helper pour les méthodes de compatibilité Java
/// </summary>
public static class JavaHelper
{
    private static DateTime _referenceDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);

    /// <summary>
    /// Fonction permettant de convertir les TimeStamp Java en DateTime C#
    /// </summary>
    /// <param name="timeStamp">TimeStamp Java</param>
    /// <returns>Date C#</returns>
    public static DateTime ConvertJavaTimeStampToDateTime(long timeStamp)
    {
        return _referenceDate.AddTicks(timeStamp * 10000).ToLocalTime();
    }
}

NOTE : Pour le moment, la plus petite unité de mesure du temps que j’ai réussi à trouver sur UNIX est le centième de seconde (0.01 sec). il faut utiliser la fonction POSIX « times » définie dans l’include « sys/times.h ».

Astuce C# du jour : Déplacer les DLL d’un binaire DotNet

En temps normal, sans parler du GAC (Global Assembly Cache), toutes les DLL dont dépendent un binaire C# doivent se trouver dans le même répertoire que ce dernier pour qu’il puisse s’exécuter correctement… Malheureusement, plus les projets grossissent, et plus le nombre de DLL augmente. Ce qui rend l’organisation du répertoire du binaire quelque peu anarchique !

Et bien, voici la solution miracle : Il est possible d’indiquer par configuration les répertoires potentiels où se trouvent les DLL du projet ! 😀

Prenons l’exemple d’une application quelconque « CanShooter.exe » qui a besoin de la DLL « CanShooter.Bean.dll » pour fonctionner. Si nous déplaçons la DLL dans un sous-répertoire « Custom API », l’application ne fonctionne plus !

La magie opère lorsque l’on créé un fichier de configuration de l’application nommé « CanShooter.config » et qui contient les éléments suivants :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <runtime>
   <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     <probing privatePath="Custom API"/>
   </assemblyBinding>
 </runtime>
</configuration>

Et voilà, le tour est joué ! 😀

Client Rest

Et voilà, c’est fait ! J’ai fait un petit client Rest tout simplissime en C# 🙂

L’idée est ici d’avoir une petite API qui se charge pour moi de faire l’appel Web et la dé-sérialisation des données dans un objet C#. Je m’appuie sur les contrats WCF pour la définition de l’objet en mémoire (DataContract, DataMember, etc.).

Imaginons que je désire récupérer les informations de mon personnage de Wow avec sa guilde. Blizzard a mis à disposition des serveurs que nous pouvons interroger en JSON. Pour notre cas de figure, la documentation de Blizzard indique ceci :

URL = Host + "/api/wow/character/" + Realm + "/" + CharacterName
       + "?locale=" + locale + "&fields=" + optional(s) field(s)

Realm = <proper realm name> | <normalized realm name>

Ce qui nous donnera un appel Web qui ressemblera à ceci :

GET /api/wow/character/La Croisade écarlate/Shoubido?fields=guild
Host: eu.battle.net

La documentation indique également la structure des données en sortie :

HTTP/1.1 200 OK
<http headers>

{
   "realm": "Medivh",
   "name": "Uther",
   "level": 85,
   "lastModified": 1307596000000,
   "thumbnail": "medivh/1/1-avatar.jpg",
   "race": 1,
   "achievementPoints": 9745,
   "gender": 0,
   "class": 2,
   "guild":{
    "name":"Knights of the Silver Hand",
    "realm":"Medivh",
    "level":25,
    "members":50,
    "achievementPoints":7500,
    "emblem":{
      "icon":119,
      "iconColor":"ffb1b8b1",
      "border":0,
      "borderColor":"ffffffff",
      "backgroundColor":"ff006391"
    }
  }
}

Nous allons dans un premier temps nous focaliser sur la classe qui contiendra les données à récupérer. La déclaration est par exemple la suivante :

    [DataContract]
    public class CharacterRawData : IExtensibleDataObject
    {
        [DataMember(Name = "realm", IsRequired = true)]
        public string Realm { get; set; }

        [DataMember(Name = "name", IsRequired = true)]
        public string Name { get; set; }

        [DataMember(Name = "level", IsRequired = true)]
        public long Level { get; set; }

        [DataMember(Name = "lastModified", IsRequired = true)]
        public long LastModified { get; set; }

        // ... Les autres champs ne sont pas présentés

        public ExtensionDataObject ExtensionData { get; set; }
    }

Le point important dans cette déclaration de classe est qu’il faut hériter de l’interface IExtensibleDataObject. Petit rappel WCF : Cette interface impose la présence de la propriété ExtensionData qui sera alimentée par les champs non déclarés dans la classe au moment de la sérialisation / dé-sérialisation des données par le moteur WCF. Ainsi, nous ne pouvons pas perdre les informations restituées par le serveur de Blizzard.

Une fois cet objet défini, il suffit d’utiliser mon petit client Rest comme suit :

// Création du client Rest
var restClient = new RestClient(new Uri("http://eu.battle.net"));

// On demande au client Rest de récupérer les données
var result = restClient.Retrieve<CharacterRawData>(
  RestResource.Create("/api/wow/character/{realm}/{character name}")
  .AddPathParameter("realm", "La Croisade écarlate")
  .AddPathParameter("character name", "Shoubido")
  .AddQueryParameter("fields", "pvp")
  .AddQueryParameter("locale", CultureInfo.CurrentCulture));

En fait, pour résumer :

  1. Nous créons un client Rest avec l’adresse du serveur que nous désirons interroger
  2. Nous appelons la méthode « Retrieve » du client Rest. Le type générique de cette méthode correspond au type de la classe qu’elle doit utiliser pour dé-sérialiser les données du serveur (Le type doit obligatoirement implémenter l’interface « IExtensibleDataObject »). Donc ici, la variable « result » est de type « CharacterRawData ». La méthode « Retrieve » prend en paramètre une ressource Rest (« RestResource »). Cette ressource définie les éléments nécessaires pour interroger le serveur.
  3. Nous utilisons la méthode statique « RestResource.Create » pour créer une nouvelle ressource Rest. Cette ressource définie l’URL à interroger (chemin + paramètres GET éventuels).
  4. Le chemin est paramétrée avec le nom de la guilde et le nom du personnage. Chaque paramètre du chemin est définie entre accolades. Afin de valoriser ces paramètres, il faut utiliser la fonction « AddPathParameter » qui prend en paramètre le nom du paramètre ainsi que sa valeur (qui peut être de tout type – Le client Rest convertira la valeur en chaîne de caractères).
  5. Enfin, l’URL peut posséder des paramètres GET qui sont valorisés en utilisant la fonction « AddQueryParameter » qui prend en paramètre le nom du paramètre GET ainsi que sa valeur (qui peut être de tout type – Le client Rest convertira la valeur en chaîne de caractères).

Et voilà, le tour est joué ! 😀 Le code source de ce petit client Rest se trouve ici.

Enjoy !

Emprunt d’identitié

Pour une fois, parlons un peu technique (J’suis pas développeur pour rien !) 😀

Je cherche a écrire / modifier des fichiers en empruntant l’identité d’un autre compte utilisateur de mon poste. Le Framework .Net actuel ne semble pas me permettre de faire de l’emprunt d’identité mis à part lancer un processus sous l’identité d’un autre compte (Cf. une des surcharges de la fonction Processs.Start()) – Mais ça j’aimerais l’éviter surtout si je dois également manipuler des fichiers sous mon compte actuel…

En creusant un peu sur le net, on s’aperçoit que l’opération est possible moyennant l’utilisation de l’API advapi32.dll. Le principe est simple, il suffit d’appeler la fonction « LogonUser » avec les paramètres adéquats. Cette fonction permet de récupérer un jeton d’identité d’une autre personne sur le système. Le Framework .Net nous permet alors d’emprunter cette identité en appelant la méthode statique WindowsIdentity.Impersonate().

En fait, le plus complexe est la gestion des erreurs avec l’Interop. Là encore, avec un peu de pratique, il n’y a rien de sorcier. La MSDN décrit très bien la fonction LoginUser de l’API advapi32.dll : http://msdn.microsoft.com/en-us/library/aa378184%28v=vs.85%29.aspx. Il n’y a donc qu’à utiliser les bons types de données et savoir comment récupérer les erreurs. Ce point fera l’objet d’un autre sujet sur l’interopérabilité C/C#.

Vous trouverez ici le code source d’un exemple concret.