如何在C++中迭代日期范围

3

我有一个包含csv文件的文件夹。每个csv文件都以日期命名(例如 01JAN2013.csv、02JAN2013.csv)。我需要按照它们的日期顺序依次读取这些文件(开始日期和结束日期已知)。

因此,我正在尝试循环遍历日期,从开始日期到结束日期,以生成文件名。

目前我的做法是:

vector<string> dd{"01", "02", "03", "04", "05", "06", "07", "08", "09","10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20","21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"};
vector<string> mmm{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG","SEP", "OCT", "NOV", "DEC"};
vector<string> yyyy{"2011","2012","2013","2014"};

string filePath=DataFolderPath;

for(int i=0;i<yyyy.size();i++)
{
    for(int j=0;j<mmm.size();j++)
    {
        for(int k=0;k<dd.size();k++)
        {
            filePath.append(dd[k]);
            filePath.append(mmm[j]);
            filePath.append(yyyy[i]);
            filePath.append(".csv");
        }
    }
}

虽然有些丑陋,但它能够完成工作。

在C++中是否有更简单的方法来循环遍历日期呢?类似于:

for ( currentDate = starDate; currentDate < endDate; currentDate++) {

//Do stuff

 }

更新:

最终我采用以下方法,结合了下面两个答案:

typdef struct tm Time;
Time startDate=makeDate(3,1,2011);
Time endDate=makeDate(24,1,2014);
time_t end=mktime(&endDate);

for(Time date=startDate;end>=mktime(&date);++date.tm_mday)
{
    char buffer[16];
    strftime(buffer, sizeof(buffer), "%d%b%Y.csv", &date);

    std::string filename(buffer);
            //To convert month to CAPS
    std::transform(filename.begin()+2, filename.begin()+5,filename.begin()+2, ::toupper);


    std::cout << filename << "\n";
}

我还使用了一个基于paddy答案的makeDate辅助函数,它返回一个struct tm而不是time_t

Time makeDate( int day, int month, int year )
{
Time ttm = {0};
ttm.tm_mday= day;
ttm.tm_mon= month-1;
ttm.tm_year= year-1900;
return ttm;
}

看起来找到已经存在的文件,然后按日期排序会更容易,不是吗? - Jerry Coffin
@JerryCoffin 每个文件只包含给定日期的数据。它并不是在那个日期创建/修改的。因此,按日期对文件进行排序将没有帮助。 - Shayan RC
不,我的意思是按名称中的日期排序。 - Jerry Coffin
@JerryCoffin 这听起来很简单,但我不知道如何做到这一点... - Shayan RC
你能把日期格式改成ddmmyyyy吗?例如31012014,而不是31Jan2014或者31_01_2014,或者其他变体。因为我预计按月份排序在当前格式下会变成按字母顺序排序。 - chrisb2244
@chrisb2244 我无法控制文件名的模式。 - Shayan RC
3个回答

7

实际上并不一定更容易......但你可以使用<ctime>中的时间函数。类似于这样:

string MakeFileName( time_t t )
{
    static const char* months[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
                                    "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
    struct tm *ptm = localtime(&t);

    char buffer[20];
    snprintf( buffer, 20, "%02d%s%04d.CSV",
              ptm->tm_day, months[ptm->tm_month], ptm->tm_year+1900 );

    return string(buffer);
}

现在只需要在time_t值中正确设置开始和结束日期(记住,它以秒为单位)。您可以只使用00:00:00来表示时间。

// Note that day and month are 1-based.
time_t GetDate( int day, int month, int year )
{
    struct tm ttm = {0};
    ttm.tm_day = day;
    ttm.tm_month = month-1;
    ttm.tm_year = year-1900;
    return mktime(&ttm);
}

通过该助手,您可以轻松设置起始日期和结束日期:
time_t start = GetDate(1, 1, 2011);
time_t end = GetDate(28, 10, 2013);
for( time_t t = start; t <= end; t += 86400 )
{
    string filename = MakeFileName(t);

    // TODO...
}

2
如果您决定生成名称,我认为@paddy的想法很正确,但实现可能需要稍作改进。特别是,mktime不仅可以进行转换,还可以修复其输入。因此,如果您给它一个30 Feb的输入,它知道那是3月1日或2日,并会相应地更改它。同样,它也知道32 December 2011实际上是1 Jan 2012,并(再次)适当调整输入。
猜测一下,您也可能不想生成未来的日期,只是让它生成到年底的日期,因为您不想每个日期都修改它。我猜只生成到运行日期就足够了。
有了这些想法,我会像这样编写代码:
#include <time.h>
#include <iostream>

int main() {
    struct tm date;

    date.tm_mon = 1;
    date.tm_mday = 1;
    date.tm_year = 2011 - 1900;

    time_t end = time(NULL);

    for (; mktime(&date) < end; ++date.tm_mday) {
        char buffer[16];

        strftime(buffer, sizeof(buffer), "%d%b%Y", &date);
        std::cout << buffer << "\t";
    }
}

目前,这只是打印出字符串,但使用正确生成的字符串作为文件名通常会非常简单。

如果您想按照我在评论中建议的方式获取所有文件名,并按日期对它们进行排序,则可以将每个文件名转换为time_t,然后对time_t进行排序。一个相当简单的方法是使用std::get_time操作符:

time_t cvt(std::string const &filename) { 
    std::istringstream in(filename);
    struct tm date;

    in >> std::get_time(&date, "%d%b%Y");
    return mktime(date);
}

使用这种方法,你可以(其中一种可能)将time_t和文件名放入一个std::map中,然后只需从头到尾遍历该映射,并按顺序处理每个文件。

std::map<time_t, std::string> files;
std::string file_name;

while (get_file_name(&file_name))
    files[cvt(file_name)] = file_name;

for (auto const &f : files)
    process(f.second);

为什么缓冲区的大小是16?当只有9个字符时? - Shayan RC
@ShayanRC:我经常将大多数尺寸舍入为2的幂。 - Jerry Coffin

2
#include <boost/date_time.hpp>

using namespace boost::gregorian;

long dateDifference( string start_date, string end_date ) 
{
    date _start_date(from_simple_string(start_date));
    date _end_date(from_simple_string(end_date));

    for(day_iterator iter = _start_date; iter!=_end_date; ++iter)
    {
       // Do what you want
    }
}

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