2
votes

comment obtenir une date stockée dans une base de données sous forme de chaîne dans Clojure?

Lorsque je récupère des valeurs d'une base de données Postgres avec clojure.java.jdbc, je me retrouve avec

{: id 1,: nom "exemple",: created_at #object [java.time.LocalDateTime 0x15e0aadc "2019-02-08T12: 52: 11.438633"]}

quand je veux

{: id 1,: nom "exemple",: created_at: created_at "2019-02-08T12: 52: 11.438633"]}

Je veux que la date soit renvoyée au format JSON et consommable par JavaScript. Le deuxième exemple n'est peut-être pas le format idéal pour cela et je suis donc ouvert aux autres, mais un objet Java ne fonctionnera pas.

J'ai essayé quelques approches, mais le compilateur a produit un certain nombre d'erreurs de «parenthèses sans correspondance» en raison de la date #object.

Des réflexions sur la façon de procéder? Dois-je faire quelque chose avec la requête SQL d'origine, est-ce que je fais quelque chose avec mes données renvoyées, ou autre chose?


2 commentaires

Et qu'utilisez-vous pour sérialiser votre carte en JSON? Ne peut-il pas gérer les datetimes?


J'utilisais la fonction write-str clojure.data.json qui a produit l'erreur suivante: 1. Java.lang.Exception non gérée Je ne sais pas comment écrire du JSON de classe java.time.LocalDateTime


3 Réponses :


2
votes

J'imagine que cheshire.core / encode vous donnerait un joli JSON à partir de cette carte.

En savoir plus sur le cheshire à https://github.com/dakrone/cheshire


1 commentaires

Je vais essayer!



1
votes

J'ai essayé de recréer votre résultat avec la base de données H2, mais cela me donne un résultat Clojure #inst au lieu d'une référence d'objet.


OK, voici le problème. Comme l'indique votre impression, vous avez un java.time.LocalDateTime (un Clojure #inst est un objet java.util.Date ).

Si vous voulez une version chaîne, tout ce que vous avez à faire est d'appeler la fonction membre toString dessus:

(dotest
  (jdbc/db-do-commands db-conn ["drop table if exists langs"])
  (jdbc/db-do-commands db-conn
    [(jdbc/create-table-ddl :langs [[:id :serial]
                                    [:lang "varchar not null"]
                                    [:creation :timestamptz]])])
  (jdbc/insert-multi! db-conn :langs
    [{:lang "Clojure" :creation (tjt/iso-str->timestamp "2008-01-01T12:34:56Z")}
     {:lang "Java"    :creation (tjt/iso-str->timestamp "1995-06-01T07:08:09Z")}])

  (let [result (jdbc/query db-conn ["select * from langs"])]
    (is=  (vec result)
      [{:id       1, :lang "Clojure",
        :creation #inst "2008-01-01T12:34:56.000000000-00:00"}
       {:id       2, :lang "Java",
        :creation #inst "1995-06-01T07:08:09.000000000-00:00"}])))

Cependant , aucune information de fuseau horaire n'est jointe. Donc, vous voulez probablement un ZonedDateTime. NOUS ASSUMERONS LE FUSEAU HORAIRE UTC CI-DESSOUS (veuillez vérifier la configuration de votre serveur DB):

(ns tst.demo.jdbc-pool
  (:use demo.core tupelo.core tupelo.test)
  (:require
    [clojure.java.jdbc :as jdbc]
    [hikari-cp.core :as pool]
    [tupelo.java-time :as tjt] ) )

(def datasource-options-pg
  {:adapter       "postgresql"
   :database-name "alan"
   :server-name   "localhost"
   :port-number   5433
   :username      "alan"
   :password      "secret" } )

(def ^:dynamic db-conn nil)

(defn with-connection-pool
  "Creates and uses a connection for test function"
  [tst-fn]
  (let [datasource (pool/make-datasource datasource-options-pg)]
    (binding [db-conn {:datasource datasource}]
      (tst-fn)
      (pool/close-datasource datasource)))) ; close the connection - also closes/destroys the in-memory database

(use-fixtures
  :once with-connection-pool) ; use the same db connection pool for all tests

Notez que le ZDT a un suffixe comme [UTC] ajouté à la fin, vous souhaiterez peut-être le convertir en Instant, puis utiliser la méthode .toString pour en obtenir une représentation sous forme de chaîne plus simple (ISO-8601).


Si cela ne vous dérange pas d'utiliser une bibliothèque externe pour rendre cela plus facile, la bibliothèque tupelo.java-time a une fonction d'assistance très pratique: p>

(ns xyz
  (require [tupelo.java-time :as tjt] ... ))

(tjt/string-date-time-iso zdt) => "2019-02-01T00:00:00Z"

Il existe de nombreuses autres fonctions d'assistance disponibles. Veuillez consulter la documentation sur l'API et les tests unitaires pour des exemples .


Mise à jour

J'ai finalement réparé mon installation Postgres (j'ai dû réinitialiser le mot de passe pour faire fonctionner Hikari). Voici mon code de test:

  (let [jud      (Date.)
        ldt      (LocalDateTime/parse "2019-02-01T00:00:00")
        zdt      (.atZone ldt (ZoneId/of "UTC"))  ; *** big assumption here! ***
        inst     (.toInstant zdt)
        inst-str (.toString inst) ]

    ; a Clojure #inst is just a java.util.Date object
    jud      =>  #inst "2019-02-09T19:38:30.088-00:00"   

    ; a ZonedDateTime can print in 2 ways
    zdt              => #object[java.time.ZonedDateTime 0x780af31 "2019-02-01T00:00Z[UTC]"]
    (.toString zdt)  => "2019-02-01T00:00Z[UTC]"

    ; a java.time.Instant also prints in 2 ways:
    instant          => #object[java.time.Instant 0x48301adb "2019-02-01T00:00:00Z"]
    instant-str      => "2019-02-01T00:00:00Z"

Ce qui précède n'est que des éléments de configuration. Voici le test unitaire qui vérifie le comportement:

  (let [ldt (LocalDateTime/parse "2019-02-01T00:00:00" )]

    (.toString ldt) => <#java.lang.String "2019-02-01T00:00">

Vous pouvez donc voir que j'obtiens toujours un résultat java.util.Date , qui Clojure imprime avec le formatage #inst . Je ne sais pas comment vous obtenez JDBC pour afficher le format LocalDateTime .


1 commentaires

Merci pour votre réponse vraiment complète! Le .toString a fonctionné. Le besoin UTC n'est pas encore envisagé. Je vais aussi essayer Tupelo.