Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails Rails—如何按时间对日期进行分组;你在不同的时区吗?_Ruby On Rails_Postgresql_Datetime - Fatal编程技术网

Ruby on rails Rails—如何按时间对日期进行分组;你在不同的时区吗?

Ruby on rails Rails—如何按时间对日期进行分组;你在不同的时区吗?,ruby-on-rails,postgresql,datetime,Ruby On Rails,Postgresql,Datetime,我有一系列约会,其中日期和时间存储在start\u dateas DateTime下。我想把它们归类为早上、白天或晚上开始。我创建了一个带有标签和范围的散列数组,用于SQL语句,其中我使用CAST(EXTRACT(EPOCH FROM start_time)AS INT)将start_date记录转换为秒数。 这将获取一天中的秒数(86400),并根据开始日期的模数将约会标记为86400。但是,许多约会发生在不同的时区,但它们都存储为UTC。因此,美国东部时间上午8:00的约会相当于美国中部标准

我有一系列约会,其中日期和时间存储在
start\u date
as DateTime下。我想把它们归类为早上、白天或晚上开始。我创建了一个带有标签和范围的散列数组,用于SQL语句,其中我使用
CAST(EXTRACT(EPOCH FROM start_time)AS INT)将
start_date
记录转换为秒数。

这将获取一天中的秒数(86400),并根据
开始日期的模数将约会标记为86400。但是,许多约会发生在不同的时区,但它们都存储为UTC。因此,美国东部时间上午8:00的约会相当于美国中部标准时间上午7:00的约会,但两者在内部存储为UTC下午12:00。这将导致约会被错误地标记为“白天”,而实际上是“上午”(从预订的用户的角度来看)

理想情况下,我希望有某种方法根据用户的时区转换
开始日期
,使其看起来像是在UTC发生的。因此,我希望东部标准时间下午12:00的预约被标记为UTC下午12:00的预约,而不是UTC下午4:00的预约。更具体地说,在执行模运算之前,我想从转换的
开始日期中减去14400秒

我可以加入对用户的任命,其中包含用户的时区。如何将这些信息合并到上面的查询中,以便根据用户在同一记录中的时区,为每条记录使用修改后的
开始日期


我知道我可以通过每个时区的循环,在每个循环中增加/减少特定的秒数,然后组合所有循环的结果来完成这一点,但我想知道是否有一种方法可以在一个查询中完成此操作。

如果您不需要严格基于SQL的解决方案,您可以使用Ruby的
select
方法提取此约会,如本例所示: (我假设约会模型中存在某种保存时区名称的
tz_name
字段)

morning_Appointment=Appointment.all.select do|a|
a、 开始日期。在时区(a.tz\u名称)。小时>6和&a.start\u日期。在时区(a.tz\u名称)。小时<20
结束
编辑:
感谢@moveson指出我的错误,我稍微改变了解决方案。

根据我的评论,我假设我们有三个表:
约会
用户
,和
首选项
。在
约会中
我们有
开始日期
用户id
。在
users
中,我们有
首选项\u id
。在
preferences
中,我们有一些列命名时区,因此我将其称为
tz_name

注意:Postgres时区函数比较混乱。我强烈建议你仔细阅读。这是一个很好的开始

可以使用纯SQL生成时间范围并返回分组结果。如果您需要一次标记和分组多个记录(数千条或更多),那么纯SQL解决方案将是最好的

假设您一次处理1000条或更少的记录,您可能会希望使用Rails作用域,因为这将给您一个ActiveRecord结果。然后使用Ruby的数组方法进行分组

该解决方案看起来是这样的:

#app/user.rb
类用户<应用程序记录
属于:偏好
你有很多约会吗
结束
#app/appointment.rb
班级预约<申请记录
属于:用户
范围:用秒,λ{
联接(用户::首选项)
.select('appoints.*,extract(时区的历元(tz_name,start_date::timestamptz)::time::int as seconds'))
}
#此方法是可选的。如果被排除,则呼叫#秒
#在约会实例上,如果
#.with_秒范围尚未应用。
def秒数
属性['seconds']
结束
def时间范围
返回零,除非秒。是否存在?
如果秒数介于?(0,32399)
“早上好”
elsif秒之间?(32_400,61_199)
“白天”
其他的
“晚上”
结束
结束
结束
范围的
select
部分可能需要一些解释

start\u date::timestamptz
:获取存储为Postgres
timestamp
的开始日期,并将其转换为Postgres服务器时区(大概是UTC)中带时区的Postgres
时间戳

时区(tz\u name,start\u date::timestamptz)
:将带有时区的
时间戳
转换回
时间戳
键入
tz\u name
时区的本地时间

时区(tz\u name,start\u date::timestamptz)::time
:删除日期并保留时间组件

然后我们从时间分量中提取历元,并将其转换为秒

最后,我们将结果转换为整数,以避免在确定时间范围时出现任何问题

现在您可以执行以下操作:

Appointment.all.with_seconds.group_by(&:时间范围)

对于将返回按三个时间范围分组的ID的纯SQL解决方案,请将此方法添加到约会模型中:

def self.grouped\u by\u time\u range
当前\u范围=带有\u秒到\u sql

query=用户的时区是如何存储的?@moveson它实际上是以字符串的形式存储在“首选项”表中,名称基于tz数据库约定。因此,EST用户有一个关联偏好,即“美国/多伦多”。您可以通过以下方式访问它:Appointment.joins(user::preference)好的,为了清楚起见,我们有三个表:
约会
用户
,和
首选项
。在
约会中
我们有
开始日期
用户id
。在
users
中,我们有
首选项\u id
。在
首选项中TIME_RANGES = [
    {label: "Morning", min: 0, max: 32399},
    {label: "Daytime", min: 32400, max: 61199},
    {label: "Evening", min: 61200, max: 86399}
]

cases = TIME_RANGES.map do |r|
    "when CAST (EXTRACT (EPOCH FROM start_date) AS INT) % 86400 between '#{r[:min]}' and '#{r[:max]}' then '#{r[:label]}'"
end

time_ranges = Appointment.select("count(*) as n, case #{time_cases.join(' ')} end as time_range").group('time_range')
morning_appointments = Appointment.all.select do |a|
  a.start_date.in_time_zone(a.tz_name).hour > 6 && a.start_date.in_time_zone(a.tz_name).hour < 20
end
user = User.first
user.appointments.with_seconds.group_by(&:time_range)