Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
mbed_mktime.c
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2017-2017 ARM Limited 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 #include "mbed_mktime.h" 00018 00019 /* 00020 * time constants 00021 */ 00022 #define SECONDS_BY_MINUTES 60 00023 #define MINUTES_BY_HOUR 60 00024 #define SECONDS_BY_HOUR (SECONDS_BY_MINUTES * MINUTES_BY_HOUR) 00025 #define HOURS_BY_DAY 24 00026 #define SECONDS_BY_DAY (SECONDS_BY_HOUR * HOURS_BY_DAY) 00027 00028 /* 00029 * 2 dimensional array containing the number of seconds elapsed before a given 00030 * month. 00031 * The second index map to the month while the first map to the type of year: 00032 * - 0: non leap year 00033 * - 1: leap year 00034 */ 00035 static const uint32_t seconds_before_month[2][12] = { 00036 { 00037 0, 00038 31 * SECONDS_BY_DAY, 00039 (31 + 28) * SECONDS_BY_DAY, 00040 (31 + 28 + 31) * SECONDS_BY_DAY, 00041 (31 + 28 + 31 + 30) * SECONDS_BY_DAY, 00042 (31 + 28 + 31 + 30 + 31) * SECONDS_BY_DAY, 00043 (31 + 28 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, 00044 (31 + 28 + 31 + 30 + 31 + 30 + 31) * SECONDS_BY_DAY, 00045 (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31) * SECONDS_BY_DAY, 00046 (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30) * SECONDS_BY_DAY, 00047 (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31) * SECONDS_BY_DAY, 00048 (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, 00049 }, 00050 { 00051 0, 00052 31 * SECONDS_BY_DAY, 00053 (31 + 29) * SECONDS_BY_DAY, 00054 (31 + 29 + 31) * SECONDS_BY_DAY, 00055 (31 + 29 + 31 + 30) * SECONDS_BY_DAY, 00056 (31 + 29 + 31 + 30 + 31) * SECONDS_BY_DAY, 00057 (31 + 29 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, 00058 (31 + 29 + 31 + 30 + 31 + 30 + 31) * SECONDS_BY_DAY, 00059 (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31) * SECONDS_BY_DAY, 00060 (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30) * SECONDS_BY_DAY, 00061 (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31) * SECONDS_BY_DAY, 00062 (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, 00063 } 00064 }; 00065 00066 bool _rtc_is_leap_year(int year) { 00067 /* 00068 * since in practice, the value manipulated by this algorithm lie in the 00069 * range [70 : 138], the algorith can be reduced to: year % 4. 00070 * The algorithm valid over the full range of value is: 00071 00072 year = 1900 + year; 00073 if (year % 4) { 00074 return false; 00075 } else if (year % 100) { 00076 return true; 00077 } else if (year % 400) { 00078 return false; 00079 } 00080 return true; 00081 00082 */ 00083 return (year) % 4 ? false : true; 00084 } 00085 00086 time_t _rtc_mktime(const struct tm* time) { 00087 // partial check for the upper bound of the range 00088 // normalization might happen at the end of the function 00089 // this solution is faster than checking if the input is after the 19th of 00090 // january 2038 at 03:14:07. 00091 if ((time->tm_year < 70) || (time->tm_year > 138)) { 00092 return ((time_t) -1); 00093 } 00094 00095 uint32_t result = time->tm_sec; 00096 result += time->tm_min * SECONDS_BY_MINUTES; 00097 result += time->tm_hour * SECONDS_BY_HOUR; 00098 result += (time->tm_mday - 1) * SECONDS_BY_DAY; 00099 result += seconds_before_month[_rtc_is_leap_year(time->tm_year)][time->tm_mon]; 00100 00101 if (time->tm_year > 70) { 00102 // valid in the range [70:138] 00103 uint32_t count_of_leap_days = ((time->tm_year - 1) / 4) - (70 / 4); 00104 result += (((time->tm_year - 70) * 365) + count_of_leap_days) * SECONDS_BY_DAY; 00105 } 00106 00107 if (result > INT32_MAX) { 00108 return (time_t) -1; 00109 } 00110 00111 return result; 00112 } 00113 00114 bool _rtc_localtime(time_t timestamp, struct tm* time_info) { 00115 if (((int32_t) timestamp) < 0) { 00116 return false; 00117 } 00118 00119 time_info->tm_sec = timestamp % 60; 00120 timestamp = timestamp / 60; // timestamp in minutes 00121 time_info->tm_min = timestamp % 60; 00122 timestamp = timestamp / 60; // timestamp in hours 00123 time_info->tm_hour = timestamp % 24; 00124 timestamp = timestamp / 24; // timestamp in days; 00125 00126 // compute the weekday 00127 // The 1st of January 1970 was a Thursday which is equal to 4 in the weekday 00128 // representation ranging from [0:6] 00129 time_info->tm_wday = (timestamp + 4) % 7; 00130 00131 // years start at 70 00132 time_info->tm_year = 70; 00133 while (true) { 00134 if (_rtc_is_leap_year(time_info->tm_year) && timestamp >= 366) { 00135 ++time_info->tm_year; 00136 timestamp -= 366; 00137 } else if (!_rtc_is_leap_year(time_info->tm_year) && timestamp >= 365) { 00138 ++time_info->tm_year; 00139 timestamp -= 365; 00140 } else { 00141 // the remaining days are less than a years 00142 break; 00143 } 00144 } 00145 00146 time_info->tm_yday = timestamp; 00147 00148 // convert days into seconds and find the current month 00149 timestamp *= SECONDS_BY_DAY; 00150 time_info->tm_mon = 11; 00151 bool leap = _rtc_is_leap_year(time_info->tm_year); 00152 for (uint32_t i = 0; i < 12; ++i) { 00153 if ((uint32_t) timestamp < seconds_before_month[leap][i]) { 00154 time_info->tm_mon = i - 1; 00155 break; 00156 } 00157 } 00158 00159 // remove month from timestamp and compute the number of days. 00160 // note: unlike other fields, days are not 0 indexed. 00161 timestamp -= seconds_before_month[leap][time_info->tm_mon]; 00162 time_info->tm_mday = (timestamp / SECONDS_BY_DAY) + 1; 00163 00164 return true; 00165 }
Generated on Wed Jul 27 2022 09:32:04 by
