如何有效地找到时间戳数组中每个 day/week/month 的第一个时间戳?

How to efficiently find the first timestamp of each day/week/month in an array of timestamps?

我有一长串排序的时间戳(股票报价)。时间戳具有最小分辨率(例如,每个 TS 至少比前一个大 1 分钟),但白天可能存在间隙(尤其是在 pre/after 小时的会话中)并且没有周末和假期。 我想(有效地)找到开始新间隔(例如天、周或月 - 取决于时间范围)的时间戳以用于图表目的。

我当前的解决方案是遍历数组并根据每个时间戳创建日期,然后比较前一个和当前数组元素的 .getDay()。

let rangeSecs = data[data.length-1]- data[0];
let rangeDays = rangeSecs / 86400;  //data has timestamps in seconds, not ms.
let spt=[]; //array of indexes of the "timestamps of interest"
const minInterval=data.slice(0,9).reduce((ac,x,i,ar)=>Math.min(ac,x-ar[i-1]))

let prevdate=new Date(data[0]*1000)

for(let i=1;i<data.length;i++){
    const curDate=new Date(data[i]*1000)
    if(rangeDays>70 && prevdate.getMonth()!=curDate.getMonth() ){  //monthly
        spt.push(i)
    }else if(rangeDays<=70 && rangeDays>14 && prevdate.getDay()>curDate.getDay() ){ //weekly
        spt.push(i)
                                
    }else if(rangeDays<=14 && prevdate.getDay()!=curDate.getDay() ){  //daily
        spt.push(i)
    }
    prevdate=curDate;

}

可以用,但是速度很慢。 有人知道如何加快速度吗?

我尝试跳过一些“安全”数量的时间戳,但因为我无法确定每天会有多少数据点(正常交易时间除外),优化不是 optimal/generic.

编辑:根据要求提供样本数据。它涵盖了约。一个月,最小时间戳间隔为 15 分钟。

[1625572800,1625573700,1625574600,1625576400,1625578200,1625579100,1625580000,1625580900,1625581800,1625582700,1625583600,1625584500,1625585400,1625586300,1625587200,1625588100,1625589000,1625589900,1625590800,1625591700,1625592600,1625593500,1625594400,1625595300,1625596200,1625597100,1625598000,1625598900,1625599800,1625600700,1625601600,1625602500,1625603400,1625604300,1625605200,1625606100,1625607000,1625607900,1625609700,1625613300,1625615100,1625656500,1625659200,1625660100,1625664600,1625665500,1625666400,1625667300,1625668200,1625669100,1625670000,1625670900,1625671800,1625672700,1625673600,1625674500,1625675400,1625676300,1625677200,1625678100,1625679000,1625679900,1625680800,1625681700,1625682600,1625683500,1625684400,1625685300,1625686200,1625687100,1625688000,1625690700,1625691600,1625693400,1625694300,1625695200,1625697900,1625700600,1625743800,1625745600,1625746500,1625747400,1625748300,1625749200,1625751000,1625751900,1625752800,1625753700,1625754600,1625755500,1625756400,1625757300,1625758200,1625759100,1625760000,1625760900,1625761800,1625762700,1625763600,1625764500,1625765400,1625766300,1625767200,1625768100,1625769000,1625769900,1625770800,1625771700,1625772600,1625773500,1625774400,1625784300,1625786100,1625787000,1625830200,1625832000,1625832900,1625834700,1625837400,1625838300,1625839200,1625840100,1625841000,1625841900,1625842800,1625843700,1625844600,1625845500,1625846400,1625847300,1625848200,1625849100,1625850000,1625850900,1625851800,1625852700,1625853600,1625854500,1625855400,1625856300,1625857200,1625858100,1625859000,1625859900,1625860800,1625862600,1625867100,1625871600,1626087600,1626091200,1626096600,1626097500,1626098400,1626099300,1626100200,1626101100,1626102000,1626102900,1626103800,1626104700,1626105600,1626106500,1626107400,1626108300,1626109200,1626110100,1626111000,1626111900,1626112800,1626113700,1626114600,1626115500,1626116400,1626117300,1626118200,1626119100,1626120000,1626123600,1626129900,1626178500,1626183000,1626183900,1626184800,1626185700,1626186600,1626187500,1626188400,1626189300,1626190200,1626191100,1626192000,1626192900,1626193800,1626194700,1626195600,1626196500,1626197400,1626198300,1626199200,1626200100,1626201000,1626201900,1626202800,1626203700,1626204600,1626205500,1626206400,1626213600,1626215400,1626262200,1626264000,1626267600,1626269400,1626270300,1626271200,1626272100,1626273000,1626273900,1626274800,1626275700,1626276600,1626277500,1626278400,1626279300,1626280200,1626281100,1626282000,1626282900,1626283800,1626284700,1626285600,1626286500,1626287400,1626288300,1626289200,1626290100,1626291000,1626291900,1626292800,1626293700,1626295500,1626300000,1626302700,1626306300,1626342300,1626346800,1626349500,1626350400,1626351300,1626352200,1626353100,1626355800,1626356700,1626357600,1626358500,1626359400,1626360300,1626361200,1626362100,1626363000,1626363900,1626364800,1626365700,1626366600,1626367500,1626368400,1626369300,1626370200,1626371100,1626372000,1626372900,1626373800,1626374700,1626375600,1626376500,1626377400,1626378300,1626379200,1626381000,1626381900,1626386400,1626390900,1626433200,1626438600,1626442200,1626443100,1626444000,1626444900,1626445800,1626446700,1626447600,1626448500,1626449400,1626450300,1626451200,1626452100,1626453000,1626453900,1626454800,1626455700,1626456600,1626457500,1626458400,1626459300,1626460200,1626461100,1626462000,1626462900,1626463800,1626464700,1626465600,1626466500,1626477300,1626692400,1626696000,1626696900,1626697800,1626698700,1626699600,1626701400,1626702300,1626703200,1626704100,1626705000,1626705900,1626706800,1626707700,1626708600,1626709500,1626710400,1626711300,1626712200,1626713100,1626714000,1626714900,1626715800,1626716700,1626717600,1626718500,1626719400,1626720300,1626721200,1626722100,1626723000,1626723900,1626724800,1626725700,1626727500,1626731100,1626735600,1626737400,1626778800,1626780600,1626783300,1626784200,1626785100,1626787800,1626788700,1626789600,1626790500,1626791400,1626792300,1626793200,1626794100,1626795000,1626795900,1626796800,1626797700,1626798600,1626799500,1626800400,1626801300,1626802200,1626803100,1626804000,1626804900,1626805800,1626806700,1626807600,1626808500,1626809400,1626810300,1626811200,1626869700,1626872400,1626874200,1626875100,1626876000,1626876900,1626877800,1626878700,1626879600,1626880500,1626881400,1626882300,1626883200,1626884100,1626885000,1626885900,1626886800,1626887700,1626888600,1626889500,1626890400,1626891300,1626892200,1626893100,1626894000,1626894900,1626895800,1626896700,1626897600,1626955200,1626957000,1626960600,1626961500,1626962400,1626963300,1626964200,1626965100,1626966000,1626966900,1626967800,1626968700,1626969600,1626970500,1626971400,1626972300,1626973200,1626974100,1626975000,1626975900,1626976800,1626977700,1626978600,1626979500,1626980400,1626981300,1626982200,1626983100,1626984000,1626992100,1627047000,1627047900,1627048800,1627049700,1627050600,1627051500,1627052400,1627053300,1627054200,1627055100,1627056000,1627056900,1627057800,1627058700,1627059600,1627060500,1627061400,1627062300,1627063200,1627064100,1627065000,1627065900,1627066800,1627067700,1627068600,1627069500,1627070400,1627074900,1627300800,1627306200,1627307100,1627308000,1627308900,1627309800,1627310700,1627311600,1627312500,1627313400,1627314300,1627315200,1627316100,1627317000,1627317900,1627318800,1627319700,1627320600,1627321500,1627322400,1627323300,1627324200,1627325100,1627326000,1627326900,1627327800,1627328700,1627329600,1627387200,1627388100,1627392600,1627393500,1627394400,1627395300,1627396200,1627397100,1627398000,1627398900,1627399800,1627400700,1627401600,1627402500,1627403400,1627404300,1627405200,1627406100,1627407000,1627407900,1627408800,1627409700,1627410600,1627411500,1627412400,1627413300,1627414200,1627415100,1627416000,1627479000,1627479900,1627480800,1627481700,1627482600,1627483500,1627484400,1627485300,1627486200,1627487100,1627488000,1627488900,1627489800,1627490700,1627491600,1627492500,1627493400,1627494300,1627495200,1627496100,1627497000,1627497900,1627498800,1627499700,1627500600,1627501500,1627502400,1627565400,1627566300,1627567200,1627568100,1627569000,1627569900,1627570800,1627571700,1627572600,1627573500,1627574400,1627575300,1627576200,1627577100,1627578000,1627578900,1627579800,1627580700,1627581600,1627582500,1627583400,1627584300,1627585200,1627586100,1627587000,1627587900,1627588800,1627599600,1627600500,1627646400,1627651800,1627652700,1627653600,1627654500,1627655400,1627656300,1627657200,1627658100,1627659000,1627659900,1627660800,1627661700,1627662600,1627663500,1627664400,1627665300,1627666200,1627667100,1627668000,1627668900,1627669800,1627670700,1627671600,1627672500,1627673400,1627674300,1627675200,1627676100]

无需将每个时间戳都转换为 Date 对象。相反,当您有一个有趣的日期时,计算出下一个有趣日期(当前日期加上一个月、一周或一天)的时间戳值,并跳过小于该值的后续时间戳。 (另请参阅对该问题的各种评论。)

我在下面的代码中做了一些假设,您可以根据需要进行调整——例如,如果间隔是一周,那么下一个最小时间戳应该总是从星期一开始,并且它应该总是从午夜开始(不分天、周或月)。

查看评论:

// Function to find the indexes of the interesting dates in the given data
function findIndexes(data) {
    let rangeSecs = data[data.length-1] - data[0];
    let rangeDays = rangeSecs / 86400;  // data has timestamps in seconds, not ms.
    let spt = []; // array of indexes of the "timestamps of interest"

    // You didn't say what these were, so I'm just using the whole array
    const scaleMin = 0;
    const scaleMax = data.length - 1;

    // Figure out what kind of interval we want: month, week, or day
    const interval = rangeDays > 70 ? "month" : rangeDays <= 14 ? "day" : "week";
    /*
    console.log(`${displayString(data[0])} to ${displayString(data[data.length-1])}, rangeDays = ${rangeDays}, interval = ${interval}`);
    */
    // First date is presumably always of interest (but you could remove this if not)
    spt.push(scaleMin);
    // Remember the last "interesting" date
    let prevdate = new Date(data[0] * 1000);
    // Find the timestamp value for the next interesting date
    let minNext = getMinNext(interval, prevdate);
    // Loop through the data
    for (let i = scaleMin + 1; i <= scaleMax; ++i) {
        const value = data[i];
        // Is this value at or above the next minimum value?
        if (value >= minNext) {
            // Yes, it's "interesting" -- save it and get the next one
            spt.push(i);
            prevdate = new Date(value * 1000);
            minNext = getMinNext(interval, prevdate);
        }
    }

    return spt;
}

// Subroutine of `findIndexes`
function getMinNext(interval, prevdate) {
    let nextTime;
    if (interval === "month") {
        // Find out when the next month starts by adding a month and clearing the hours,
        // minutes, seconds, and milliseconds
        nextTime = new Date(+prevdate);
        nextTime.setMonth(nextTime.getMonth() + 1);
        nextTime.setHours(0, 0, 0, 0);
    } else {
        // Find out when the next week or day starts
        const intervalLength = interval === "day" ? 86400 : 7 * 86400;
        nextTime = new Date(+prevdate + (intervalLength * 1000));
        nextTime.setHours(0, 0, 0, 0);
        if (interval === "week") {
            // Going by weeks, scan backward to Monday (this only scans the first time,
            // after that we add seven days so it's always Monday)
            while (nextTime.getDay() !== 1) {
                nextTime.setDate(nextTime.getDate() - 1);
            }
        }
    }
    const minNext = Math.floor(nextTime / 1000);
    /*
    console.log(`minNext for ${displayString(prevdate)} is ${displayString(minNext)}`);
    */
    return minNext;
}

具有随机数据的实时示例:

// Function to find the indexes of the interesting dates in the given data
function findIndexes(data) {
    let rangeSecs = data[data.length-1] - data[0];
    let rangeDays = rangeSecs / 86400;  // data has timestamps in seconds, not ms.
    let spt = []; // array of indexes of the "timestamps of interest"

    // You didn't say what these were, so I'm just using the whole array
    const scaleMin = 0;
    const scaleMax = data.length - 1;

    // Figure out what kind of interval we want: month, week, or day
    const interval = rangeDays > 70 ? "month" : rangeDays <= 14 ? "day" : "week";
    /*
    console.log(`${displayString(data[0])} to ${displayString(data[data.length-1])}, rangeDays = ${rangeDays}, interval = ${interval}`);
    */
    // First date is presumably always of interest (but you could remove this if not)
    spt.push(scaleMin);
    // Remember the last "interesting" date
    let prevdate = new Date(data[0] * 1000);
    // Find the timestamp value for the next interesting date
    let minNext = getMinNext(interval, prevdate);
    // Loop through the data
    for (let i = scaleMin + 1; i <= scaleMax; ++i) {
        const value = data[i];
        // Is this value at or above the next minimum value?
        if (value >= minNext) {
            // Yes, it's "interesting" -- save it and get the next one
            spt.push(i);
            prevdate = new Date(value * 1000);
            minNext = getMinNext(interval, prevdate);
        }
    }

    return spt;
}

// Subroutine of `findIndexes`
function getMinNext(interval, prevdate) {
    let nextTime;
    if (interval === "month") {
        // Find out when the next month starts by going back to the 1st, adding a month and clearing the hours,
        // minutes, seconds, and milliseconds
        nextTime = new Date(+prevdate);
        nextTime.setDate(1);
        nextTime.setMonth(nextTime.getMonth() + 1);
        nextTime.setHours(0, 0, 0, 0);
    } else {
        // Find out when the next week or day starts
        const intervalLength = interval === "day" ? 86400 : 7 * 86400;
        nextTime = new Date(+prevdate + (intervalLength * 1000));
        nextTime.setHours(0, 0, 0, 0);
        if (interval === "week") {
            // Going by weeks, scan backward to Monday (this only scans the first time,
            // after that we add seven days so it's always Monday)
            while (nextTime.getDay() !== 1) {
                nextTime.setDate(nextTime.getDate() - 1);
            }
        }
    }
    const minNext = Math.floor(nextTime / 1000);
    /*
    console.log(`minNext for ${displayString(prevdate)} is ${displayString(minNext)}`);
    */
    return minNext;
}

// Examples:
let indexes;
console.log("Days:");
const daysData = getRandomData(3 * 60 * 60); // Up to 4 hour intervals
indexes = findIndexes(daysData);
console.log(indexes.map(index => new Date(daysData[index] * 1000).toLocaleString()));
console.log("Weeks:");
const weeksData = getRandomData(3 * 24 * 60 * 60); // Up to 3 day intervals
indexes = findIndexes(weeksData);
console.log(indexes.map(index => new Date(weeksData[index] * 1000).toLocaleString()));
console.log("Months:");
const monthsData = getRandomData(21 * 24 * 60 * 60); // Up to 3 week intervals
indexes = findIndexes(monthsData);
console.log(indexes.map(index => new Date(monthsData[index] * 1000).toLocaleString()));
console.log("Your sample data:");
const sampleData = getSampleData();
indexes = findIndexes(sampleData);
console.log(indexes.map(index => new Date(sampleData[index] * 1000).toLocaleString()));

// Make random data for us
function getRandomData(interval) {
    let value = Math.floor(Date.now() / 1000);
    const data = Array.from({length: 20}, () => {
        const current = value;
        value += Math.floor(Math.random() * interval);
        return current;
    });
    /*
    for (const value of data) {
        console.log(new Date(value * 1000).toLocaleString());
    }
    */
    return data;
}

function displayString(value) {
    if (typeof value === "number") {
        value = new Date(value * 1000);
    }
    return value.toLocaleString();
}

function getSampleData() {
    return [
        1625572800,1625573700,1625574600,1625576400,1625578200,1625579100,1625580000,1625580900,
        1625581800,1625582700,1625583600,1625584500,1625585400,1625586300,
        1625587200,1625588100,1625589000,1625589900,1625590800,1625591700,
        1625592600,1625593500,1625594400,1625595300,1625596200,1625597100,
        1625598000,1625598900,1625599800,1625600700,1625601600,1625602500,
        1625603400,1625604300,1625605200,1625606100,1625607000,1625607900,
        1625609700,1625613300,1625615100,1625656500,1625659200,1625660100,
        1625664600,1625665500,1625666400,1625667300,1625668200,1625669100,
        1625670000,1625670900,1625671800,1625672700,1625673600,1625674500,
        1625675400,1625676300,1625677200,1625678100,1625679000,1625679900,
        1625680800,1625681700,1625682600,1625683500,1625684400,1625685300,
        1625686200,1625687100,1625688000,1625690700,1625691600,1625693400,
        1625694300,1625695200,1625697900,1625700600,1625743800,1625745600,
        1625746500,1625747400,1625748300,1625749200,1625751000,1625751900,
        1625752800,1625753700,1625754600,1625755500,1625756400,1625757300,
        1625758200,1625759100,1625760000,1625760900,1625761800,1625762700,
        1625763600,1625764500,1625765400,1625766300,1625767200,1625768100,
        1625769000,1625769900,1625770800,1625771700,1625772600,1625773500,
        1625774400,1625784300,1625786100,1625787000,1625830200,1625832000,
        1625832900,1625834700,1625837400,1625838300,1625839200,1625840100,
        1625841000,1625841900,1625842800,1625843700,1625844600,1625845500,
        1625846400,1625847300,1625848200,1625849100,1625850000,1625850900,
        1625851800,1625852700,1625853600,1625854500,1625855400,1625856300,
        1625857200,1625858100,1625859000,1625859900,1625860800,1625862600,
        1625867100,1625871600,1626087600,1626091200,1626096600,1626097500,
        1626098400,1626099300,1626100200,1626101100,1626102000,1626102900,
        1626103800,1626104700,1626105600,1626106500,1626107400,1626108300,
        1626109200,1626110100,1626111000,1626111900,1626112800,1626113700,
        1626114600,1626115500,1626116400,1626117300,1626118200,1626119100,
        1626120000,1626123600,1626129900,1626178500,1626183000,1626183900,
        1626184800,1626185700,1626186600,1626187500,1626188400,1626189300,
        1626190200,1626191100,1626192000,1626192900,1626193800,1626194700,
        1626195600,1626196500,1626197400,1626198300,1626199200,1626200100,
        1626201000,1626201900,1626202800,1626203700,1626204600,1626205500,
        1626206400,1626213600,1626215400,1626262200,1626264000,1626267600,
        1626269400,1626270300,1626271200,1626272100,1626273000,1626273900,
        1626274800,1626275700,1626276600,1626277500,1626278400,1626279300,
        1626280200,1626281100,1626282000,1626282900,1626283800,1626284700,
        1626285600,1626286500,1626287400,1626288300,1626289200,1626290100,
        1626291000,1626291900,1626292800,1626293700,1626295500,1626300000,
        1626302700,1626306300,1626342300,1626346800,1626349500,1626350400,
        1626351300,1626352200,1626353100,1626355800,1626356700,1626357600,
        1626358500,1626359400,1626360300,1626361200,1626362100,1626363000,
        1626363900,1626364800,1626365700,1626366600,1626367500,1626368400,
        1626369300,1626370200,1626371100,1626372000,1626372900,1626373800,
        1626374700,1626375600,1626376500,1626377400,1626378300,1626379200,
        1626381000,1626381900,1626386400,1626390900,1626433200,1626438600,
        1626442200,1626443100,1626444000,1626444900,1626445800,1626446700,
        1626447600,1626448500,1626449400,1626450300,1626451200,1626452100,
        1626453000,1626453900,1626454800,1626455700,1626456600,1626457500,
        1626458400,1626459300,1626460200,1626461100,1626462000,1626462900,
        1626463800,1626464700,1626465600,1626466500,1626477300,1626692400,
        1626696000,1626696900,1626697800,1626698700,1626699600,1626701400,
        1626702300,1626703200,1626704100,1626705000,1626705900,1626706800,
        1626707700,1626708600,1626709500,1626710400,1626711300,1626712200,
        1626713100,1626714000,1626714900,1626715800,1626716700,1626717600,
        1626718500,1626719400,1626720300,1626721200,1626722100,1626723000,
        1626723900,1626724800,1626725700,1626727500,1626731100,1626735600,
        1626737400,1626778800,1626780600,1626783300,1626784200,1626785100,
        1626787800,1626788700,1626789600,1626790500,1626791400,1626792300,
        1626793200,1626794100,1626795000,1626795900,1626796800,1626797700,
        1626798600,1626799500,1626800400,1626801300,1626802200,1626803100,
        1626804000,1626804900,1626805800,1626806700,1626807600,1626808500,
        1626809400,1626810300,1626811200,1626869700,1626872400,1626874200,
        1626875100,1626876000,1626876900,1626877800,1626878700,1626879600,
        1626880500,1626881400,1626882300,1626883200,1626884100,1626885000,
        1626885900,1626886800,1626887700,1626888600,1626889500,1626890400,
        1626891300,1626892200,1626893100,1626894000,1626894900,1626895800,
        1626896700,1626897600,1626955200,1626957000,1626960600,1626961500,
        1626962400,1626963300,1626964200,1626965100,1626966000,1626966900,
        1626967800,1626968700,1626969600,1626970500,1626971400,1626972300,
        1626973200,1626974100,1626975000,1626975900,1626976800,1626977700,
        1626978600,1626979500,1626980400,1626981300,1626982200,1626983100,
        1626984000,1626992100,1627047000,1627047900,1627048800,1627049700,
        1627050600,1627051500,1627052400,1627053300,1627054200,1627055100,
        1627056000,1627056900,1627057800,1627058700,1627059600,1627060500,
        1627061400,1627062300,1627063200,1627064100,1627065000,1627065900,
        1627066800,1627067700,1627068600,1627069500,1627070400,1627074900,
        1627300800,1627306200,1627307100,1627308000,1627308900,1627309800,
        1627310700,1627311600,1627312500,1627313400,1627314300,1627315200,
        1627316100,1627317000,1627317900,1627318800,1627319700,1627320600,
        1627321500,1627322400,1627323300,1627324200,1627325100,1627326000,
        1627326900,1627327800,1627328700,1627329600,1627387200,1627388100,
        1627392600,1627393500,1627394400,1627395300,1627396200,1627397100,
        1627398000,1627398900,1627399800,1627400700,1627401600,1627402500,
        1627403400,1627404300,1627405200,1627406100,1627407000,1627407900,
        1627408800,1627409700,1627410600,1627411500,1627412400,1627413300,
        1627414200,1627415100,1627416000,1627479000,1627479900,1627480800,
        1627481700,1627482600,1627483500,1627484400,1627485300,1627486200,
        1627487100,1627488000,1627488900,1627489800,1627490700,1627491600,
        1627492500,1627493400,1627494300,1627495200,1627496100,1627497000,
        1627497900,1627498800,1627499700,1627500600,1627501500,1627502400,
        1627565400,1627566300,1627567200,1627568100,1627569000,1627569900,
        1627570800,1627571700,1627572600,1627573500,1627574400,1627575300,
        1627576200,1627577100,1627578000,1627578900,1627579800,1627580700,
        1627581600,1627582500,1627583400,1627584300,1627585200,1627586100,
        1627587000,1627587900,1627588800,1627599600,1627600500,1627646400,
        1627651800,1627652700,1627653600,1627654500,1627655400,1627656300,
        1627657200,1627658100,1627659000,1627659900,1627660800,1627661700,
        1627662600,1627663500,1627664400,1627665300,1627666200,1627667100,
        1627668000,1627668900,1627669800,1627670700,1627671600,1627672500,
        1627673400,1627674300,1627675200,1627676100
];
}
.as-console-wrapper {
    max-height: 100% !important;
}