readdate 并不是标准C库(ANSI C/C99/C11等)的一部分,它是一个特定于某些Unix系统(特别是早期的System V Unix,如Solaris)的函数。

尽管如此,了解 readdate 仍然很有价值,因为它是一个设计巧妙且高效的日期解析函数,下面我将从以下几个方面为你详细解释:
- 函数原型和作用
- 工作原理和返回值
- 使用示例
- 跨平台问题与现代替代方案
函数原型和作用
readdate 函数的作用是将一个字符串形式的日期和时间解析成一个 time_t 类型的值(即自1970年1月1日00:00:00 UTC以来的秒数,也就是Unix时间戳)。
它的原型通常在 <unistd.h> 或 <time.h> 头文件中定义:
#include <unistd.h> // 或 <time.h> time_t readdate(const char *string);
参数:

string: 一个指向以null结尾的字符串的指针,该字符串包含了需要解析的日期和时间。
返回值:
- 成功时,返回解析后的
time_t值(Unix时间戳)。 - 失败时,返回
-1。
工作原理和返回值
readdate 最独特的地方在于它的错误处理机制。
它并不是简单地返回 -1 来表示失败,相反,它会将无法解析的部分字符串的长度作为负数返回,这个设计非常巧妙,因为它不仅告诉你“失败了”,还告诉你“失败在哪里”。
返回值详解:

| 返回值情况 | 含义 |
|---|---|
| 一个正数 | 解析成功,返回的值就是对应的 time_t。 |
| 一个负数 | 解析失败,这个负数的绝对值表示输入字符串中第一个无法被识别的字符的位置(从0开始计数)。 |
支持的格式:
readdate 支持非常灵活的日期和时间格式,它会尝试匹配以下几种常见格式:
MM/DD/YY或MM/DD/YYYY(美国格式)DD/MM/YY或DD/MM/YYYY(欧洲格式)YYMMDD- 可以包含时间部分,如
HH:MM或HH:MM:SS - 可以包含时区信息,如
GMT,PST,EST等。
"12/25/23 14:30 GMT" 会被正确解析。
使用示例
下面是一个简单的C程序,演示了 readdate 的使用,包括成功和失败的情况。
#include <stdio.h>
#include <unistd.h> // 包含 readdate 的头文件
#include <time.h>
void print_time_t(time_t t) {
if (t == (time_t)-1) {
printf("解析失败,\n");
} else {
// 将 time_t 转换为可读的字符串
char *time_str = ctime(&t);
printf("解析成功,Unix时间戳: %ld\n", (long)t);
printf("对应的时间: %s", time_str);
}
printf("----------------------------------------\n");
}
int main() {
// 示例1: 成功解析
printf("尝试解析: \"12/25/2025 15:30 GMT\"\n");
time_t t1 = readdate("12/25/2025 15:30 GMT");
print_time_t(t1);
// 示例2: 成功解析 (欧洲格式)
printf("尝试解析: \"25/12/23 14:30\"\n");
time_t t2 = readdate("25/12/23 14:30");
print_time_t(t2);
// 示例3: 失败解析,并检查错误位置
// 字符串 "10/32/2025" 是无效的(月份10没有32号)
// readdate 会停在 '3' 的位置,索引是 5
printf("尝试解析: \"10/32/2025\"\n");
time_t t3 = readdate("10/32/2025");
if (t3 == (time_t)-1) {
printf("解析失败,错误位置: %d (第 %d 个字符)\n", (int)t3, (int)(-t3));
}
print_time_t(t3); // 这个调用会显示“解析失败”
// 示例4: 失败解析,格式完全错误
// readdate 会停在 'H' 的位置,索引是 0
printf("尝试解析: \"Hello World\"\n");
time_t t4 = readdate("Hello World");
if (t4 == (time_t)-1) {
printf("解析失败,错误位置: %d (第 %d 个字符)\n", (int)t4, (int)(-t4));
}
print_time_t(t4); // 这个调用会显示“解析失败”
return 0;
}
编译和运行(在支持readdate的系统上,如Solaris):
gcc -o readdate_example readdate_example.c ./readdate_example
预期输出:
尝试解析: "12/25/2025 15:30 GMT"
解析成功,Unix时间戳: 1703506200
对应的时间: Mon Dec 25 15:30:00 2025
----------------------------------------
尝试解析: "25/12/23 14:30"
解析成功,Unix时间戳: 1703575800
对应的时间: Tue Dec 26 02:30:00 2025
----------------------------------------
尝试解析: "10/32/2025"
解析失败,错误位置: 5 (第 5 个字符)
解析失败。
----------------------------------------
尝试解析: "Hello World"
解析失败,错误位置: 0 (第 0 个字符)
解析失败。
----------------------------------------
跨平台问题与现代替代方案
由于 readdate 不是标准C函数,你的代码在Linux (glibc)、macOS (libsystem) 或Windows上很可能无法编译通过,在现代跨平台开发中,我们应该避免使用它。
现代替代方案
有几种标准且跨平台的方法来解析日期字符串:
使用 strptime (POSIX标准)
strptime (String Parse Time) 是POSIX标准的一部分,在Linux、macOS以及许多其他Unix-like系统上都可用,它比 readdate 更灵活,因为它允许你指定确切的格式字符串。
原型:
#include <time.h> char *strptime(const char *buf, const char *format, struct tm *tm);
工作方式:
buf: 要解析的字符串。format: 描述buf格式的字符串("%m/%d/%Y %H:%M")。tm: 一个指向struct tm结构体的指针,用于存储解析后的结果。- 返回值:如果成功,返回指向输入字符串中最后一个被解析字符的下一个位置的指针;如果失败,返回
NULL。
示例:
#include <stdio.h>
#include <time.h>
#include <string.h>
int main() {
const char *date_str = "12/25/2025 15:30";
struct tm tm_info = {0};
char *ret;
ret = strptime(date_str, "%m/%d/%Y %H:%M", &tm_info);
if (ret == NULL) {
printf("strptime 解析失败,\n");
} else {
printf("strptime 解析成功,\n");
// tm_info 现在包含了年、月、日、时、分等信息
// 需要用 mktime 将其转换为 time_t
time_t t = mktime(&tm_info);
printf("Unix时间戳: %ld\n", (long)t);
}
return 0;
}
使用第三方库
如果你的项目需要处理非常复杂或多种多样的日期格式,使用专门的日期时间库是最佳选择,这些库功能强大、容错性好。
- date.h: 一个仅头文件的现代C++日期库,也提供了C接口,非常推荐。
- libical: 用于处理iCalendar (RFC 5545) 格式的库,功能强大。
- Boost.DateTime: C++ Boost库的一部分,功能全面。
手动解析
对于格式非常简单且固定的字符串,可以自己用 sscanf 或字符串操作函数(如 strtok)手动解析。
示例 (使用 sscanf):
#include <stdio.h>
#include <time.h>
int main() {
int year, month, day, hour, minute;
const char *date_str = "2025-12-25 15:30";
// 注意:sscanf 不会进行严格的日期有效性检查(如2月30日)
if (sscanf(date_str, "%d-%d-%d %d:%d", &year, &month, &day, &hour, &minute) == 5) {
struct tm tm_info = {0};
tm_info.tm_year = year - 1900;
tm_info.tm_mon = month - 1;
tm_info.tm_mday = day;
tm_info.tm_hour = hour;
tm_info.tm_min = minute;
tm_info.tm_sec = 0;
tm_info.tm_isdst = -1; // 让 mktime 自动判断夏令时
time_t t = mktime(&tm_info);
if (t == (time_t)-1) {
printf("mkktim失败,可能是无效日期,\n");
} else {
printf("手动解析成功,Unix时间戳: %ld\n", (long)t);
}
} else {
printf("手动解析失败,格式不匹配,\n");
}
return 0;
}
| 特性 | readdate |
strptime |
手动解析/第三方库 |
|---|---|---|---|
| 标准性 | 非标准 (特定于某些Unix) | POSIX标准 (跨平台Unix-like) | 不依赖标准库 |
| 灵活性 | 自动识别多种格式 | 高 (需指定格式字符串) | 取决于实现 |
| 错误处理 | 独特 (返回错误位置) | 返回 NULL |
需自行实现 |
| 推荐度 | 不推荐 (用于旧代码维护) | 推荐 (标准、灵活) | 根据需求选择 |
除非你正在维护一个古老的、运行在特定Unix系统上的代码库,否则应该避免使用 readdate,对于新的项目,strptime 是最标准、最推荐的跨平台替代方案,如果需要更高级的功能,可以考虑使用成熟的第三方库。
