⏰介绍

日期和时间工具 - cppreference.com

<time.h> 是在C语言中的日期与时间工具。其中主要有三大模块,常量,类型和函数。

在C++中推荐写成<ctime>,基本都是继承.h中的东西。

函数主要分为两类,时间操作函数和格式转换函数。

本文主要为C语言中的标准操作,其中在C11和C23又添加和废弃了许多内容,但这块不会本文着重讲解。

UTC 协调世界时

纪元(地球纪元)

夏令时

⏰常量

⏱️CLOCKS_PER_SEC

#include <stdio.h>
#include <time.h>

int main(void) {
    clock_t beg = clock();
    clock_t end = clock();
    printf("Use time:%lf\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}

⏰类型

⏱️tm

struct tm {
    int tm_sec;    // [秒 [0, 61] (C89) [0, 60] (C99)] seconds after the minute
    int tm_min;    // [分 [0, 59]] minutes after the hour
    int tm_hour;   // [时 [0, 23]] hours since midnight
    int tm_mday;   // [日 [1, 31]] day of the month
    int tm_mon;    // [月 [0, 11]] months since January
    int tm_year;   // [年 [1900:0]] years since 1900
    int tm_wday;   // [周 [0, 6]] days since Sunday
    int tm_yday;   // [第几天 [0, 365]] days since January 1
    int tm_isdst;  // [夏时令标签] daylight savings time flag
};
#include <stdio.h>
#include <time.h>

int main(void) {
    struct tm start = {.tm_mday = 1};
    mktime(&start);
    // Sun Jan 01 00:00:00 1900
    printf("%s\n", asctime(&start));
}

⏱️time_t

⏱️clock_t

⏱️timespec (C11)

// 保有时间间隔的结构体,将其拆分成秒数和纳秒数。
struct timespec {
    time_t tv_sec;   // [秒 >= 0] Seconds 
    long   tv_nsec;  // [纳秒 [0, 999999999]] Nanoseconds
};

timespec_get()

C11 返回基于给定时间基底的日历时间

#include <stdint.h>
#include <stdio.h>
#include <time.h>

int main(void) {
    struct timespec ts;
    // C11 函数
    // 修改 ts 所指向的 struct timespec 对象
    // 以保有以时间基底 base 表示的当前日历时间。
#ifdef TIME_UTC
    timespec_get(&ts, TIME_UTC);
#endif

    char buff[100];
    strftime(buff, sizeof buff, "%D %T", gmtime(&ts.tv_sec));
    printf("Current time: %s.%09ld UTC\n", buff, ts.tv_nsec);
    printf("Raw timespec.time_t: %jd\n", (intmax_t)ts.tv_sec);
    printf("Raw timespec.tv_nsec: %09ld\n", ts.tv_nsec);
}

⏰函数-时间操作

⏲️time

time - cppreference.com

🏷️返回纪元开始经过的当前系统日历时间

time_t time( time_t *arg );
  • 运行失败
    • return -1
  • 运行成功
    • 入参,返回值数值一致
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t in;
    time_t out = time(&in);
    // >success:    out == in
    // >error:      -1
    // 1709276388 = time(1709276388)
    printf("%ld = time(%ld)\n", out, in);
    return 0;
}

⏲️clock

clock - cppreference.com

🏷️返回未加工的程序启动时开始经过的处理器时间

clock_t clock(void);

最经典的应用就是计时器了。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ARR_LEN (100000)

// return (arg1 > arg2) - (arg1 < arg2); // 可行的简写
// return arg1 - arg2; // 错误的简写(若给出 INT_MIN 则会失败)
int cmp(const void* a, const void* b) {
    int arg1 = *(const int*)a;
    int arg2 = *(const int*)b;

    if (arg1 < arg2) {
        return -1;
    }
    if (arg1 > arg2) {
        return 1;
    }
    return 0;
}

int main(void) {
    int arr[ARR_LEN];
    
    clock_t beg = clock();
    qsort(arr, sizeof(arr) / sizeof(*arr), sizeof(int), cmp);
    clock_t end = clock();
    printf("Use time:%lf\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}

⏲️difftime

difftime - cppreference.com

🏷️计算时间差

double difftime( time_t time_end, time_t time_beg );

简单测了下和直接相减效果一样。可能是为了统一接口和适配器模式。

#include <stdio.h>
#include <time.h>

int main(void) {
    time_t now = time(NULL);
    time_t beg = {0};

    // 计算时间差
    double during = difftime(now, beg);
    printf("Now:\t %lf\n", 1.0 * now);
    printf("During:\t %lf\n", during);
}

⏰函数-格式转换-类型转换

⏲️gmtime

gmtime, gmtime_r, gmtime_s - cppreference.com

🏷️将从纪元开始的时间转换成以协调世界时(UTC)表示的日历时间

🏷️time_t -> tm

注意,这里存储的是一个全局的静态对象。

函数 gmtime 可能不是线程安全的。

struct tm *gmtime  ( const time_t *timer );
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t t = time(NULL);

    struct tm *tm_p1 = gmtime(&t);
    struct tm *tm_p2 = gmtime(&t);
    // 指向同一个静态对象
    printf("First  struct tm* = %p\n", tm_p1);
    printf("Second struct tm* = %p\n", tm_p2);

    printf("UTC:   %s", asctime(gmtime(&t)));
    printf("local: %s", asctime(localtime(&t)));
}

⏲️localtime

localtime, localtime_r, localtime_s - cppreference.com

🏷️将从纪元开始的时间转换成以本地时间表示的日历时间

🏷️time_t -> tm

注意,这里存储的是一个全局的静态对象。

函数 localtime 可以不是线程安全的。

struct tm *localtime  ( const time_t *timer );
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t t = time(NULL);

    struct tm *tm_p1 = localtime(&t);
    struct tm *tm_p2 = localtime(&t);
    // 指向同一个静态对象
    printf("First  struct tm* = %p\n", tm_p1);
    printf("Second struct tm* = %p\n", tm_p2);

    printf("UTC:   %s", asctime(gmtime(&t)));
    printf("local: %s", asctime(localtime(&t)));
}

⏲️mktime

mktime - cppreference.com

🏷️将日历时间转换成纪元开始经过的时间

🏷️tm -> time_t

time_t mktime( struct tm *time );
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    time_t now = time(NULL);
    // 获取当前时间,并加一天
    struct tm tmm = *localtime(&now);
    tmm.tm_mday += 1;
    time_t nexDay = mktime(&tmm);

    printf("Now:\t%ld\n", now);
    printf("NexDay:\t%ld\n", nexDay);
    printf("OneDayTime:\t%lf\n", difftime(nexDay, now));
    printf("24*60*60=\t%d\n", 24 * 60 * 60);
}

⏰函数-格式转换-文本表示

⏲️asctime

asctime, asctime_s - cppreference.com

🏷️将 struct tm 对象转换成文本表示

🏷️tm -> str

asctime 返回指向静态数据的指针从而不是线程安全的。

char* asctime( const struct tm* time_ptr );
#include <stdio.h>
#include <time.h>

int main(void) {
    struct tm tm = *localtime(&(time_t){time(NULL)});
    printf("%s\n", asctime(&tm));

    printf("%p\n", asctime(&tm));
    printf("%p\n", asctime(&tm));
}

⏲️ctime

ctime, ctime_s - cppreference.com

🏷️将 struct time_t 对象转换成文本表示

🏷️time_t -> str

char* ctime( const time_t* timer );
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t result = time(NULL);
    printf("%s\n", ctime(&result));

    printf("%p\n", ctime(&result));
    printf("%p\n", ctime(&result));
}

⏲️strftime

strftime - cppreference.com

🏷️将 struct tm 对象转换成自定义文本表示

🏷️time_t -> copyToStr

size_t strftime(char* str, 
                size_t count, 
                const char* format,
                const struct tm* time);
#include <stdio.h>
#include <time.h>

int main(void) {
    struct tm now_tm = *localtime(&(time_t){time(NULL)});

    char buff[1024];
    // >strftime():Friday 03/01/24 19:41:24
    if (strftime(buff, sizeof buff, ">strftime():%A %c", &now_tm)) {
        puts(buff);
    } else {
        puts("strftime failed");
    }
}

⏰格式

🧮Www Mmm dd hh:mm:ss yyyy\n

// `asctime() & ctime()`以下固定的 25 字符表示形式: `Www Mmm dd hh:mm:ss yyyy\n`
errno_t asctime_s( char* buf, rsize_t bufsz, const struct tm* time_ptr );
errno_t ctime_s( char *buf, rsize_t bufsz, const time_t* timer );
  • Www ——来自 time_ptr->tm_wday星期之日的三字母英文缩写, MonTueWedThuFriSatSun 之一。
  • Mmm ——来自 time_ptr->tm_mon名的三字母英文缩写, JanFebMarAprMayJunJulAugSepOctNovDec 之一。
  • dd ——来自 timeptr->tm_mday 的 2 位月之日,如同由 sprintf%2d 打印
  • hh ——来自 timeptr->tm_hour 的 2 位,如同由 sprintf%.2d 打印
  • mm ——来自 timeptr->tm_min 的 2 位,如同由 sprintf%.2d 打印
  • ss ——来自 timeptr->tm_sec 的 2 位,如同由 sprintf%.2d 打印
  • yyyy ——来自 timeptr->tm_year + 1900 的 4 位,如同由 sprintf%4d 打印

若是非法入参则行为未定义,如:

  • *time_ptr 的任何成员在其正常范围外则行为未定义

  • time_ptr->tm_year 所指示的历年拥有多于 4 位数或小于 1000 年则行为未定义。


自定义格式函数:

strftime()的格式:strftime - cppreference.com

⏰应用

下面两份是使用C++代码表示。

其中主要用到的是<ctime>的类型,还有一些别的库中对时间操作的函数。

✍️力扣1185. 一周中的第几天

给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。

输入为三个整数:daymonthyear,分别表示日、月、年。

您返回的结果必须是这几个值中的一个 {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}

static const std::string weekDays[] = { 
    "Sunday",   
    "Monday", 
    "Tuesday", 
    "Wednesday", 
    "Thursday", 
    "Friday", 
    "Saturday" 
};

class Solution {
public:
    std::string dayOfTheWeek(int day, int month, int year) {
        std::tm ctm{};
        ctm.tm_year = year - 1900;
        ctm.tm_mon = month - 1;
        ctm.tm_mday = day;
        // a timestamp
        std::time_t ctime_t = std::mktime(&ctm);
        // return a static ret
        ctm = *std::localtime(&ctime_t);
        return weekDays[ctm.tm_wday];
    }
};

✍️力扣1154. 一年中的第几天

给你一个字符串 date ,按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。返回该日期是当年的第几天。

class Solution {
public:
    int dayOfYear(string date) {
        std::tm dt;
        std::istringstream(date) >> std::get_time(&dt, "%Y-%m-%d");
        return dt.tm_yday + 1;
    }
};



END

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐