我原来的问题太紧凑而且到处都是,所以我试着在这里清理它。试图弄清楚如何在美国的SQL脚本中正确实现DST。
a.ActualEnd = DateTime value& c.TimeZoneBias =根据用户本地时间偏移的分钟数
更新:下面的整个脚本
INSERT INTO [AS400].S062f7ar.APLUS83MDS.PEPAPPTS01
(PPCONO, PPREP1, PPDATE, PPCOUNT)
select '1' as PPCONO,
b.new_SalesrepId as PPREP1,
MAX(CONVERT(varchar(8),
(a.ActualEnd - c.TimeZoneBias / cast(24 * 60 as float)), 112)) as PPDATE,
count(b.new_SalesrepId) as PPCOUNT
from ActivityPointerBase as a
join SystemUserExtensionBase as b
on b.SystemUserId = a.OwnerId
join UserSettingsBase as c
on c.SystemUserId = b.SystemUserId
where b.new_SalesrepId <> '99999999'
and a.ActivityTypeCode = '4201'
and b.new_SalesrepId is not NULL
and a.StateCode = '1'
and CONVERT(varchar(8),
a.ActualEnd - c.TimeZoneBias / cast(24 * 60 as float),
112) >= dateadd(day,datediff(day,
1,
CONVERT(varchar(8),
GetDate(),
112)
),
0)
and CONVERT(varchar(8),
a.ActualEnd - c.TimeZoneBias / cast(24 * 60 as float),
112) < dateadd(day,datediff(day,
0,
CONVERT(varchar(8),
GetDate(),
112)
),
0)
group by b.new_SalesrepId,
CONVERT(varchar(8),
(a.ActualEnd - c.TimeZoneBias / cast(24 * 60 as float)), 112)
order by b.new_SalesrepId ASC;
希望将美国的DST正确纳入此声明。我不希望每次DST到处时都必须手动更改脚本。
答案 0 :(得分:1)
由于这些要点未在问题中明确定义,我在此陈述我对它们的假设。
我看到三种可能的解决方案。我相信所有这些都是可行的,假设首先在其中几个上执行一些数据迁移。
在表ActivityPointerBase中将ActualEnd列实现为DATETIMEOFFSET类型。应用程序将需要提供用户当前本地偏移量的时间戳。然后简单地使用CONVERT(DATE,a.ActualEnd)来获取用户的本地日期。甚至允许您比较用户之间的时间戳,因为数据库可以在比较两个DATETIMEOFFSET值时隐式执行偏移计算。还有一些函数可以将DATETIMEOFFSET转换为UTC DATETIME,以便与其他表进行比较。
SELECT '1' as PPCONO,
b.new_SalesrepId AS PPREP1,
CONVERT(VARCHAR(8), a.ActualEnd, 112) AS PPDATE,
COUNT(b.new_SalesrepId) AS PPCOUNT
FROM ActivityPointerBase AS a
JOIN SystemUserExtensionBase AS b ON b.SystemUserId = a.OwnerId
WHERE b.new_SalesrepId <> '99999999'
AND a.ActivityTypeCode = '4201'
AND b.new_SalesrepId IS NOT NULL
AND a.StateCode = '1'
AND CONVERT(DATE, a.ActualEnd) = CONVERT(DATE, DATEADD(DAY, -1, GETDATE()))
GROUP BY b.new_SalesrepId,
CONVERT(VARCHAR(8), a.ActualEnd, 112)
ORDER BY b.new_SalesrepId ASC;
将用户本地时间和UTC时间存储在数据库中。由于您以后显然需要用户本地时间,因此请将其存储为系统的要求。然后,您在ActivityPointerBase表中同时拥有ActualEnd和UserActualEnd列。
SELECT '1' as PPCONO,
b.new_SalesrepId AS PPREP1,
CONVERT(VARCHAR(8), a.UserActualEnd, 112) AS PPDATE,
COUNT(b.new_SalesrepId) AS PPCOUNT
FROM ActivityPointerBase AS a
JOIN SystemUserExtensionBase AS b ON b.SystemUserId = a.OwnerId
WHERE b.new_SalesrepId <> '99999999'
AND a.ActivityTypeCode = '4201'
AND b.new_SalesrepId IS NOT NULL
AND a.StateCode = '1'
AND CONVERT(DATE, a.UserActualEnd) = CONVERT(DATE, DATEADD(DAY, -1, GETDATE()))
GROUP BY b.new_SalesrepId,
CONVERT(VARCHAR(8), a.UserActualEnd, 112)
ORDER BY b.new_SalesrepId ASC;
构建一个系统,您可以从UTC时间戳重建用户本地时间。我将在下面概述一个简单的,但它绝不是测试,也不是重建它们的唯一方法。
首先,您需要在UserSettingsBase表中使用ObserveDst列,因为某些状态遵循DST而某些状态不遵循,因此TimeZoneBias不足以重建用户本地时间。
ObserveDst INT NOT NULL
如果他们观察到DST,则包含值1,否则为0。
其次,您将需要一个DST开始和结束时间戳表,因为他们已经随着时间的推移更改了定义,并且可能在将来再次这样做。我建议将一张桌子拆分成间隔。这样,您可以与其他表执行简单连接,而不是定义和调用每个记录操作的函数。
CREATE TABLE Dst
(
BeginDT DATETIME NOT NULL,
EndDT DATETIME NOT NULL,
DstBias INT NOT NULL,
PRIMARY KEY(BeginDT)
);
并在表上放置(BeginDT,EndDT,DstBias)索引。它可以包含这种情况下的所有列,因为它不会是一个非常大的表。 2014年,您将拥有以下记录:
('2014-01-01 00:00:00', '2014-03-09 02:00:00', 0)
('2014-03-09 02:00:00', '2014-11-02 03:00:00', 60)
('2014-11-02 03:00:00', '2015-01-01 00:00:00', 0)
11月小时可能看起来有点奇怪,但此表的目的是从本地非DST时间转移到本地DST时间。和2014-11-02 03:00:00美国东部时间是2014-11-02 02:00:00美国东部时间。另外,请注意这些是闭合打开的时间间隔,这意味着包括第一个时间戳和最多但不包括最后一个时间戳。如果您在DST之前有历史日期,则可以将它们全部压缩到一个区间。你甚至可以加入每年的开始和结束时间间隔,只给你N年的N + 3条记录。
美国交通部为选择遵守夏令时(http://www.dot.gov/regulations/daylight-saving-time)的州定义联邦法定日期。我建议您查看他们或其他合理的官方网站,了解您所需年份的正确日期。
然后您只需加入Dst表并执行基本偏移计算。
SELECT '1' as PPCONO,
b.new_SalesrepId AS PPREP1,
CONVERT(VARCHAR(8), CONVERT(DATE, DATEADD(MINUTE, d.DstBias*c.ObserveDst-c.TimeZoneBias, a.ActualEnd)), 112) AS PPDATE,
COUNT(b.new_SalesrepId) AS PPCOUNT
FROM ActivityPointerBase AS a
JOIN SystemUserExtensionBase AS b ON b.SystemUserId = a.OwnerId
JOIN UserSettingsBase AS c ON c.SystemUserId = b.SystemUserId
JOIN Dst AS d ON DATEADD(MINUTE, -c.TimeZoneBias, a.ActualEnd) >= d.BeginDT AND DATEADD(MINUTE, -c.TimeZoneBias, a.ActualEnd) < d.EndDT
WHERE b.new_SalesrepId <> '99999999'
AND a.ActivityTypeCode = '4201'
AND b.new_SalesrepId IS NOT NULL
AND a.StateCode = '1'
AND CONVERT(DATE, DATEADD(MINUTE, d.DstBias*c.ObserveDst-c.TimeZoneBias, a.ActualEnd)) = CONVERT(DATE, DATEADD(DAY, -1, GETDATE()))
GROUP BY b.new_SalesrepId,
CONVERT(VARCHAR(8), CONVERT(DATE, DATEADD(MINUTE, d.DstBias*c.ObserveDst-c.TimeZoneBias, a.ActualEnd)), 112)
ORDER BY b.new_SalesrepId ASC;