从现在到过去最简单的方法是读取文件中的日期并将其解析为月份、日期和年份(
m,d,y
)。如果您不打算对每个转换进行完整的验证,则可以使用
fscanf
和适当的
字段宽度修饰符来分离4位数的年份和2位数的月份和日期,例如:
while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
然后,在循环内部所需的一切就是用年、月和日值填充struct tm
(记得从年份中减去1900
,并将每个小时、分钟和秒成员都设置为零,将夏令时成员设置为-1
)。一个简单的函数可以做到这一点,并在调用mktime
之后返回time_t
,例如:
time_t fill_broken_down_time (int y, int m, int d)
{ /* initialize struct members */
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt); /* return mktime conversion to time_t */
}
为了完成操作,你只需要获取
time_t
值并调用
difftime
函数,以获取当前时间和从文件中读取的时间之间的差值(以
double
值表示)。在
main()
函数中继续循环。
while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
printf ("date[%d] %d/%d/%d is %.2f seconds from now.\n",
n++, m, d, y, difftime (now, then));
}
把所有东西都放在一起,你可以这样做:
#include <stdio.h>
#include <time.h>
time_t fill_broken_down_time (int y, int m, int d)
{
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt);
}
int main (int argc, char **argv) {
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
int y, m, d, n = 1;
if (!fp) {
perror ("file open failed");
return 1;
}
while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
printf ("date[%d] %d/%d/%d is %.2f seconds from now.\n",
n++, m, d, y, difftime (now, then));
}
if (fp != stdin) fclose (fp);
return 0;
}
注意:程序期望从第一个参数中读取要读取日期的文件名(如果没有给出参数,则程序将默认从stdin中读取)
示例输入文件
$ cat dat/3dates.txt
20190101
20190304
20180922
示例用法/输出
$ ./bin/time_from_now dat/3dates.txt
date[1] 1/1/2019 is 6300212.00 seconds from now.
date[2] 3/4/2019 is 943412.00 seconds from now.
date[3] 9/22/2018 is 15030212.00 seconds from now.
编辑每个评论修改输入文件格式
如果您的数据文件实际上与您最初发布问题时所发布的三行日期不同,并且其中包含日期信息之前的标题行,则需要在处理日期行之前读取、识别和处理这些行。由于您希望以天为单位而不是秒为单位输出,因此只需将秒数除以86400
即可得到时间差。
要读取和处理标题行,只需调整您的读取操作,将整个行一次性读入足够大的缓冲区中。声明一个足够大的常量来确保您的缓冲区足够大,例如:
#define MAXC 1024u
...
int main (int argc, char **argv) {
...
char buf[MAXC];
然后,您只需使用
sscanf
而不是
fscanf
来执行每行中信息的精确解析。如果行格式不满足
yyyymmdd
格式,则知道它不是日期行 - 以任何方式处理这些行(在下面的示例中仅输出前缀为
"non-date line: "
)。
结合将自文件时间以来的秒数除以每天的
86400
秒,您的新读取循环将类似于:
while (fgets (buf, MAXC, fp)) {
if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
printf ("non-date line: %s", buf);
continue;
}
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
double secs = difftime (now, then);
printf ("date[%d] %02d/%02d/%04d is %11.2f sec (%g days) from now.\n",
n++, m, d, y, secs, secs / 86400.0);
}
你说:
"I am not able to open the file"
该程序期望您提供要作为程序第一个参数读取的文件名,否则程序将默认从标准输入(
stdin
)读取。这意味着您必须向程序提供文件名,例如:
./yourprogram your_date_file
或者你需要通过将该信息从其他程序的输出中传输到程序中,或者简单地将文件重定向为stdin
的输入来提供该数据,例如:
some_utility_making_dates | ./yourprogram
或者
./yourprogram < your_date_file
将所有更改合并后,您的程序将如下所示:
#include <stdio.h>
#include <time.h>
#define MAXC 1024u
time_t fill_broken_down_time (int y, int m, int d)
{
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt);
}
int main (int argc, char **argv) {
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
int y, m, d, n = 1;
char buf[MAXC];
if (!fp) {
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) {
if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
printf ("non-date line: %s", buf);
continue;
}
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
double secs = difftime (now, then);
printf ("date[%d] %02d/%02d/%04d is %11.2f sec (%g days) from now.\n",
n++, m, d, y, secs, secs / 86400.0);
}
if (fp != stdin) fclose (fp);
return 0;
}
带标题的示例输入文件
$ cat dat/3dates-w-headers.txt
This file contains dates to read and convert to days.
The file also contains this description and dates in the format:
yyyymmdd
20190101
20190304
20180922
示例用法/输出
$ ./bin/time_from_now2 dat/3dates-w-headers.txt
non-date line: This file contains dates to read and convert to days.
non-date line: The file also contains this description and dates in the format:
non-date line:
non-date line: yyyymmdd
date[1] 01/01/2019 is 6348645.00 sec (73.4797 days) from now.
date[2] 03/04/2019 is 991845.00 sec (11.4797 days) from now.
date[3] 09/22/2018 is 15078645.00 sec (174.521 days) from now.
请查看并告诉我是否有进一步的问题。
struct tm
结构体(表示拆分的时间),然后将其传递给mktime
函数转换为time_t
类型。你可以用这个time_t
类型的数据与当前时间相减得到秒数差(通常使用localtime()
函数获取当前时间)。 - David C. Rankin