Rails - 如何在同一个请求中处理多个不同的时区

5
我希望在一个Rails应用中为时间和考勤系统显示跨越多个时区的数据。一些背景信息:
我们制造电子时间钟。人们将它们放在他们的企业中。员工打卡记录出入工作,并记录其工作时间。
时间钟把某人打卡的时间作为 Unix 时间(例如,我们的 Javascript 时间钟实现会这样获取打卡时间:moment().unix())推到我们的 API。然后 API 把它存储在 PostgreSQL 数据库中作为 timestamp without time zone
当用户登录网站时,around_filter 根据此用户组织的设置为请求设置适当的时区。

问题出现在我们有一个跨越多个时区的组织。例如,一个在每个澳大利亚首都城市都设有办事处的企业将跨越三个时区(夏令时期间更多)。然而,将有一个人在中央办公室需要检查整个组织的数据 - 我们称他们为经理。

假设我们的经理位于悉尼,现在是上午11点。他们管理三个办事处 - 分别位于悉尼、布里斯班(夏令时期间比悉尼晚一个小时)和阿德莱德(夏令时期间比悉尼晚半小时)。员工在当地时间9点打卡。因此,在经理的仪表板上,所有打卡时间都应显示为上午9点。 然而,当前的实现(使用around_filter)将分别显示为上午9点、上午8点和上午8:30,因为它们将使用悉尼的时区进行偏移。

对来自不同城市的员工应用了一层过滤,因此可以告诉系统A来自悉尼,B来自阿德莱德,C来自布里斯班。问题 - 我需要建议 - 是如何最有效地让Rails显示不同时区的偏移。

奖励信用: 除了展示时间,我们还需要读取输入。例如,有人可能早到了5分钟,他们的出勤表需要进行更正。如果当地经理(即布里斯班的某个人)纠正了布里斯班雇员的时间表,那么管理起来应该相对容易——因为我们知道他们在布里斯班,我们可以将请求的时区设置为布里斯班,让ActiveRecord为我们执行偏移量计算。但是,如果总经理(位于悉尼但管理所有时区)想要进行更改,那么我们需要能够根据他们的时区正确转换他们的输入为基于协调世界时(UTC)的时间。关于如何最好地做到这一点的任何建议都将不胜感激。

问题的具体例子

在我的数据库中,我的打卡记录表(clok_ins table)看起来像这样:

user_id (integer) | time (timestamp without time zone)
------------------|-----------------------------------
1                 | "2012-09-25 22:00:00.0"
2                 | "2012-09-25 22:30:00.0"
3                 | "2012-09-25 23:00:00.0"

我的用户表看起来像这样:

user_id (integer) | time_zone (varchar)
------------------|-----------------------------------
1                 | "Sydney"
2                 | "Adelaide"
3                 | "Brisbane"

(这只是一个简化,实际上还有一个关于用户和他们所在时区之间的连接)
如果我们将每个用户的时区应用于他们打卡的时间,我们会发现他们都处于当地时间早上9点。例如,在UTC时间的2012年09月25日23:00:00.0,在布里斯班(+1000)的时间是2012年09月26日09:00:00.0。Rails的一般做法是使用around_filter为请求设置时区;如果我在这里这样做,每个时间都会相差半个小时,这是不正确的。因此,我正在寻求关于处理来自不同时区的时间的最佳实践建议。

你的措辞有些混乱。实际问题是什么?能否请您简化一下您的问题?以“the issue”开头的那个句子有一些语法错误,这使得很难理解您想要做什么。也许您可以展示一些代码来说明?(顺便说一句 - 我也在处理时间时钟方面工作,如果我能理解您的问题,我很乐意帮忙。) - Matt Johnson-Pint
@MattJohnson 谢谢 - 那句话太糟糕了 - 我已经重写了它。我还添加了一些更具体的例子。 - Alex Ghiculescu
我仍然不明白你的问题是什么。你似乎只是在寻求广泛的建议。我唯一能提供的建议是要注意在夏令时回退转换期间进行本地到UTC转换,因为结果可能会有歧义。但如果你正在寻找Rails代码,也许你应该展示一下你已经尝试过的,并专注于一个特定的问题。就像你目前所措辞的那样,这个问题太过广泛了。 - Matt Johnson-Pint
1个回答

4

我认为最简单的方法是在呈现时间时使用Time.use_zone方法。例如:

Time.use_zone('Sydney') { Time.current }

Time.use_zone(person.office.time_zone) { person.clock_ins.last.time_stamp }

这个功能“允许在提供的块内本地覆盖Time.zone;完成后将Time.zone重置为现有值。”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接