Files
Geneweb/lib/dateDisplay.ml
2024-03-05 22:01:20 +01:00

676 lines
23 KiB
OCaml
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(* Copyright (c) 1998-2007 INRIA *)
open Config
open Def
open Util
open Gwdb
let get_wday conf = function
| Dgreg (({ prec = Sure; delta = 0 } as d), _) when d.day <> 0 && d.month <> 0
->
let jd = Calendar.sdn_of_gregorian d in
let wday =
let jd_today = Calendar.sdn_of_gregorian conf.today in
let x = conf.today_wd - jd_today + jd in
if x < 0 then 6 + ((x + 1) mod 7) else x mod 7
in
" (" ^ transl_nth conf "(week day)" wday ^ ")"
| _ -> ""
let death_symbol conf =
try List.assoc "death_symbol" conf.base_env with Not_found -> ""
let code_date conf encoding d m y =
let apply_date_code = function
| 'd' -> string_of_int d
| 'm' -> transl_nth conf "(month)" (m - 1)
| 'y' -> string_of_int y
| c -> "%" ^ String.make 1 c
in
let rec loop i =
if i = String.length encoding then ""
else
let s, i =
match encoding.[i] with
| '%' when i + 1 < String.length encoding ->
let s = apply_date_code encoding.[i + 1] in
(s, i + 1)
| '[' -> (
try
(* code similar to Util.gen_decline *)
let len = String.length encoding in
let j = String.index_from encoding i ']' in
let k = String.index_from encoding i '|' in
if k < j && j + 2 < len && encoding.[j + 1] = '%' then
let s = apply_date_code encoding.[j + 2] in
let s1 =
if start_with_vowel conf s then
String.sub encoding (k + 1) (j - k - 1)
else String.sub encoding (i + 1) (k - i - 1)
in
(s1 ^ s, j + 2)
else (String.make 1 '[', i)
with Not_found -> (String.make 1 '[', i))
| c -> (String.make 1 c, i)
in
s ^ loop (i + 1)
in
loop 0
let code_dmy conf d =
let encoding =
let n =
if d.day = 1 then 0
else if d.day != 0 then 1
else if d.month != 0 then 2
else 3
in
transl_nth conf "(date)" n
in
code_date conf encoding d.day d.month d.year
let default_french_month =
let tab =
[|
"Vendemiaire";
"Brumaire";
"Frimaire";
"Nivose";
"Pluviose";
"Ventose";
"Germinal";
"Floreal";
"Prairial";
"Messidor";
"Thermidor";
"Fructidor";
"Extra";
|]
in
fun m -> tab.(m)
let default_hebrew_month =
let tab =
[|
"Tishri";
"Heshvan";
"Kislev";
"Tevet";
"Shevat";
"AdarI";
"AdarII";
"Nisan";
"Iyyar";
"Sivan";
"Tammuz";
"Av";
"Elul";
|]
in
fun m -> tab.(m)
let french_month conf m =
let r = transl_nth conf "(french revolution month)" m in
if r = "[(french revolution month)]" then "[" ^ default_french_month m ^ "]"
else r
let hebrew_month conf m =
let r = transl_nth conf "(hebrew month)" m in
if r = "[(hebrew month)]" then "[" ^ default_hebrew_month m ^ "]" else r
let code_french_year conf y =
transl_nth conf "year/month/day" 3
^ " "
^ if y >= 1 && y < 4000 then Mutil.roman_of_arabian y else string_of_int y
let code_french_date conf d m y =
let s =
if d = 0 then ""
else string_of_int d ^ if d = 1 then "<sup>er</sup>" else ""
in
let s =
if m = 0 then ""
else s ^ (if s = "" then "" else " ") ^ french_month conf (m - 1)
in
s ^ (if s = "" then "" else " ") ^ code_french_year conf y
let code_hebrew_date conf d m y =
let s = if d = 0 then "" else string_of_int d in
let s =
if m = 0 then ""
else s ^ (if s = "" then "" else " ") ^ hebrew_month conf (m - 1)
in
s ^ (if s = "" then "" else " ") ^ string_of_int y
let string_of_on_prec_dmy_aux conf sy sy2 d =
match d.prec with
| Sure ->
if d.day = 0 && d.month = 0 then transl conf "in (year)" ^ " " ^ sy
else if d.day = 0 then transl_decline conf "in (month year)" sy
else transl_decline conf "on (day month year)" sy
| About | Before | After ->
let s = sy in
if d.prec = About then transl_decline conf "about (date)" s
else if d.prec = Before then transl_decline conf "before (date)" s
else transl_decline conf "after (date)" s
| Maybe ->
let s =
if d.day = 0 && d.month = 0 then transl conf "in (year)" ^ " " ^ sy
else if d.day = 0 then transl_decline conf "in (month year)" sy
else transl_decline conf "on (day month year)" sy
in
transl_decline conf "possibly (date)" s
| OrYear d2 ->
let s =
if d.day = 0 && d.month = 0 then transl conf "in (year)" ^ " " ^ sy
else if d.day = 0 then transl_decline conf "in (month year)" sy
else transl_decline conf "on (day month year)" sy
in
let s2 =
if d2.day2 = 0 && d2.month2 = 0 then transl conf "in (year)" ^ " " ^ sy2
else if d2.day2 = 0 then transl_decline conf "in (month year)" sy2
else transl_decline conf "on (day month year)" sy2
in
s ^ " " ^ transl conf "or" ^ " " ^ Mutil.nominative s2
| YearInt d2 ->
let s =
if d.day = 0 && d.month = 0 then sy
else if d.day = 0 then sy
else transl_decline conf "on (day month year)" sy
in
let s2 =
if d2.day2 = 0 && d2.month2 = 0 then sy2
else if d2.day2 = 0 then sy2
else transl_decline conf "on (day month year)" sy2
in
transl conf "between (date)"
^ " " ^ s ^ " " ^ transl_nth conf "and" 0 ^ " " ^ Mutil.nominative s2
let replace_spaces_by_nbsp s =
let rec loop i len =
if i = String.length s then Buff.get len
else if s.[i] = ' ' then loop (i + 1) (Buff.mstore len "&nbsp;")
else loop (i + 1) (Buff.store len s.[i])
in
loop 0 0
let string_of_on_prec_dmy conf sy sy2 d =
Adef.safe
@@
let r = string_of_on_prec_dmy_aux conf sy sy2 d in
replace_spaces_by_nbsp r
let string_of_on_french_dmy conf d =
let sy = code_french_date conf d.day d.month d.year in
let sy2 =
match d.prec with
| OrYear d2 | YearInt d2 -> code_french_date conf d2.day2 d2.month2 d2.year2
| _ -> ""
in
string_of_on_prec_dmy conf sy sy2 d
let string_of_on_hebrew_dmy conf d =
let sy = code_hebrew_date conf d.day d.month d.year in
let sy2 =
match d.prec with
| OrYear d2 | YearInt d2 -> code_hebrew_date conf d2.day2 d2.month2 d2.year2
| _ -> ""
in
string_of_on_prec_dmy conf sy sy2 d
let string_of_prec_dmy conf s s2 d =
Adef.safe
@@
match d.prec with
| Sure -> Mutil.nominative s
| About -> transl_decline conf "about (date)" s
| Before -> transl_decline conf "before (date)" s
| After -> transl_decline conf "after (date)" s
| Maybe -> transl_decline conf "possibly (date)" s
| OrYear _ -> s ^ " " ^ transl conf "or" ^ " " ^ Mutil.nominative s2
| YearInt _ ->
transl conf "between (date)"
^ " " ^ s ^ " " ^ transl_nth conf "and" 0 ^ " " ^ Mutil.nominative s2
let string_of_dmy_aux fn conf d =
let sy = code_dmy conf d in
let sy2 =
match d.prec with
| OrYear d2 | YearInt d2 -> code_dmy conf (Date.dmy_of_dmy2 d2)
| _ -> ""
in
fn conf sy sy2 d
let string_of_on_dmy conf d = string_of_dmy_aux string_of_on_prec_dmy conf d
let string_of_dmy conf d = string_of_dmy_aux string_of_prec_dmy conf d
(* ************************************************************************ *)
(* [Fonc] translate_dmy : config -> (string * string * string) ->
calendar -> bool -> (string * string * string) *)
(* ************************************************************************ *)
(** [Description] : Traduit en fonction du calendrier, le mois et/ou l'année
d'une date et renvoie le triplet conformément au format
de la date.
[Args] :
- conf : configuration de la base
- (fst, snd, trd) : la date au bon format
- cal : calendar
- short : booléen pour savoir si on affiche au format court, e.g.
VD/Vendémiaire
[Retour] : (string * string * string) : date traduite
[Rem] : Non exporté en clair hors de ce module. *)
let translate_dmy conf (fst, snd, trd) cal short =
let translate_month m =
match cal with
| Dfrench when m <> "" ->
if short then Util.short_f_month (int_of_string m)
else french_month conf (int_of_string m)
| Dhebrew when m <> "" ->
if short then
String.uppercase_ascii
(String.sub (hebrew_month conf (int_of_string m)) 0 2)
else hebrew_month conf (int_of_string m)
| _ -> m
in
let translate_year y =
match cal with
| Dfrench ->
let y1 = int_of_string y in
if y1 >= 1 && y1 < 4000 then Mutil.roman_of_arabian y1 else y
| _ -> y
in
match transl conf "!dates order" with
| "yymmdd" | "yyyymmdd" -> (translate_year fst, translate_month snd, trd)
| "mmddyyyy" -> (translate_month fst, snd, translate_year trd)
| _ -> (fst, translate_month snd, translate_year trd)
(** [decode_dmy conf dmy]
Returns a triplet corresponding to day/month/year, arranged in
the order defined by [!dates order] keyword in the lexicon.
Supported formats are: "dmyyyy" / "mmddyyyy" / "yyyymmdd" / "ddmmyyyy" and "ddmmyy".
NB: "yy" and "yyyy" variants will produce the same output ([string_of_int] without padding)
If the format is not supported "ddmmyyyy" is used.
*)
let decode_dmy conf d =
match transl conf "!dates order" with
| "dmyyyy" ->
(string_of_int d.day, string_of_int d.month, string_of_int d.year)
| "mmddyyyy" -> (
(* Si le jour et/ou le mois n'est pas sur 2 caractères, *)
(* on rajoute les 0 nécessaires. *)
match (d.day, d.month, d.year) with
| 0, 0, year -> ("", "", string_of_int year)
| 0, month, year ->
let m = Printf.sprintf "%02d" month in
(m, "", string_of_int year)
| day, month, year ->
let d = Printf.sprintf "%02d" day in
let m = Printf.sprintf "%02d" month in
(m, d, string_of_int year))
| "yyyymmdd" | "yymmdd" -> (
(* Si le jour et/ou le mois n'est pas sur 2 caractères, *)
(* on rajoute les 0 nécessaires. *)
match (d.day, d.month, d.year) with
| 0, 0, year -> (string_of_int year, "", "")
| 0, month, year ->
let m = Printf.sprintf "%02d" month in
(string_of_int year, m, "")
| day, month, year ->
let d = Printf.sprintf "%02d" day in
let m = Printf.sprintf "%02d" month in
(string_of_int year, m, d))
| "ddmmyyyy" | "ddmmyy" | _ -> (
(* Si le jour et/ou le mois n'est pas sur 2 caractères, *)
(* on rajoute les 0 nécessaires. *)
match (d.day, d.month, d.year) with
| 0, 0, year -> ("", "", string_of_int year)
| 0, month, year ->
let m = Printf.sprintf "%02d" month in
("", m, string_of_int year)
| day, month, year ->
let d = Printf.sprintf "%02d" day in
let m = Printf.sprintf "%02d" month in
(d, m, string_of_int year))
let gregorian_precision conf d =
if d.delta = 0 then string_of_dmy conf d
else
let d2 =
Calendar.gregorian_of_sdn d.prec (Calendar.sdn_of_gregorian d + d.delta)
in
Adef.safe
@@ transl conf "between (date)"
^ " "
^ (string_of_on_dmy conf d :> string)
^ " " ^ transl_nth conf "and" 0 ^ " "
^ (string_of_on_dmy conf d2 :> string)
let string_of_date_aux ?(link = true) ?(dmy = string_of_dmy)
?(sep = Adef.safe " ") conf =
let mk_link c d (s : Adef.safe_string) =
Adef.safe
@@ Printf.sprintf
{|<a href="%sm=CAL&y%c=%d&m%c=%d&d%c=%d&t%c=1" class="date">%s</a>|}
(commd conf :> string)
c d.year c d.month c d.day c
(s :> string)
in
function
| Dgreg (d, Dgregorian) ->
let s = dmy conf d in
if link && d.day > 0 then mk_link 'g' d s else s
| Dgreg (d, Djulian) ->
let cal_prec =
if d.year < 1582 then Adef.safe ""
else " (" ^<^ gregorian_precision conf d ^>^ ")"
in
let d1 = Calendar.julian_of_gregorian d in
let year_prec =
if
(d1.month > 0 && d1.month < 3)
|| (d1.month = 3 && d1.day > 0 && d1.day < 25)
then Printf.sprintf " (%d/%d)" (d1.year - 1) (d1.year mod 10)
else ""
in
let s =
dmy conf d1 ^^^ year_prec ^<^ sep
^^^ transl_nth conf "gregorian/julian/french/hebrew" 1
^<^ cal_prec
in
if link && d1.day > 0 then mk_link 'j' d1 s else s
| Dgreg (d, Dfrench) -> (
let d1 = Calendar.french_of_gregorian d in
let s = string_of_on_french_dmy conf d1 in
let s = if link && d1.day > 0 then mk_link 'f' d1 s else s in
match d.prec with
| Sure | About | Before | After | Maybe ->
s ^^^ sep ^^^ " (" ^<^ gregorian_precision conf d ^>^ ")"
| OrYear _ | YearInt _ -> s)
| Dgreg (d, Dhebrew) -> (
let d1 = Calendar.hebrew_of_gregorian d in
let s = string_of_on_hebrew_dmy conf d1 in
match d.prec with
| Sure | About | Before | After | Maybe ->
s ^^^ sep ^^^ " (" ^<^ gregorian_precision conf d ^>^ ")"
| OrYear _ | YearInt _ -> s)
| Dtext t -> "(" ^<^ (Util.escape_html t :> Adef.safe_string) ^>^ ")"
let string_of_ondate ?link conf d =
(string_of_date_aux ?link ~dmy:string_of_on_dmy conf d :> string)
|> Util.translate_eval |> Adef.safe
let string_of_date conf = function
| Dgreg (d, _) -> string_of_dmy conf d
| Dtext t -> (Util.escape_html t :> Adef.safe_string)
let string_slash_of_date conf date =
let rec slashify_dmy (fst, snd, trd) d =
let code fst snd trd =
List.fold_right
(fun s accu -> if s <> "" then s ^ "/" ^ accu else accu)
[ fst; snd ] trd
in
match d.prec with
| OrYear d2 ->
let sy = code fst snd trd in
let d2 = Date.dmy_of_dmy2 d2 in
let sy2 = slashify_dmy (decode_dmy conf d2) d2 in
sy ^ " " ^ transl conf "or" ^ " " ^ sy2
| YearInt d2 ->
let sy = code fst snd trd in
let d2 = Date.dmy_of_dmy2 d2 in
let sy2 = slashify_dmy (decode_dmy conf d2) d2 in
transl conf "between (date)"
^ " " ^ sy ^ " " ^ transl_nth conf "and" 0 ^ " " ^ sy2
| _ ->
let sy = code fst snd trd in
(string_of_prec_dmy conf sy "" d :> string)
in
match date with
| Dtext t -> (Util.escape_html t :> Adef.safe_string)
| Dgreg (d, cal) -> (
Adef.safe
@@
match cal with
| Dgregorian -> slashify_dmy (decode_dmy conf d) d
| Djulian ->
let d1 = Calendar.julian_of_gregorian d in
slashify_dmy (translate_dmy conf (decode_dmy conf d1) Djulian true) d1
^ " ("
^ transl_nth conf "gregorian/julian/french/hebrew" 1
^ ")"
| Dfrench ->
let d1 = Calendar.french_of_gregorian d in
slashify_dmy (translate_dmy conf (decode_dmy conf d1) Dfrench true) d1
| Dhebrew ->
let d1 = Calendar.french_of_gregorian d in
slashify_dmy (translate_dmy conf (decode_dmy conf d1) Dhebrew true) d1
^ " ("
^ transl_nth conf "gregorian/julian/french/hebrew" 3
^ ")")
let string_of_age conf a =
Adef.safe
@@
match a with
| { day = 0; month = 0; year = y } ->
if y > 1 then string_of_int y ^ " " ^ transl conf "years old"
else if y = 1 then transl conf "one year old"
else transl conf "birth"
| { day = 0; month = m; year = y } ->
if y >= 2 then string_of_int y ^ " " ^ transl conf "years old"
else if y > 0 || m > 1 then
string_of_int ((y * 12) + m) ^ " " ^ transl conf "months old"
else if m = 1 then transl conf "one month old"
else transl conf "less than one month old"
| { day = d; month = m; year = y } ->
if y >= 2 then string_of_int y ^ " " ^ transl conf "years old"
else if y > 0 || m > 1 then
string_of_int ((y * 12) + m) ^ " " ^ transl conf "months old"
else if m = 1 then transl conf "one month old"
else if d >= 2 then string_of_int d ^ " " ^ transl conf "days old"
else if d = 1 then transl conf "one day old"
else "0"
(* ************************************************************************ *)
(* [Fonc] prec_text : config -> Def.dmy -> string *)
(* ************************************************************************ *)
(** [Description] : Renvoie la précision d'une date.
[Args] :
- conf : configuration de la base
- d : Def.dmy
[Retour] : string
[Rem] : Exporté en clair hors de ce module. *)
let prec_text conf d =
match d.prec with
| About -> (
(* On utilise le dictionnaire pour être sur *)
(* que ce soit compréhensible de tous. *)
match transl conf "about (short date)" with "ca" -> "ca " | s -> s)
| Maybe -> "?"
| Before -> "<"
| After -> ">"
| OrYear _ -> "|"
| YearInt _ -> ".."
| Sure -> ""
(* ************************************************************************ *)
(* [Fonc] month_text : Def.dmy -> string *)
(* ************************************************************************ *)
(** [Description] : Renvoie le mois d'une date.
[Args] :
- d : Def.dmy
[Retour] : string
[Rem] : Exporté en clair hors de ce module. *)
let month_text d = if d.month = 0 then "" else string_of_int d.month
(* ************************************************************************ *)
(* [Fonc] year_text : Def.dmy -> string *)
(* ************************************************************************ *)
(** [Description] : Renvoie l'année d'une date.
[Args] :
- d : Def.dmy
[Retour] : string
[Rem] : Exporté en clair hors de ce module. *)
let year_text d =
match d.prec with
| OrYear d2 when d.year <> d2.year2 ->
string_of_int d.year ^ "/" ^ string_of_int d2.year2
| YearInt d2 when d.year <> d2.year2 ->
string_of_int d.year ^ ".." ^ string_of_int d2.year2
| _ -> string_of_int d.year
(* ************************************************************************ *)
(* [Fonc] prec_year_text : config -> Def.dmy -> string *)
(* ************************************************************************ *)
(** [Description] : Renvoie la précision d'une date et l'année de la date.
[Args] :
- conf : configuration de la base
- d : Def.dmy
[Retour] : string
[Rem] : Exporté en clair hors de ce module. *)
let prec_year_text conf d =
let s =
match d.prec with
| About -> (
(* On utilise le dictionnaire pour être sur *)
(* que ce soit compréhensible de tous. *)
match transl conf "about (short date)" with "ca" -> "ca " | s -> s)
| Maybe -> "?"
| Before -> "/"
| _ -> ""
in
let s = s ^ year_text d in
match d.prec with After -> s ^ "/" | _ -> s
(* ********************************************************************** *)
(* [Fonc] short_dates_text : config -> base -> person -> string *)
(* ********************************************************************** *)
(** [Description] : Renvoie la concatenation de l'année de naissance et
l'année de décès (si trouvée par get_birth_death_date). La précision
de la date est ajoutée pour chaque année.
L'affichage est le suivant :
* 1700-1780 (date naissance - date décès)
* 1700- (naissance - décédé)
* 1700 (naissance - vivant)
* †1780 (pas date naissance - date décès)
*(pas date naissance - décédé)
[Args] :
- conf : configuration de la base
- base : base de donnée
- p : person
[Retour] : string
[Rem] : Exporté en clair hors de ce module. *)
let short_dates_text conf base p =
Adef.safe
@@
if authorized_age conf base p then
let birth_date, death_date, _ = Gutil.get_birth_death_date p in
let s =
match (birth_date, death_date) with
| Some (Dgreg (b, _)), Some (Dgreg (d, _)) ->
prec_year_text conf b ^ "-" ^ prec_year_text conf d
| Some (Dgreg (b, _)), _ -> (
(* La personne peut être décédée mais ne pas avoir de date. *)
match get_death p with
| Death (_, _) | DeadDontKnowWhen | DeadYoung ->
prec_year_text conf b ^ "-"
| _ -> prec_year_text conf b)
| _, Some (Dgreg (d, _)) -> death_symbol conf ^ prec_year_text conf d
| _, _ -> (
(* La personne peut être décédée mais ne pas avoir de date. *)
match get_death p with
| Death (_, _) | DeadDontKnowWhen | DeadYoung -> death_symbol conf
| _ -> "")
in
if s <> "" then " <bdo dir=\"ltr\">" ^ s ^ "</bdo>" else s
else ""
(* ********************************************************************** *)
(* [Fonc] short_marriage_date_text :
config -> base -> person -> person -> string *)
(* ********************************************************************** *)
(** [Description] : Renvoie l'année de la date de mariage ansi que la
précision de la date.
[Args] :
- conf : configuration de la base
- base : base de donnée
- p1 : conjoint 1
- p2 : conjoint 2
[Retour] : string
[Rem] : Exporté en clair hors de ce module. *)
let short_marriage_date_text conf base fam p1 p2 =
Adef.safe
@@
if authorized_age conf base p1 && authorized_age conf base p2 then
match Date.cdate_to_dmy_opt (get_marriage fam) with
| Some d ->
"<span style=\"font-size:70%\">" ^ prec_year_text conf d ^ "</span>"
| None -> ""
else ""
(* ********************************************************************** *)
(* [Fonc] short_family_date_text :
config -> base -> bool -> family -> string *)
(* ********************************************************************** *)
(** [Description] : Renvoie l'année marriage - séparation
ou uniquement l'année de la séparation.
[Args] :
- conf : configuration de la base
- base : base de donnée
- bool : si faux, uniquement dates de la séparation
- family : famille
[Retour] : string
[Rem] : Exporté en clair hors de ce module. *)
let short_family_dates_text conf _base marr_sep fam =
let marr_dates_aux =
match Date.cdate_to_dmy_opt (Gwdb.get_marriage fam) with
| Some dmy -> Some (prec_year_text conf dmy)
| None -> Some ""
in
let sep_dates_aux =
match
List.find_opt
(fun e ->
e.efam_name = Efam_Divorce
|| e.efam_name = Efam_Annulation
|| e.efam_name = Efam_Separated)
(Gwdb.get_fevents fam)
with
| None -> None
| Some e -> (
match Date.cdate_to_dmy_opt e.efam_date with
| None -> Some ""
| Some dmy -> Some (prec_year_text conf dmy))
in
Adef.safe
@@
if marr_sep then
match (marr_dates_aux, sep_dates_aux) with
| Some m, Some s -> m ^ "-" ^ s
| Some m, None -> m
| None, _ -> ""
else Option.value ~default:"" sep_dates_aux
(* For public interfce, force [string_of_prec_dmy] args to be safe strings *)
let string_of_prec_dmy conf s s2 d =
let s = (s : Adef.safe_string :> string) in
let s2 = (s2 : Adef.safe_string :> string) in
string_of_prec_dmy conf s s2 d