10.1 The Role of RTC and Time Representation
“RTC” stands for Real-Time Clock, which translates to a real-time clock chip. Real-time clock chips are among the most widely used electronic devices in daily life, providing accurate real-time information to people or electronic systems. The real-time clock chip offers a time read/write interface through its pins, and typically includes a battery to ensure that the internal circuit operates normally and keeps time even when the external system is powered off. Different clock chips have different internal mechanisms, and the format for storing time data and the methods for read/write operations vary. The Linux system and drivers encapsulate the operational details of different clock chips, providing a unified time operation interface for applications.
So how is time represented in the Linux world? Is it represented like humans do, with year, month, day + hour, minute, second? Smart programmers naturally avoid this simplistic approach; instead, they use a single integer to represent time, which indicates the difference in seconds from Epoch Time. Epoch Time refers to a specific moment: January 1, 1970, 00:00:00. Assuming N seconds have passed since January 1, 1970, 00:00:00, the time value in the Linux system is N.
This raises the question: why start from January 1, 1970, 00:00:00? What happened that year to make Unix systems adopt it as the “epoch”? Unix originated in that era, with its prototype released in 1969, initially based on hardware with a 60Hz time count. The Unix Programmer’s Manual published at the end of 1971 defined Unix Time starting from January 1, 1971, 00:00:00, incrementing every second by 60. Later, it was realized that if each second had 60 counts, the maximum time value would be reached in just 1.1 years. Thus, it was changed to count in seconds, allowing time to be represented for up to 68.1 years. The starting point was adjusted to 1970 for convenience in human memory and calculations. Thus, the Unix epoch was established, and Unix timestamps became a proprietary term. The Linux system subsequently adopted this method of defining time.
At that time, computer operating systems were 32-bit, and time was represented using a 32-bit signed integer, with a data range of -2147483648 to 2147483647. This means the maximum time value can only reach 2147483647 seconds, which translates to 68.1 years (2147483647 ÷ 365 ÷ 24 ÷ 60 ÷ 60 = 68.1 years). Therefore, the longest time that can be represented by 32 bits is 1970 + 68.1 = 2038. More precisely, on January 19, 2038, at 03:14:07, the time will reach its maximum value of 0x7FFFFFFF. After this point, the next second will revert to 0x80000000, which corresponds to December 13, 1901, at 20:45:52, leading to a phenomenon known as time wraparound, causing many systems to malfunction.
As mentioned earlier, this was the situation during the “first year” of Unix, where 32-bit time was sufficient to solve the problems of that time. Nowadays, mainstream CPUs are 64-bit, and using 64-bit data to represent time is a natural progression. A 64-bit signed integer can represent time until December 4, 292,277,026,596, at 15:30:08, ensuring that we no longer have to worry about time wraparound issues.

10.2 RTC Operation Commands
Now that we understand how time is represented, how does Linux use and maintain time? How can we manipulate time through Linux?
10.2.1 System Time and Hardware Time
In Linux, there are two types of clocks: system clock and hardware clock. The system time is maintained by the timer of the CPU’s main chip, and typically the highest precision timer on the chip is chosen as the time reference to avoid significant time drift after prolonged system operation. The characteristic of system time is that it is lost when the system loses power. The hardware clock refers to the time maintained by the RTC chip included in the system. RTC chips have a dual power supply mechanism with both a battery and system power; during normal operation, the system supplies power, and when the system loses power, the battery takes over. Therefore, the RTC time can continue to run normally even when the system power is off. The primary purpose of the hardware clock in the Linux system is to keep time when Linux is not running.
At system startup, the system time is initialized from the hardware clock, and thereafter the hardware clock is no longer used. When the system boots, the Linux operating system reads the hardware time from the RTC chip, and the CPU’s internal timer maintains the time thereafter. From this point on, the time used by the operating system is system time, and unless explicitly controlled through commands to read/write the RTC, the system will not fetch or synchronize time from the RTC.
10.2.2 System Time Operation Commands
To view the system time:
date
Sat May 1 08:11:19 EDT 2020
Formatted output:
date +”%Y-%m-%d”
2020-05-01
Output after 2 seconds:
date -d “2 second” +”%Y-%m-%d %H:%M.%S”
2020-05-01 14:21.31
Display the time for 1234567890 seconds:
date -d “1970-01-01 1234567890 seconds” +”%Y-%m-%d %H:%m:%S”
2009-02-13 23:02:30
Common format conversion:
date -d “2009-05-01″ +”%Y/%m/%d %H:%M.%S”
2020/05/01 00:00.00
Output other dates:
date -d “+1 day” +%Y%m%d # Display the date for the next day
date -d “-1 day” +%Y%m%d # Display the date for the previous day
date -d “-1 month” +%Y%m%d # Display the date for the previous month
date -d “+1 month” +%Y%m%d # Display the date for the next month
date -d “-1 year” +%Y%m%d # Display the date for the previous year
date -d “+1 year” +%Y%m%d # Display the date for the next year
Set system time:
date -s 20200501 # Set to 20200501, this will set the specific time to 00:00:00
date -s 01:01:01 # Set specific time, will not change the date
date -s “01:01:01 2020-05-01” # This sets the entire time
date -s “01:01:01 20200501” # This sets the entire time
date -s “2020-05-01 01:01:01” # This sets the entire time
date -s “20200501 01:01:01” # This sets the entire time
For more command parameters, visit: https://www.cnblogs.com/machangwei-8/p/10352546.html
10.2.3 Hardware Time Operation Commands
Display hardware time:
hwclock or hwclock -r or hwclock –show
Tuesday, April 11, 2000 13:24:35 -0.109687 seconds
Set hardware clock:
hwclock –set –date ‘2015-04-11 13:36:11’
Synchronize system clock to hardware clock:
hwclock -w
Synchronize hardware clock to system clock:
hwclock -s
For more command parameters, visit: https://www.cnblogs.com/wj78080458/p/9806774.html
10.3 RTC Data Structures and Functions
In the Linux environment, we have learned to use commands to modify system time and hardware time. In programming, we can certainly use the system call to manipulate time directly, but this approach is not very professional and does not meet most needs, as often we need not just to modify time but to perform calculations on time.
RTC programming focuses on learning time-related structures and related operation functions.
10.3.1 Time-Related Data Structures
In C programming, it is often necessary to trigger events at specific times, which involves obtaining system time. There are various structure types related to time. In the Linux system, time-related data types are defined in the header file /usr/include/sys/time.h:
There are several time-related data types:
1. time_t Type: Long Integer
Generally used to represent the number of seconds since Epoch Time (midnight January 1, 1970). The unit is seconds.
#define _TIME_T
typedef long time_t; #endif
The function time_t time(time_t* lpt) is used to obtain time_t data, returning the time elapsed since Epoch Time (midnight January 1, 1970) in seconds. If lpt is not NULL, the return value is also stored in the variable pointed to by lpt.
Example:
time_t t = time(NULL);
2. struct timeb Structure
It has four members: one for seconds and another for milliseconds.
struct timeb{
time_t time;
unsigned short millitm;
short timezone;
short dstflag; };
time is the number of seconds accumulated since Epoch Time (midnight January 1, 1970).
millitm is the number of milliseconds within a second.
dstflag is non-zero if this is daylight saving time.
timezone is the difference in minutes between UTC time and local time.
The function int ftime(struct timeb *tp) is used to obtain timeb, returning 0 on success and -1 on failure.
Example:
struct timeb tp;
ftime(&tp);
3. struct timeval and struct timezone Structures
timeval has two members: one for seconds and another for microseconds.
struct timeval{
long tv_sec; /* seconds */
long tv_usec;/* microseconds */
};
tv_sec is the number of seconds from Epoch Time to the creation of struct timeval, and tv_usec is the microsecond count, i.e., the fractional part of the second. For example, if tv_sec is 1234567890 and tv_usec is 1234, it means the current time is 1234567890 seconds and 1234 microseconds from Epoch Time.
struct timezone{
int tz_minuteswest;/* difference in minutes from Greenwich Mean Time */
int tz_dsttime; /* type of DST correction */
}; tz_minuteswest indicates the time difference in minutes between the current system’s time zone and UTC. For example, in Beijing GMT+8, tz_minuteswest is -480. tz_dsttime indicates daylight saving time (DST).
The function int gettimeofday(struct timeval* tv, struct timezone *tz) is used to obtain timeval and timezone. In the gettimeofday() function, either tv or tz can be NULL. If NULL, the corresponding structure will not be returned. The function returns 0 on success and -1 on failure, with the error code stored in errno.
Example:
struct timeval tv;
gettimeofday(&tv, NULL);
4. struct tm Structure
struct tm {
int tm_sec; /* seconds – range [0,59] */
int tm_min; /* minutes – range [0,59] */
int tm_hour; /* hours – range [0,23] */
int tm_mday; /* day of the month – range [1,31] */
int tm_mon; /* month (starting from January, 0 represents January) – range [0,11] */
int tm_year; /* year, starting from 1900 */
int tm_wday; /* day of the week – range [0,6], where 0 represents Sunday, 1 represents Monday, and so on */
int tm_yday; /* day of the year starting from January 1 – range [0,365], where 0 represents January 1, 1 represents January 2, and so on */
int tm_isdst; /* daylight saving time flag; if DST is in effect, tm_isdst is positive. If DST is not in effect, tm_isdst is 0; if the situation is unknown, tm_isdst is negative. */
};
int tm_sec represents the current seconds, normal range is 0-59
int tm_min represents the current minutes, range 0-59
int tm_hour represents the hours since midnight, range 0-23
int tm_mday represents the day of the current month, range 01-31
int tm_mon represents the current month, starting from January, range 0-11
int tm_year represents the number of years since 1900
int tm_wday represents the day of the week, starting from Monday, range 0-6
int tm_yday represents the number of days since January 1 of the current year, range 0-365
int tm_isdst is the daylight saving time flag
The function struct tm* gmtime(const time_t*timep) is used to parse and obtain tm. gmtime() converts the information in the time_t data type pointed to by timep into a human-readable date and time format, returning the result as a pointer to the struct tm. Example:
struct tm* tm =NULL ;
time_t t = time(NULL);
tm = gmtime(&t);
10.3.2 Time-Related Functions
1. Time Formatting Function
strftime() function prototype: size_t strftime(char *str, size_t max, char *fmt, struct tm *tp); The function recognizes a set of format commands that start with a percent sign (%), specified by fmt, and can use the strftime function to convert time into the desired format (the output is a string). strftime is similar to sprintf.
str represents the returned time string
count is the maximum number of bytes to write
format is a format string consisting of zero or more conversion specifiers and ordinary characters (except %)
tm is the input time
The format commands are case-sensitive:
%a abbreviated weekday name
%A full weekday name
%b abbreviated month name
%B full month name
%c standard date and time string
%C last two digits of the year
%d day of the month as a decimal number
%D month/day/year
%e day of the month as a decimal number (with a space for single digits)
%F year-month-day
%g last two digits of the year, using the week-based year
%G year, using the week-based year
%h abbreviated month name
%H hour (00-23)
%I hour (01-12)
%j day of the year (001-366)
%m month (01-12)
%M minute (00-59)
%n newline
%p local equivalent of AM or PM
%r 12-hour time
%R hour and minute: hh:mm
%S second (00-59)
%t horizontal tab
%T hour, minute, and second: hh:mm:ss
%u day of the week (1-7, where Monday is 1)
%U week number of the year (00-53), with Sunday as the first day of the week
%V week number of the year, using the week-based year
%w day of the week (0-6, where Sunday is 0)
%W week number of the year (00-53), with Monday as the first day of the week
%x standard date string
%X standard time string
%y year without century (00-99)
%Y year with century
%z, %Z time zone name; if the time zone name cannot be obtained, an empty string is returned.
%% percent sign
Example:
time_t t = time(NULL);
struct tm *info;
info = gmtime(&t);
strftime(buffer, 80, “%Y-%m-%d %H:%M:%S”, info);
printf(“Formatted date & time : |%s|\n”, buffer );
2. localtime Function
localtime() function prototype: struct tm *localtime(const time_t *timer) uses the value of timer to fill the tm structure, interpreting the value of timer in the local time zone.
Example:
time_t rawtime;
struct tm *info;
time( &rawtime );
info = localtime( &rawtime );
3. mktime Function
mktime() function prototype: time_t mktime(struct tm *timeptr) converts the structure pointed to by timeptr into a time_t value based on the local time zone. The function converts the tm structure data pointed to by timeptr into the number of seconds elapsed since January 1, 1970, 00:00:00 UTC. The function returns a time_t value corresponding to the calendar time passed as a parameter. If an error occurs, it returns -1.
Example:
time_t rawtime;
struct tm * timeinfo;
int year, month ,day;
/* Get current time information and modify user input */
time ( &rawtime );
timeinfo = localtime ( &rawtime );
timeinfo->tm_year-= 1;// last year’s date
rawtime = mktime ( timeinfo );
4. asctime Function
asctime() function prototype: char *asctime(const struct tm *timeptr); returns a pointer to a string representing the date and time of the struct timeptr. It contains the date and time information in a readable format: Www Mmm dd hh:mm:ss yyyy, where Www represents the weekday, Mmm is the month in letters, dd is the day of the month, hh:mm:ss is the time, and yyyy is the year.
Example:
struct tm t;
t.tm_sec = 10;
t.tm_min = 10;
t.tm_hour = 6;
t.tm_mday = 25;
t.tm_mon = 2;
t.tm_year = 89;
t.tm_wday = 6;
puts(asctime(&t));
5. ctime Function
ctime() function prototype: char *ctime(const time_t *timer); converts the result obtained from the time function into a time string, returning a string representing local time based on the parameter timer. The returned string format is: Www Mmm dd hh:mm:ss yyyy, where Www represents the weekday, Mmm is the month in letters, dd is the day of the month, hh:mm:ss is the time, and yyyy is the year. Calling ctime(t) is equivalent to asctime(localtime(t)).
Example:
time_t curtime;
time(&curtime);
printf(“Current time = %s”, ctime(&curtime));
10.4 RTC Time Programming Example
In an embedded Linux environment, RTC time programming is the same as programming in a desktop Linux environment. This article demonstrates programming in a desktop Linux environment to implement time display, calculation, and other functions. The compiled program is named “mytime”. After entering the program, different commands can be used to complete different functions, as shown in the table below:
|
Serial No. |
Command |
Function |
Example |
|
1 |
p |
Display current time in the terminal |
p |
|
2 |
y |
Display yesterday’s date in the terminal |
y |
|
3 |
n |
Display the number of days until the new year in the terminal |
n |
|
4 |
a |
Display age based on the input birth year in the terminal |
a 2001 |
|
5 |
e |
Exit the program |
e |
In the main function, the usage manual is printed first, and then a loop receives user input commands, calling the corresponding functions based on the commands:
32 // Print usage manual
33 printf( “\n\n”\
34 “Usage:\np\n”\
35 “y\n”\
36 “n\n”\
37 “a 2001\n”\
38 “e\n”\
39 “p: Display current time in the terminal\ny: View yesterday’s date\nn: View the number of days until the new year\n”\
40 “a: Calculate age based on input birth year\ne: Exit the program\n”\
41 );
42
43 // Main program loop to receive input commands and execute different functions based on commands
44 while (1){
45 if (c !=’\n’)
46 printf(“\nPlease enter command:”);
47 scanf(“%c”,&c);
48 switch(c){
49 case ‘p’:// Display current time in the terminal
50 displaydate();
51 break;
52 case ‘y’:// Display yesterday’s date
53 displayyesterday();
54 break;
55 case ‘n’:// Display the number of days until the new year
56 displaynewyear();
57 break;
58 case ‘a’:// Calculate age based on input birth year
59 scanf(“%d”,&age);
60 displayage(age);
61 break;
62 case ‘e’:// Exit the program
63 exit(0);
64 break;
65 default : /* optional */
66 break;
67 }
68 }
Function to display current time in the terminal:
/**********************************************************
72 * Function Name: displaydate
73 * Function Description: Print current time information in the terminal
74 * Input Parameters: None
75 * Output Parameters: None
76 * Return Value: None
78 ***********************************************************/
79 void displaydate(){
80 struct tm *ptr;
81 time_t lt;
82
83 /* Get calendar time */
84 lt = time(NULL);
85
86 /* Convert to local time */
87 ptr = localtime(<);
88
89 /* Print in local time string format */
90 printf(“%s\n”,ctime(<));
91
92 /* Print in local time string format */
93 printf(“%s\n”,asctime(ptr));
94
95 }
Function to display yesterday’s date in the terminal:
/**********************************************************
97 * Function Name: displayyesterday
98 * Function Description: Print yesterday’s date in the terminal
99 * Input Parameters: None
100 * Output Parameters: None
101 * Return Value: None
103 ***********************************************************/
104 void displayyesterday(void){
105 struct tm *ptr;
106 time_t lt;
107
108 /* Get calendar time */
109 lt = time(NULL);
110 lt -= 24*60*60;
111
112 /* Convert to local time */
113 ptr = localtime(<);
114
115 /* Print in local time string format */
116 printf(“Yesterday was %d year %d month %d day\n”,ptr->tm_year + 1900,ptr->tm_mon + 1,ptr->tm_mday);
117 }
Function to display the number of days until the new year:
118 /**********************************************************
119 * Function Name: displaynewyear
120 * Function Description: Print the number of days until the new year in the terminal
121 * Input Parameters: None
122 * Output Parameters: None
123 * Return Value: None
124 * 2020/05/10 V1.0 yanxni Created
125 ***********************************************************/
126 void displaynewyear(void){
127 struct tm *ptr;
128 time_t lt,lt2;
129 int date;
130 /* Get calendar time */
132 lt = time(NULL);
133 /* Convert to local time */
135 ptr = localtime(<);
136 /* Construct local time for the new year */
137 ptr->tm_year += 1;
138 ptr->tm_mon = 0;
139 ptr->tm_mday =1;
140 ptr->tm_hour =0;
141 ptr->tm_min =0;
142 ptr->tm_sec =0;
143 lt2 = mktime(ptr);
144 date = (lt2-lt)/(24*60*60);
145 printf(“%d days until the new year\n”,date);
146 }
Function to display age based on input birth year:
/**********************************************************
151 * Function Name: displayage
152 * Function Description: Print age in the terminal
153 * Input Parameters: Birth year
154 * Output Parameters: None
155 * Return Value: None
156 * 2020/05/10 V1.0 yanxni Created
157 ***********************************************************/
158 void displayage(int year){
159 struct tm *ptr;
160 time_t lt;
161 /* Get calendar time */
162 lt = time(NULL);
163 /* Convert to local time */
164 ptr = localtime(<);
165 /* Calculate and print age */
166 printf(“Your age is: %d years\n”,ptr->tm_year +1900 – year);
167 }