在SQLITE中通过纪元转换日期的夏令时问题

5us2dqdw  于 2022-11-15  发布在  SQLite
关注(0)|答案(1)|浏览(215)

我正在使用一个非常大的数据,其中精度非常重要,我正在观察人类在一天中不同时间的行为。我使用以下代码从纪元日期提取日期(使用rsqlite-基本上只允许在R内使用)查询的构建方式相同):

dbGetQuery(db,"SELECT datetime(started_at, 'unixepoch', 'utc','-4 hours') FROM biketransactions limit 3")

我使用的数据是在纽约记录的,但有一个问题是,纽约并不总是比UTC时区晚4小时。如果没有夏令时,它的时差为-5小时。使用此方法,我将获得“夏季”期间的正确小时戳,但“冬季时间”中的数据将始终假定为一小时后的数据。这将极大地影响我的最终结果。有哪些方法可以在考虑夏令时的情况下转换纪元时间?

vecaoik1

vecaoik11#

这里有三种方法,

  • 第一个使用包含夏令时开始和结束日期和时间的表。
  • 第二个使用CTE(公用表表达式)动态构建仅适用于本年度的单行表(可能不适合)
  • 第三种方法在第二种方法的基础上更进一步,因为它加入了基于开始日期日期的年份的CTE(动态确定夏令时)。因此,它可以处理任何日期(假设夏令时的确定仍然是从3月第二个星期日的凌晨2点到11月第一个星期日的01:59:59)。
  • 您可能希望先检查第三个解决方案**。
    首个解决方案

一种方法是创建一个表(演示中的DST),其中包含夏令时的开始和结束日期和时间。
然后,如果Start_at在相应年份的两个日期之前,则可以根据-1(真)或-0(假)调整-5小时偏移量。
例如datetime(started_at,'unixepoch','-'||(5 - (started_at BETWEEN dst_start AND dst_end))||' hours')
这是一个演示:-

DROP TABLE IF EXISTS dst;
DROP TABLE IF EXISTS bt;
/* Create and populate DST table  with start of ds and end of ds*/
CREATE TABLE IF NOT EXISTS dst (dst_start INTEGER, dst_end INTEGER);
INSERT INTO dst VALUES 
    (strftime('%s','2022-03-13 02:00:00'),strftime('%s','2022-11-06 01:59:59')),
    (strftime('%s','2023-03-12 02:00:00'),strftime('%s','2023-11-05 01:59:59')),
    (strftime('%s','2024-03-10 02:00:00'),strftime('%s','2024-11-03 01:59:59')),
    (strftime('%s','2025-03-09 02:00:00'),strftime('%s','2025-11-02 01:59:59'))
;
/* Create the biketransactions table and load with some test data */
CREATE TABLE IF NOT EXISTS bt (started_at INTEGER);
with cte(started_at) AS (
    SELECT strftime('%s','now') 
    UNION ALL SELECT strftime('%s',started_at,'unixepoch','10 days') FROM cte LIMIT 100
    )
INSERT INTO bt SELECT * FROM cte;


/* select 3 values based upon the bt started_at 
    1. The actual stored date
    2. Whether (1) or not (0) daylight savings is applicable
    3. the adjusted datetime    
*/
SELECT datetime(started_at,'unixepoch') AS UTC,
    started_at BETWEEN dst_start AND dst_end AS dstflag,
    datetime(started_at,'unixepoch','-'||(5 - (started_at BETWEEN dst_start AND dst_end))||' hours') AS adjusted
    
FROM bt 
/* join gets the applicable row from the dst table i.e. the row for the year */
JOIN dst ON strftime('%Y',started_at,'unixepoch') = strftime('%Y',dst_start,'unixepoch')
;

结果包括(DST DATETIME突出显示):

第二种解决方案

另一种方法是根据夏令时开始于3月的第二个星期日和结束于11月的第一个星期日的规则来确定当前夏令时的开始和结束。然而,如图所示,这仅限于本年度的日期:
这是一个演示:-

DROP TABLE IF EXISTS bt;
/* Create the biketransactions table and load with some test data */
CREATE TABLE IF NOT EXISTS bt (started_at INTEGER);
with cte(started_at) AS (
    SELECT strftime('%s','now') 
    UNION ALL SELECT strftime('%s',started_at,'unixepoch','10 days') FROM cte LIMIT 200
    )
INSERT INTO bt SELECT * FROM cte;

/* 
    as a CTE (Commaon Tabel Expression) to determine the start and end dates and times of daylight savings for the current year
    note that this is restricted to only working for dates in the current year
*/
WITH ds AS (SELECT 
    strftime('%Y','now') AS year, /* YEAR */
    strftime('%s',strftime('%Y','now')||'-03-01 02:00:00','+'||(14 - strftime('%w',strftime('%Y','now')||'-03-01'))||' days') AS dst_start,
    strftime('%s',strftime('%Y','now')||'-11-01 01:59:59','+'||(7 - strftime('%w',strftime('%Y','now')||'-11-01'))||' days') AS dst_end
    )
SELECT 
    datetime(started_at,'unixepoch'), 
    started_at BETWEEN (SELECT dst_start FROM ds) AND (SELECT dst_end FROM ds),
    datetime(started_at,'unixepoch','-'||(5-(started_at BETWEEN (SELECT dst_start FROM ds) AND (SELECT dst_end FROM ds)))||' hours') AS adjusted 
    FROM bt 
;

结果是:

  • 可以看出,当夏令时适用于本年度以外的任何其他年份时,上述情况将不会有正确的时间。
    第三(也许是最好的解决方案)

第一个解决方案需要维护一个额外的表格,第二个解决方案不能很好地满足除本年度以外的其他日期。第三种选择进一步使用开始日期的年份作为调整的基础。因此,这可能是更可取的解决方案:

SELECT 
    datetime(started_at,'unixepoch') AS unadjusted,
    CASE 
        WHEN
            started_at 
            BETWEEN 
                strftime(
                    '%s',strftime('%Y',started_at,'unixepoch') /* Year according to the started_at date */
                        ||'-03-01 02:00:00','+' /* 1st March */
                        ||(14 - strftime('%w',strftime('%Y',started_at,'unixepoch')||'-03-01'))||' days' /* 2nd Sunday */
                        )
            AND
                strftime(
                    '%s',strftime('%Y',started_at,'unixepoch') /* Year according to the started_at date */
                        ||'-11-01 01:59:59','+' /* 1st Nov */
                        ||(7 - strftime('%w',strftime('%Y',started_at,'unixepoch')||'-11-01'))||' days' /* 1st Sunday */
                        )
        THEN /* i.e. if started_at is between daylight savings date thus 1 */ datetime(started_at,'unixepoch','-4 hours')
        ELSE /* if not between the daylight savings dates thus 0*/ datetime(started_at,'unixepoch','-5 hours')
    END as adjusted
FROM bt;

这使用相同的方法将数据加载到biketransaction(简写为bt)表中,结果是:

其后:

  • 仅限于实际和调整后的日期和时间。
  • 夏令时内的日期在突出显示的区块中

相关问题