Correction des timezones des RDV dans l’API#6207
Correction des timezones des RDV dans l’API#6207AntoineGirard wants to merge 1 commit intoproductionfrom
Conversation
francois-ferrandis
left a comment
There was a problem hiding this comment.
Merci Antoine d'avoir anticipé que le sujet allait me tenir à cœur. ❤️
Cette première proposition, si j'ai bien tout compris, ne corrige pas le problème. J'ai expliqué en commentaire ma compréhension du sujet et proposé des tests et une implémentation.
J'ai aussi quelques suggestions moins importantes (preloading et RdvPlanBlueprint).
|
|
||
| describe "utilise la bonne timezone" do | ||
| context "lorsque la timezone de l'organisation n'est pas définie" do | ||
| it "utilise la timezone de l'instance" do | ||
| get "/api/v1/rdvs", headers: headers, params: { user_id: user.id, agent_id: agent.id }, as: :json | ||
| expect(parsed_response_body["rdvs"].first["starts_at"]).to eq rdv_with_user_and_agent.starts_at.to_s | ||
| expect(parsed_response_body["rdvs"].first["ends_at"]).to eq rdv_with_user_and_agent.ends_at.to_s | ||
| end | ||
| end | ||
|
|
||
| context "lorsque la timezone de l'organisation est définie" do | ||
| let(:organisation) { create(:organisation, time_zone: "America/Guadeloupe") } | ||
|
|
||
| it "utilise la timezone de l'organisation" do | ||
| get "/api/v1/rdvs", headers: headers, params: { user_id: user.id, agent_id: agent.id }, as: :json | ||
| expect(parsed_response_body["rdvs"].first["ends_at"]).to eq rdv_with_user_and_agent.ends_at.in_time_zone("America/Guadeloupe").to_s | ||
| end | ||
| end | ||
| end |
There was a problem hiding this comment.
Ces tests n'explicitent pas le comportement attendu, mais vérifient uniquement que l'on utilise in_time_zone.
Est-on d'accord que le comportement attendu est celui-ci ?
describe "utilise la bonne timezone" do
context "lorsque la timezone de l'organisation n'est pas définie" do
it "utilise la timezone de l'instance" do
rdv = create(:rdv, starts_at: ActiveSupport::TimeZone["Paris"].parse("2026-02-28 14:30"), organisation:, agents: [agent])
get "/api/v1/rdvs", headers: headers, params: { agent_id: agent.id }, as: :json
api_rdv = parsed_response_body["rdvs"].find { _1["id"].to_i == rdv.id }
expect(api_rdv["starts_at"]).to eq "2026-02-28 14:30:00 +0100"
expect(api_rdv["ends_at"]).to eq "2026-02-28 15:15:00 +0100"
end
end
context "lorsque la timezone de l'organisation est définie" do
let(:organisation) { create(:organisation, time_zone: "America/Guadeloupe") }
it "utilise la timezone de l'organisation" do
rdv = create(:rdv, starts_at: ActiveSupport::TimeZone["Paris"].parse("2026-02-28 14:30"), organisation:, agents: [agent])
get "/api/v1/rdvs", headers: headers, params: { agent_id: agent.id }, as: :json
api_rdv = parsed_response_body["rdvs"].find { _1["id"].to_i == rdv.id }
expect(api_rdv["starts_at"]).to eq "2026-02-28 14:30:00 -0400"
expect(api_rdv["ends_at"]).to eq "2026-02-28 15:15:00 -0400"
end
end
end|
|
||
| field :starts_at do |rdv, _options| | ||
| if rdv.motif.organisation.time_zone | ||
| rdv.starts_at.in_time_zone(rdv.motif.organisation.time_zone) |
There was a problem hiding this comment.
Cette implémentation continue de retourner à peu près la même valeur fausse, simplement sous une autre forme.
ActiveSupport::TimeWithZone#in_time_zone a pour effet de convertir un instant d'une timezone à une autre. Cela signifie que si un agent guadeloupéen prend un RDV à 14h30 aujourd'hui, nous stockons 2026-02-28 14:30 CET +01:00 en base, c'est à dire que nous stockons en réalité 2026-02-28 13:30 UTC.
Avec ce nouveau code, nous allons avoir cet effet :
Time.zone.parse("2026-02-28 14:30").in_time_zone("America/Guadeloupe").to_s
=> "2026-02-28 09:30:00 -0400"Cette valeur correspond au même instant erroné que celui que l'API renvoie actuellement, juste formaté pour exprimer qu'il a lieu en -0400. Et donc le serveur de la coop va l'interpréter exactement de la même façon qu'actuellement, c'est à dire à 9h30 du matin en Guadeloupe.
Ce que nous voulons faire plutôt, c'est savoir que nous stockons en base une heure naïve, et nous le faisons en utilisant l'heure locale de Paris. Pour convertir cette heure naïve en heure locale guadeloupéenne, il faut l’interpréter naïvement dans la timezone guadeloupéenne :
ActiveSupport::TimeZone["America/Guadeloupe"].parse("2026-02-28 14:30").to_s
=> "2026-02-28 14:30:00 -0400"
Nous avons bien une représentation de l'instant auquel le RDV a lieu : aujourd'hui à 14h30 en Guadeloupe.
There was a problem hiding this comment.
Tu as raison, je suis allé beaucoup trop vite sur le sujet, et j’ai pondu ce truc erroné.
J’étais biaisé parce que dans les ICS on avait « juste » changé la tzid, mais effectivement ici, il faudrait faire ce que tu proposes juste au-dessus idéalement.
Désolé d’avoir envoyé ce truc beaucoup trop vite
Je vais corriger quand j’aurai un peu de temps
| end | ||
|
|
||
| field :starts_at do |rdv, _options| | ||
| if rdv.motif.organisation.time_zone |
There was a problem hiding this comment.
Question de perf : est-ce que le motif et l'orga est préloadé ? D'ailleurs on peut direct choper l'orga car les Rdv on un organisation_id.
| @@ -1,7 +1,7 @@ | |||
| class RdvBlueprint < Blueprinter::Base | |||
There was a problem hiding this comment.
Il faudra aussi modifier RdvPlanBlueprint qui retourne rdv.starts_at. Peut-être que ça incite à mettre ces logiques de gestion de timezone dans un module séparé qui explique le problème.
Closes #6202
Contexte
Le contexte complet est dans l’issue ci-dessus ☝🏻
Solution
@francois-ferrandis, je sais que cette PR va nous ramener à notre fameuse discussion sur comment on stocke les fuseaux horaires en base. Je sais ce que tu penses, et je te comprends, mais j’ai l’impression qu’ici, on peut fournir une solution simple à nos agents guadeloupéens.
Qu’en penses-tu ?