diff --git a/dune-project b/dune-project
index e218c82e2dc2e8bbdd4b893bc8b179a6a961a144..9d9b51a47c0eb2f9629e163d5a3389a260c3c860 100644
--- a/dune-project
+++ b/dune-project
@@ -19,7 +19,7 @@
  (name ocaml_edt)
  (synopsis "Generates timetables for course planification")
  (description "Program and library to generate a timetable for a course or a set of courses, outputs in HTML format among others")
- (depends ocaml dune cmdliner calendar yojson)
+ (depends ocaml dune cmdliner calendar yojson ounit2)
  (tags
   (topics "timetable")))
 
diff --git a/lib/Timetable.ml b/lib/Timetable.ml
index 933a6a8199165811a446140ea9497eeda5f13bee..da9cca46bf60c6eba62dda2b8ff08920b1e32c3a 100644
--- a/lib/Timetable.ml
+++ b/lib/Timetable.ml
@@ -10,10 +10,19 @@ let lex access cmp fn v1 v2 =
 let eq _ _ = 0
 (* let rec gcd n m = if m = 0 then n else gcd m (n mod m) *)
 
-type date = CalendarLib.Date.t
-type heure = int
+type date = CalendarLib.Calendar.Date.t
+type heure = CalendarLib.Calendar.Time.t
 
-let heure (h : int) (m : int) : heure = (h * 60) + m
+let date (annee : int) (mois : int) (jour : int) : date =
+  CalendarLib.Calendar.Date.make annee mois jour
+
+let heure (h : int) (m : int) : heure = CalendarLib.Calendar.Time.make h m 0
+
+let week_start_of_date d : date =
+  let open CalendarLib.Calendar.Date in
+  let day = day_of_week d in
+  let offset = 1 - int_of_day day in
+  add d (Period.day offset)
 
 (* let as_iso (d : date) : string = CalendarLib.Printer.Date.sprint "%F" d *)
 
@@ -30,36 +39,43 @@ module type ResourcesT = sig
 
   val conflicts : t -> t -> bool
   val compare : t -> t -> int
+  val title : t -> string
+  val description : t -> string
 end
 
 module Make (R : ResourcesT) = struct
-  module D = CalendarLib.Date
+  module C = CalendarLib.Calendar
+  module P = CalendarLib.Calendar.Period
+  module Pr = CalendarLib.Printer.Calendar
 
-  type creneau = { resources : R.t; date : date; start : heure; duration : int }
+  type creneau = { resources : R.t; start : C.t; duration : int }
   type creneaux = creneau list
   type week = date * creneaux
 
+  let creneau r dst tst dur =
+    { resources = r; start = C.create dst tst; duration = dur }
+
+  let resources c = c.resources
+  let starts c = c.start
+  let duration c = c.duration
+  let new_week y m d = (week_start_of_date @@ C.Date.make y m d, [])
+  let empty_creneaux = []
+
   let compare =
     eq
-    |> lex (fun c -> c.date) D.compare
-    |> lex (fun c -> c.start) ( - )
+    |> lex (fun c -> c.start) C.compare
     |> lex (fun c -> c.duration) ( - )
     |> lex (fun c -> c.resources) R.compare
 
-  let inside h h' d = h' <= h && h <= h' + d
+  (** [inside h h' d] renvoie true si h est dans l'intervalle [h', h'+d] *)
+  let inside (h : C.t) (h' : C.t) d =
+    let h'' = C.add h' (P.minute d) in
+    C.compare h' h <= 0 && C.compare h h'' <= 0
 
   let intersect c1 c2 =
-    c1.date = c2.date
-    && (inside c1.start c2.start c2.duration
-       || inside c2.start c1.start c1.duration)
+    inside c1.start c2.start c2.duration || inside c2.start c1.start c1.duration
 
   let conflict c1 c2 = intersect c1 c2 && R.conflicts c1.resources c2.resources
-  (* let date = D.make *)
-
-  let week_of_date d : date =
-    let day = D.day_of_week d in
-    let offset = 1 - D.int_of_day day in
-    D.add d (D.Period.day offset)
 
   exception Conflit of (creneau * creneau)
 
@@ -74,11 +90,12 @@ module Make (R : ResourcesT) = struct
           let l_s = supprime_conflits raise_exc c rest in
           if l_s == rest then l else l_s
 
-  let rec place (raise_exc : bool) (c : creneau) (l : creneaux) : creneaux =
+  let rec place ?(raise_exc : bool = false) (c : creneau) (l : creneaux) :
+      creneaux =
     match l with
     | [] -> [ c ]
-    | c' :: rest when conflict c c' -> place raise_exc c rest
-    | c' :: rest when compare c' c <= 0 -> c' :: place raise_exc c rest
+    | c' :: rest when conflict c c' -> place ~raise_exc c rest
+    | c' :: rest when compare c' c <= 0 -> c' :: place ~raise_exc c rest
     | c' :: rest -> c :: c' :: supprime_conflits raise_exc c rest
 
   let groupby gv cmp cmp2 l =
@@ -96,10 +113,74 @@ module Make (R : ResourcesT) = struct
     in
     aux l
 
-  let by_week l = groupby (fun c -> week_of_date c.date) D.compare compare l
-  let week_start : week -> date = fst
+  let by_week l =
+    groupby
+      (fun c -> week_start_of_date (C.to_date c.start))
+      C.Date.compare compare l
 
-  let to_fullcalendar (_cr : (date * creneau list) list) : Yojson.Safe.t =
-    (* let open Yojson.Safe in *)
-    `Null
+  let week_start : week -> date = fst
+  let ends c = C.add c.start (P.minute c.duration)
+
+  let to_fullcalendar (weeks : (date * creneau list) list) : Yojson.Basic.t =
+    let eid = ref 0 in
+    let creneau_to_event c =
+      let id = !eid in
+      eid := !eid + 1;
+      `Assoc
+        [
+          ("id", `String ("evt" ^ string_of_int id));
+          ("title", `String (R.title c.resources));
+          ("start", `String (Pr.sprint "%FT%T" c.start));
+          ("end", `String (Pr.sprint "%FT%T" (ends c)));
+        ]
+    in
+    `List
+      (List.map (fun (_, crl) -> List.map creneau_to_event crl) weeks
+      |> List.flatten)
+
+  let write_fullcalendar ?(prefix = "") ?(suffix = "") (weeks : week list)
+      (filename : string) =
+    let ch = open_out filename in
+    output_string ch prefix;
+    to_fullcalendar weeks |> Yojson.Basic.pretty_to_channel ch;
+    output_string ch suffix;
+    close_out_noerr ch
+
+  let simple_page weeks filename =
+    let tpl =
+      {|
+<!DOCTYPE html>
+<html lang='en'>
+  <head>
+    <meta charset='utf-8' />
+    <script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.15/index.global.min.js'></script>
+    <script>
+
+      document.addEventListener('DOMContentLoaded', function() {
+        var data = $$DATA$$;
+        var calendarEl = document.getElementById('calendar');
+        var calendar = new FullCalendar.Calendar(calendarEl, {
+          initialView: 'timeGridWeek',
+          events: data
+        });
+        calendar.render();
+      });
+
+    </script>
+  </head>
+  <body>
+    <div id='calendar'></div>
+  </body>
+</html>
+      |}
+    in
+    let output =
+      Str.global_replace
+        (Str.regexp_string "$$DATA$$")
+        (to_fullcalendar weeks |> Yojson.Basic.to_string)
+        tpl
+    in
+    let ch = open_out filename in
+    output_string ch output;
+    close_out_noerr ch
 end
diff --git a/lib/Timetable.mli b/lib/Timetable.mli
index 71d813fe75a27bb93b391de912f1612f8a48026e..399292ad7a6104fba43bf555c08af5fe1e5bd80a 100644
--- a/lib/Timetable.mli
+++ b/lib/Timetable.mli
@@ -1,6 +1,7 @@
-type date = CalendarLib.Date.t
+type date
 type heure
 
+val date : int -> int -> int -> date
 val heure : int -> int -> heure
 
 module type ResourcesT = sig
@@ -8,11 +9,25 @@ module type ResourcesT = sig
 
   val conflicts : t -> t -> bool
   val compare : t -> t -> int
+  val title : t -> string
+  val description : t -> string
 end
 
 module Make (R : ResourcesT) : sig
-  type creneau = { resources : R.t; date : date; start : heure; duration : int }
+  type creneau
   (** Type pour représenter un créneau associé à des ressources *)
+    
+  val creneau: R.t -> date -> heure -> int -> creneau
+  (** Créée un nouveau créneau *)
+
+  val resources : creneau -> R.t
+  (** ressources associées à un créneau *)
+
+  val starts : creneau -> CalendarLib.Calendar.t
+  (** heure de début du créneau *)
+
+  val duration : creneau -> int
+  (** heure de fin du créneau *)
 
   type creneaux
   (** Représente un ensemble de créneaux *)
@@ -20,10 +35,17 @@ module Make (R : ResourcesT) : sig
   type week
   (** Représente une semaine *)
 
+  val new_week : int -> int -> int -> week
+  (** [new_week y m d] crée une nouvelle semaine contenant le jour passé en
+    argument sous la forme [y-m-d] (année, mois, jour)*)
+
   exception Conflit of (creneau * creneau)
   (** Conflit de créneaux *)
 
-  val place : bool -> creneau -> creneaux -> creneaux
+val empty_creneaux: creneaux
+(** ensemble de créneaux vides *)
+
+  val place : ?raise_exc:bool -> creneau -> creneaux -> creneaux
   (** [place b cr crl] ajoute le créneau [cr] à la liste des créneaux [crl] si
         ce créneau rentre en conflit avec un créneau dans [crl], alors si [b]
         vaut [true] une exception [Conflit] est levée. Si [b] vaut [false], alors
@@ -37,7 +59,18 @@ module Make (R : ResourcesT) : sig
   val week_start : week -> date
   (** date du premier jour de la semaine *)
 
-  val to_fullcalendar : week list -> Yojson.Safe.t
+  val to_fullcalendar : week list -> Yojson.Basic.t
   (** convertit un ensemble de semaines en structure json utilisable comme
     donnée pour le composant javascript FullCalendar *)
+
+  val write_fullcalendar :
+    ?prefix:string -> ?suffix:string -> week list -> string -> unit
+  (** [write_fullcalendar weeks filename]
+    convertit une liste de semaines [weeks] en json et l'écrit dans le fichier nommé [filename].
+    [?prefix] est écrit avant le json dans le fichier
+    [?suffix] est écrit après le json dans le fichier *)
+
+  val simple_page : week list -> string -> unit
+  (** [simple_page weeks filename] écrit une page HTML simple permettant de
+    visualiser les événements compris dans weeks *)
 end
diff --git a/lib/dune b/lib/dune
index 7f777ba982e2ee5eca969486a769d6a6be10f622..336a7c92a03291f38f2e0c15a45cc05e6ef1ac77 100644
--- a/lib/dune
+++ b/lib/dune
@@ -1,3 +1,3 @@
 (library
  (name ocaml_edt)
- (libraries calendar yojson))
+ (libraries calendar yojson str ))
diff --git a/ocaml_edt.opam b/ocaml_edt.opam
index f4add2348a8b83f0f8679caa3935a2d1424f2fa0..7c95af65f0f1f8a3b5cb7a70f1c4d27017b225bd 100644
--- a/ocaml_edt.opam
+++ b/ocaml_edt.opam
@@ -16,6 +16,7 @@ depends: [
   "cmdliner"
   "calendar"
   "yojson"
+  "ounit2"
   "odoc" {with-doc}
 ]
 build: [
diff --git a/test/dune b/test/dune
index fb23c18a4ad38c137fdc33637d9d4556109de418..b3379c950351bc986c0d3b35349752b7c726c034 100644
--- a/test/dune
+++ b/test/dune
@@ -1,2 +1,3 @@
 (test
- (name test_ocaml_edt))
+ (name test_ocaml_edt)
+ (libraries ocaml_edt ounit2))
diff --git a/test/test_ocaml_edt.ml b/test/test_ocaml_edt.ml
index 957f8c4064bce2b8257a4fc66509932029409543..4c1097a410431700e2309f2345858b86d954a6dc 100644
--- a/test/test_ocaml_edt.ml
+++ b/test/test_ocaml_edt.ml
@@ -15,4 +15,31 @@ PARTICULAR PURPOSE. See the GNU General Public License for more details.
 You should have received a copy of the GNU General Public License along with
 OCamlEDT. If not, see <https://www.gnu.org/licenses/>.
 
-*)
\ No newline at end of file
+*)
+open OUnit2
+open Ocaml_edt.Timetable
+
+module StringResource = struct
+  type t = string
+
+  let conflicts _s1 _s2 = true
+  let compare = String.compare
+  let title s = s
+  let description s = s
+end
+
+module TT = Make (StringResource)
+open TT
+
+let creneaux =
+  empty_creneaux
+  |> place (creneau "a" (date 2024 09 10) (heure 8 0) 90)
+  |> place (creneau "b" (date 2024 09 10) (heure 14 0) 195)
+  |> place (creneau "c" (date 2024 09 11) (heure 8 0) 90)
+
+let test_write_html _ = simple_page (by_week creneaux) "test.html"
+
+let suite =
+  "Ocaml_edt test suite" >::: [ "test_write_html" >:: test_write_html ]
+
+let () = run_test_tt_main suite