mirror of https://github.com/flysand7/ciabatta.git
				
				
				
			Implement some time.h functions
This commit is contained in:
		
							parent
							
								
									01394ae39a
								
							
						
					
					
						commit
						dc3bab6eb5
					
				
							
								
								
									
										55
									
								
								inc/time.h
								
								
								
								
							
							
						
						
									
										55
									
								
								inc/time.h
								
								
								
								
							|  | @ -1,15 +1,28 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| // 7.29.1 p.2: Macros
 | ||||
| 
 | ||||
| #define __STDC_VERSION_TIME_H__ 202311L | ||||
| 
 | ||||
| #if !defined(NULL) | ||||
|     #define NULL ((void *)0) | ||||
| #endif | ||||
| 
 | ||||
| #define TIME_UTC 1 | ||||
| 
 | ||||
| #define CLOCKS_PER_SEC  ((clock_t)1000000) | ||||
| 
 | ||||
| #define TIME_UTC 1 | ||||
| #define TIME_MONOTONIC 2 | ||||
| #define TIME_ACTIVE 3 | ||||
| #define TIME_THREAD_ACTIVE 4 | ||||
| 
 | ||||
| // 7.29.1 p.4: Types
 | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
|     typedef unsigned long long size_t; | ||||
| #else | ||||
|     typedef unsigned long size_t; | ||||
| #endif | ||||
| 
 | ||||
| typedef unsigned long long clock_t; | ||||
| typedef unsigned long long time_t; | ||||
| 
 | ||||
|  | @ -36,28 +49,30 @@ typedef unsigned long long time_t; | |||
|     }; | ||||
| #endif | ||||
| 
 | ||||
| clock_t    clock       (void); | ||||
| time_t     time        (time_t *timer); | ||||
| time_t     mktime      (struct tm *timeptr); | ||||
| double     difftime    (time_t time1, time_t time0); | ||||
| int        timespec_get(struct timespec *ts, int base); | ||||
| char      *asctime     (const struct tm *timeptr); | ||||
| char      *ctime       (const time_t *timer); | ||||
| struct tm *gmtime      (const time_t *timer); | ||||
| struct tm *localtime   (const time_t *timer); | ||||
| // Basic timer functions
 | ||||
| clock_t clock(void); | ||||
| time_t time(time_t *timer); | ||||
| double difftime(time_t time1, time_t time0); | ||||
| 
 | ||||
| size_t strftime( | ||||
|     char *restrict s, | ||||
|     size_t maxsize, | ||||
|     const char *restrict format, | ||||
|     const struct tm *restrict timeptr | ||||
| ); | ||||
| // Human-readable time functions
 | ||||
| time_t mktime(struct tm *timeptr); | ||||
| struct tm *gmtime(const time_t *timer); | ||||
| struct tm *localtime(const time_t *timer); | ||||
| 
 | ||||
| // Timespec functions
 | ||||
| int timespec_get(struct timespec *ts, int base); | ||||
| int timespec_getres(struct timespec *ts, int base); | ||||
| 
 | ||||
| // Time formatting functions
 | ||||
| char *asctime(const struct tm *timeptr); | ||||
| char *ctime(const time_t *timer); | ||||
| size_t strftime(char *restrict str, size_t sz, const char *restrict fmt, const struct tm *restrict time); | ||||
| 
 | ||||
| #ifdef __STDC_WANT_LIB_EXT1__ | ||||
| 
 | ||||
| errno_t asctime_s(char *s, rsize_t maxsize, const struct tm *timeptr); | ||||
| errno_t ctime_s  (char *s, rsize_t maxsize, const time_t *timer); | ||||
| struct tm *gmtime_s   (const time_t * restrict timer, struct tm * restrict result); | ||||
| errno_t ctime_s(char *s, rsize_t maxsize, const time_t *timer); | ||||
| struct tm *gmtime_s(const time_t * restrict timer, struct tm * restrict result); | ||||
| struct tm *localtime_s(const time_t * restrict timer, struct tm * restrict result); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -1,17 +1,18 @@ | |||
| 
 | ||||
| #define TIME_TICKS_PER_SEC          1000 | ||||
| #define FILE_TICKS_PER_SEC          10000000 | ||||
| #define FILE_TICKS_UNTIL_UNIX_EPOCH 116444736000000000ULL | ||||
| #define NS_PER_SEC                  1000000000 | ||||
| #define NS_PER_MS                   1000000 | ||||
| 
 | ||||
| #define NANOS_PER_SEC               1000000000 | ||||
| #define NANOS_PER_FILE_TICK         (NANOS_PER_SEC / FILE_TICKS_PER_SEC) | ||||
| #define NANOS_PER_TIME_TICK         (NANOS_PER_SEC / TIME_TICKS_PER_SEC) | ||||
| #define TIME_TICKS_PER_SEC          1000000000ULL | ||||
| #define NS_BEFORE_UNIX_EPOCH        11644473600000000000ULL | ||||
| 
 | ||||
| typedef struct tm tm_t; | ||||
| 
 | ||||
| // Store the time since we started running the process
 | ||||
| static uint64_t timer_freq; | ||||
| static uint64_t timer_start; | ||||
| 
 | ||||
| // Initialize timers
 | ||||
| 
 | ||||
| static void _setup_timer(void) { | ||||
|     LARGE_INTEGER freq, start; | ||||
|     QueryPerformanceFrequency(&freq); | ||||
|  | @ -20,19 +21,63 @@ static void _setup_timer(void) { | |||
|     timer_freq  = freq.QuadPart; | ||||
| } | ||||
| 
 | ||||
| static ULONGLONG win32_get_unix_nanos() { | ||||
|     FILETIME ft; | ||||
|     GetSystemTimeAsFileTime(&ft); | ||||
| // Timestamp utilitity functions
 | ||||
| 
 | ||||
| static ULONGLONG _time_filetime_to_ns(FILETIME ft) { | ||||
|     ULARGE_INTEGER ft64 = { | ||||
|         .LowPart  = ft.dwLowDateTime, | ||||
|         .HighPart = ft.dwHighDateTime, | ||||
|     }; | ||||
|     ULONGLONG fticks      = ft64.QuadPart; | ||||
|     ULONGLONG unix_fticks = fticks - FILE_TICKS_UNTIL_UNIX_EPOCH; | ||||
|     ULONGLONG unix_nanos  = unix_fticks * NANOS_PER_FILE_TICK; | ||||
|     return unix_nanos; | ||||
|     ULONGLONG fticks = ft64.QuadPart; | ||||
|     return fticks * 100; | ||||
| } | ||||
| 
 | ||||
| static ULONGLONG _time_utc_ns() { | ||||
|     FILETIME ft; | ||||
|     GetSystemTimeAsFileTime(&ft); | ||||
|     ULONGLONG ns = _time_filetime_to_ns(ft); | ||||
|     ULONGLONG unix_ns = ns - NS_BEFORE_UNIX_EPOCH; | ||||
|     return unix_ns; | ||||
| } | ||||
| 
 | ||||
| static ULONGLONG _time_mono_ns() { | ||||
|     ULONGLONG time_ms = GetTickCount64(); | ||||
|     ULONGLONG time_ns = time_ms * NS_PER_MS; | ||||
|     return time_ns; | ||||
| } | ||||
| 
 | ||||
| static ULONGLONG _time_process_ns() { | ||||
|     FILETIME _1; | ||||
|     FILETIME _2; | ||||
|     FILETIME ktime; | ||||
|     FILETIME utime; | ||||
|     HANDLE current_process = GetCurrentProcess(); | ||||
|     if(GetProcessTimes(current_process, &_1, &_2, &ktime, &utime) == 0) { | ||||
|         abort(); | ||||
|     } | ||||
|     ULONGLONG kernel_time = _time_filetime_to_ns(ktime); | ||||
|     ULONGLONG user_time = _time_filetime_to_ns(utime); | ||||
|     ULONGLONG cpu_time = kernel_time + user_time; | ||||
|     return cpu_time; | ||||
| } | ||||
| 
 | ||||
| static ULONGLONG _time_thread_ns() { | ||||
|     FILETIME _1; | ||||
|     FILETIME _2; | ||||
|     FILETIME ktime; | ||||
|     FILETIME utime; | ||||
|     HANDLE current_thread = GetCurrentThread(); | ||||
|     if(GetThreadTimes(current_thread, &_1, &_2, &ktime, &utime) == 0) { | ||||
|         abort(); | ||||
|     } | ||||
|     ULONGLONG kernel_time = _time_filetime_to_ns(ktime); | ||||
|     ULONGLONG user_time = _time_filetime_to_ns(utime); | ||||
|     ULONGLONG cpu_time = kernel_time + user_time; | ||||
|     return cpu_time; | ||||
| } | ||||
| 
 | ||||
| // Timer functions
 | ||||
| 
 | ||||
| clock_t clock(void) { | ||||
|     LARGE_INTEGER curr; | ||||
|     if (!QueryPerformanceCounter(&curr)) { | ||||
|  | @ -42,14 +87,14 @@ clock_t clock(void) { | |||
|         return -1; | ||||
|     } | ||||
|     clock_t ticks = curr.QuadPart - timer_start; | ||||
|     // Basically millis / timer_freq * CLOCKS_PER_SEC but more precise
 | ||||
|     // Same as (ticks / timer_freq * CLOCKS_PER_SEC) but more precise
 | ||||
|     return ticks / timer_freq * CLOCKS_PER_SEC | ||||
|          + ticks % timer_freq * CLOCKS_PER_SEC / timer_freq; | ||||
| } | ||||
| 
 | ||||
| time_t time(time_t *timer) { | ||||
|     ULONGLONG unix_nanos  = win32_get_unix_nanos(); | ||||
|     time_t    timer_ticks = unix_nanos / NANOS_PER_TIME_TICK; | ||||
|     ULONGLONG unix_nanos  = _time_mono_ns(); | ||||
|     time_t    timer_ticks = unix_nanos; | ||||
|     if(timer != NULL) { | ||||
|         *timer = timer_ticks; | ||||
|     } | ||||
|  | @ -63,9 +108,290 @@ double difftime(time_t time1, time_t time0) { | |||
| } | ||||
| 
 | ||||
| int timespec_get(struct timespec *ts, int base) { | ||||
|     if (base != TIME_UTC) return 0; | ||||
|     ULONGLONG unix_nanos = win32_get_unix_nanos(); | ||||
|     ts->tv_sec  = unix_nanos / NANOS_PER_SEC; | ||||
|     ts->tv_nsec = unix_nanos; | ||||
|     ULONGLONG current_ns; | ||||
|     switch(base) { | ||||
|         case TIME_UTC:           current_ns = _time_utc_ns(); break; | ||||
|         case TIME_MONOTONIC:     current_ns = _time_mono_ns(); break; | ||||
|         case TIME_ACTIVE:        current_ns = _time_process_ns(); break; | ||||
|         case TIME_THREAD_ACTIVE: current_ns = _time_thread_ns(); break; | ||||
|         default: return 0; | ||||
|     } | ||||
|     ts->tv_sec  = current_ns / NS_PER_SEC; | ||||
|     ts->tv_nsec = current_ns; | ||||
|     return base; | ||||
| } | ||||
| 
 | ||||
| int timespec_getres(struct timespec *ts, int base) { | ||||
|     switch(base) { | ||||
|         case TIME_UTC:           return NS_PER_SEC; | ||||
|         case TIME_MONOTONIC:     return NS_PER_SEC; | ||||
|         case TIME_ACTIVE:        return NS_PER_SEC; | ||||
|         case TIME_THREAD_ACTIVE: return NS_PER_SEC; | ||||
|         default: return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Human-readable time
 | ||||
| 
 | ||||
| const int _days_in_month[12] = { | ||||
|     31,28, 31,30,31, 30,31,31, 30,31,30, 31 | ||||
| }; | ||||
| 
 | ||||
| const int _month_days_since_year_start[12] = { | ||||
|     0, | ||||
|     31, | ||||
|     31+28, | ||||
|     31+28+31, | ||||
|     31+28+31+30, | ||||
|     31+28+31+30+31, | ||||
|     31+28+31+30+31+30, | ||||
|     31+28+31+30+31+30+31, | ||||
|     31+28+31+30+31+30+31+31, | ||||
|     31+28+31+30+31+30+31+31+30, | ||||
|     31+28+31+30+31+30+31+31+30+31, | ||||
|     31+28+31+30+31+30+31+31+30+31+30, | ||||
| }; | ||||
| 
 | ||||
| static bool _is_leap_year(int year) { | ||||
|     if(year % 400 == 0) { | ||||
|         return true; | ||||
|     } | ||||
|     if(year % 100 == 0) { | ||||
|         return false; | ||||
|     } | ||||
|     if(year % 4 == 0) { | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static int _leap_years_before(int year) { | ||||
|     year--; | ||||
|     return (year / 4) - (year / 100) + (year / 400); | ||||
| } | ||||
| 
 | ||||
| static int _leap_years_between(int start, int end) { | ||||
|     return _leap_years_before(end) - _leap_years_before(start + 1); | ||||
| } | ||||
| 
 | ||||
| static int _whole_days_since_year_start(int year, int mon, int day) { | ||||
|     int days = _month_days_since_year_start[mon] + day; | ||||
|     if(mon > 1 || (mon == 1 && day > 27)) { | ||||
|         if(_is_leap_year(year)) { | ||||
|             days += 1; | ||||
|         } | ||||
|     } | ||||
|     return days; | ||||
| } | ||||
| 
 | ||||
| static void _tm_adjust(tm_t *time) { | ||||
|     long long year = 1900 + time->tm_year; | ||||
|     long long mon = time->tm_mon; | ||||
|     long long day = time->tm_mday - 1; | ||||
|     long long hour = time->tm_hour; | ||||
|     long long min = time->tm_min; | ||||
|     long long sec = time->tm_sec; | ||||
|     // Adjust
 | ||||
|     if(sec < 0) { | ||||
|         min -= (-sec+59)/60; | ||||
|         sec = (sec%60 + 60) % 60; | ||||
|     } | ||||
|     else if(sec >= 60) { | ||||
|         min += sec / 60; | ||||
|         sec %= 60; | ||||
|     } | ||||
|     if(min < 0) { | ||||
|         hour -= (-min+59)/60; | ||||
|         min = (min%60 + 60) % 60; | ||||
|     } | ||||
|     else if(min >= 60) { | ||||
|         hour += min / 60; | ||||
|         min %= 60; | ||||
|     } | ||||
|     if(hour < 0) { | ||||
|         day -= (-hour+23)/24; | ||||
|         hour = (hour%24 + 24) % 24; | ||||
|     } | ||||
|     else if(hour >= 24) { | ||||
|         day += hour / 24; | ||||
|         hour %= 24; | ||||
|     } | ||||
|     if(mon < 0) { | ||||
|         year -= (-mon+11)/12; | ||||
|         mon = (mon%12+12)%12; | ||||
|     } | ||||
|     else if(mon >= 12) { | ||||
|         year += mon / 12; | ||||
|         mon %= 12; | ||||
|     } | ||||
|     if(day < 0) { | ||||
|         for(;;) { | ||||
|             int mon_prev = mon - 1; | ||||
|             int year_prev = year; | ||||
|             if(mon_prev == -1) { | ||||
|                 mon_prev = 11; | ||||
|                 year_prev -= 1; | ||||
|             } | ||||
|             int days_in_prev_month = _days_in_month[mon_prev]; | ||||
|             if(mon_prev == 1 && _is_leap_year(year_prev)) { | ||||
|                 days_in_prev_month += 1; | ||||
|             } | ||||
|             if(-day > days_in_prev_month) { | ||||
|                 day += days_in_prev_month; | ||||
|                 mon -= 1; | ||||
|                 if(mon == -1) { | ||||
|                     mon = 11; | ||||
|                     year -= 1; | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 break; // Can't subtract any whole months, exit the loop
 | ||||
|             } | ||||
|         } | ||||
|         if(day < 0) { | ||||
|             mon -= 1; | ||||
|             day = _days_in_month[mon] + day; | ||||
|             if(mon == -1) { | ||||
|                 mon = 11; | ||||
|                 year -= 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         for(;;) { | ||||
|             int days_in_month = _days_in_month[mon]; | ||||
|             if(mon == 1 && _is_leap_year(year)) { | ||||
|                 days_in_month += 1; | ||||
|             } | ||||
|             if(day < days_in_month) { | ||||
|                 break; | ||||
|             } | ||||
|             day -= days_in_month; | ||||
|             mon += 1; | ||||
|         } | ||||
|     } | ||||
|     // Refill the struct with normalized time
 | ||||
|     time->tm_year = year - 1900; | ||||
|     time->tm_mon = mon; | ||||
|     time->tm_mday = day + 1; | ||||
|     time->tm_hour = hour; | ||||
|     time->tm_min = min; | ||||
|     time->tm_sec = sec; | ||||
| } | ||||
| 
 | ||||
| time_t mktime(tm_t *time) { | ||||
|     // Get the human-readable time from the structure
 | ||||
|     _tm_adjust(time); | ||||
|     long long year = 1900 + time->tm_year; | ||||
|     long long mon = time->tm_mon; | ||||
|     long long day = time->tm_mday - 1; | ||||
|     long long hour = time->tm_hour; | ||||
|     long long min = time->tm_min; | ||||
|     long long sec = time->tm_sec; | ||||
|     if(year < 1970) { | ||||
|         return (time_t)(-1); | ||||
|     } | ||||
|     // Calculate unix timestamp
 | ||||
|     time_t timestamp; | ||||
|     timestamp = (year - 1970) * 365 + _leap_years_between(1970, year); | ||||
|     time_t days_since_year = _whole_days_since_year_start(year, mon, day); | ||||
|     timestamp = timestamp + days_since_year; | ||||
|     time_t days_since_epoch = timestamp; | ||||
|     timestamp = timestamp * 24 + hour; | ||||
|     timestamp = timestamp * 60 + min; | ||||
|     timestamp = timestamp * 60 + sec; | ||||
|     timestamp = timestamp * NS_PER_SEC; | ||||
|     // Refill the week day and year day
 | ||||
|     time->tm_wday = (days_since_epoch % 7 + 4) % 7; // 1970-01-01 is thursday (wday=4)
 | ||||
|     time->tm_yday = days_since_year; | ||||
|     time->tm_isdst = -1; | ||||
|     return timestamp; | ||||
| } | ||||
| 
 | ||||
| // Breaking-down of the time
 | ||||
| 
 | ||||
| tm_t *gmtime(const time_t *timer) { | ||||
|     tm_t *time = malloc(sizeof(tm_t)); | ||||
|     time_t timestamp = *timer; | ||||
|     timestamp /= NS_PER_SEC; | ||||
|     time->tm_sec = timestamp % 60; | ||||
|     timestamp /= 60; | ||||
|     time->tm_min = timestamp % 60; | ||||
|     timestamp /= 60; | ||||
|     time->tm_hour = timestamp % 24; | ||||
|     timestamp /= 24; | ||||
|     int year = 1970; | ||||
|     int days = timestamp; | ||||
|     // Start subtracting 400,100,4-year-stretches
 | ||||
|     const int DAYS_IN_400_YEARS = 365 * 400 + 97; | ||||
|     const int DAYS_IN_100_YEARS = 365 * 100 + 24; | ||||
|     const int DAYS_IN_4_YEARS   = 365 * 4 + 1; | ||||
|     year += (days / DAYS_IN_400_YEARS) * 400; | ||||
|     days %= DAYS_IN_400_YEARS; | ||||
|     year += (days / DAYS_IN_100_YEARS) * 100; | ||||
|     days %= DAYS_IN_100_YEARS; | ||||
|     year += (days / DAYS_IN_4_YEARS) * 4; | ||||
|     days %= DAYS_IN_4_YEARS; | ||||
|     // Save
 | ||||
|     int days_since_epoch = days; | ||||
|     // Count remaining up-to 3 years in a loop
 | ||||
|     for(;;) { | ||||
|         int this_year_days = 365 + _is_leap_year(year); | ||||
|         if(days < this_year_days) { | ||||
|             break; | ||||
|         } | ||||
|         year += 1; | ||||
|         days -= this_year_days; | ||||
|     } | ||||
|     // Save
 | ||||
|     int days_since_year = days; | ||||
|     // Find the current month
 | ||||
|     int month = 0; | ||||
|     for(;;) { | ||||
|         int days_in_month = _days_in_month[month] + _is_leap_year(year); | ||||
|         if(days < days_in_month) { | ||||
|             break; | ||||
|         } | ||||
|         days -= days_in_month; | ||||
|         month += 1; | ||||
|     } | ||||
|     time->tm_year = year - 1900; | ||||
|     time->tm_mon = month; | ||||
|     time->tm_mday = days + 1; | ||||
|     time->tm_wday = (days_since_epoch % 7 + 4) % 7; // 1970-01-01 is thursday (wday=4)
 | ||||
|     time->tm_yday = days_since_year; | ||||
|     time->tm_isdst = -1; | ||||
|     return time; | ||||
| } | ||||
| 
 | ||||
| static bool _offset_utc_time_to_local(tm_t *time) { | ||||
|     TIME_ZONE_INFORMATION tz; | ||||
|     if(GetTimeZoneInformation(&tz) == TIME_ZONE_ID_INVALID) { | ||||
|         return false; | ||||
|     } | ||||
|     int bias = tz.Bias; | ||||
|     time->tm_min -= bias; | ||||
|     _tm_adjust(time); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| tm_t *localtime(const time_t *timer) { | ||||
|     tm_t *utc_time = gmtime(timer); | ||||
|     if(!_offset_utc_time_to_local(utc_time)) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return utc_time; | ||||
| } | ||||
| 
 | ||||
| // String formatting
 | ||||
| 
 | ||||
| char *asctime(const tm_t *timeptr) { | ||||
|     return NULL; | ||||
| } | ||||
| char *ctime(const time_t *timer) { | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| size_t strftime(char *restrict s, size_t size, const char *restrict fmt, const tm_t *restrict time) { | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,10 +1,26 @@ | |||
| 
 | ||||
| #include <time.h> | ||||
| #include <stdio.h> | ||||
| #include <time.h> | ||||
| static const char *const wday[] = { | ||||
|     "Sunday", "Monday", "Tuesday", "Wednesday", | ||||
|     "Thursday", "Friday", "Saturday", "-unknown-" | ||||
| }; | ||||
| 
 | ||||
| int main() { | ||||
|     time_t t1 = time(NULL); | ||||
|     time_t t2 = time(NULL); | ||||
|     double s = difftime(t2, t1); | ||||
|     // July 4, 2001: Wednesday
 | ||||
|     struct tm time_str; | ||||
|     time_str.tm_year = 2001 - 1900; | ||||
|     time_str.tm_mon = 7 - 1; | ||||
|     time_str.tm_mday = 4; | ||||
|     time_str.tm_hour = 0; | ||||
|     time_str.tm_min = 0; | ||||
|     time_str.tm_sec = 1; | ||||
|     time_str.tm_isdst = -1; | ||||
|     time_t timestamp; | ||||
|     if((timestamp = mktime(&time_str)) == (time_t)(-1)) { | ||||
|         time_str.tm_wday = 7; | ||||
|     } | ||||
|     char const *day_of_week = wday[time_str.tm_wday]; | ||||
|     struct tm *gmt = gmtime(×tamp); | ||||
|     struct tm *local = localtime(×tamp); | ||||
|     return 0; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue