summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2017-01-25 19:34:13 -0500
committerMichael Sevakis <jethead71@rockbox.org>2017-02-03 14:40:55 -0500
commit248bff5eb8c56dc3c8b4ff2e47c05c7c09cd4849 (patch)
tree3956b2c20460816e25358985da3a3e45340ff8bb
parent4d4b0c5a07c907efda20771fa6baca6f1c91802e (diff)
downloadrockbox-248bff5eb8c56dc3c8b4ff2e47c05c7c09cd4849.tar.gz
rockbox-248bff5eb8c56dc3c8b4ff2e47c05c7c09cd4849.zip
Improve code for gmtime_r()
Unlike the current code, it does no looping to count days or do table lookups, which means running time doesn't increase with year or month. A good thing if it's call a lot, especially if the algorithm were asked to compute dates centuries or more from the epoch start. As a bonus, handles negative time values. Change-Id: I198a23daf621e40623e6b44dacf2387078b4db9c
-rw-r--r--firmware/libc/gmtime.c154
1 files changed, 86 insertions, 68 deletions
diff --git a/firmware/libc/gmtime.c b/firmware/libc/gmtime.c
index c5deb593cd..b01050904d 100644
--- a/firmware/libc/gmtime.c
+++ b/firmware/libc/gmtime.c
@@ -7,6 +7,8 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2017 by Michael Sevakis
11 *
10 * Copyright (C) 2012 by Bertrik Sikken 12 * Copyright (C) 2012 by Bertrik Sikken
11 * 13 *
12 * Based on code from: rtc_as3514.c 14 * Based on code from: rtc_as3514.c
@@ -21,96 +23,112 @@
21 * KIND, either express or implied. 23 * KIND, either express or implied.
22 * 24 *
23 ****************************************************************************/ 25 ****************************************************************************/
24#include <stdbool.h>
25#include <stdint.h>
26#include "time.h" 26#include "time.h"
27 27
28#define MINUTE_SECONDS 60 28/* Constants that mark Thursday, 1 January 1970 */
29#define HOUR_SECONDS 3600 29#define UNIX_EPOCH_DAY_NUM 134774
30#define DAY_SECONDS 86400 30#define UNIX_EPOCH_YEAR (1601 - 1900)
31#define WEEK_SECONDS 604800
32#define YEAR_SECONDS 31536000
33#define LEAP_YEAR_SECONDS 31622400
34 31
35/* Days in each month */ 32/* Last day number it can do */
36static uint8_t days_in_month[] = 33#define MAX_DAY_NUM 551879
37 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
38 34
39static inline bool is_leapyear(int year) 35/* d is days since epoch start, Monday, 1 January 1601 (d = 0)
36 *
37 * That date is the beginning of a full 400 year cycle and so simplifies the
38 * calculations a bit, not requiring offsets before divisions to shift the
39 * leap year cycle.
40 *
41 * It can handle dates up through Sunday, 31 December 3111 (d = 551879).
42 *
43 * time_t can't get near the limits anyway for now but algorithm can be
44 * altered slightly to increase range if even needed.
45 */
46static void get_tmdate(unsigned int d, struct tm *tm)
40{ 47{
41 return (((year%4)==0) && (((year%100)!=0) || ((year%400)==0))); 48 static const unsigned short mon_yday[13] =
42} 49 {
50 /* year day of 1st of month (non-leap)
51 +31 +28 +31 +30 +31 +30 +31 +31 +30 +31 +30 +31 +31 */
52 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
53 };
43 54
44struct tm *gmtime(const time_t *timep) 55 unsigned int x0, x1, x2, x3; /* scratch variables */
45{
46 static struct tm time;
47 return gmtime_r(timep, &time);
48}
49 56
50struct tm *gmtime_r(const time_t *timep, struct tm *tm) 57 /* calculate year from day number */
51{ 58 x0 = d / 1460;
52 time_t seconds = *timep; 59 x1 = x0 / 25;
53 int year, i, mday, hour, min; 60 x2 = x1 / 4;
54 61
55 /* weekday */ 62 unsigned int y = (d - x0 + x1 - x2) / 365;
56 tm->tm_wday = (seconds / DAY_SECONDS + 4) % 7;
57 63
58 /* Year */ 64 tm->tm_year = y + UNIX_EPOCH_YEAR; /* year - 1900 */
59 year = 1970;
60 while (seconds >= LEAP_YEAR_SECONDS)
61 {
62 if (is_leapyear(year)){
63 seconds -= LEAP_YEAR_SECONDS;
64 } else {
65 seconds -= YEAR_SECONDS;
66 }
67 65
68 year++; 66 /* calculate year day from year number and day number */
69 } 67 x0 = y / 4;
68 x1 = x0 / 25;
69 x2 = x1 / 4;
70
71 unsigned int yday = d - x0 + x1 - x2 - y * 365;
70 72
71 if (is_leapyear(year)) { 73 tm->tm_yday = x3 = yday; /* 0..364/365 */
72 days_in_month[1] = 29; 74
73 } else { 75 /* check if leap year; adjust February->March transition if so rather
74 days_in_month[1] = 28; 76 than keeping a leap year version of mon_yday[] */
75 if(seconds>YEAR_SECONDS){ 77 if (y - x0 * 4 == 3 && (x0 - x1 * 25 != 24 || x1 - x2 * 4 == 3)) {
76 year++; 78 /* retard month lookup to make year day 59 into 29 Feb, both to make
77 seconds -= YEAR_SECONDS; 79 year day 60 into 01 March, lagging one day for remainder of year */
80 if (x3 >= mon_yday[2] && --x3 >= mon_yday[2]) {
81 yday--;
78 } 82 }
79 } 83 }
80 tm->tm_year = year - 1900;
81 84
82 /* Month */ 85 /* stab approximately at current month based on year day; advance if
83 for (i = 0; i < 12; i++) 86 it fell short (never initially more than 1 short). */
84 { 87 x0 = x3 / 32;
85 if (seconds < days_in_month[i]*DAY_SECONDS){ 88 if (mon_yday[x0 + 1] <= x3) {
86 tm->tm_mon = i; 89 x0++;
87 break; 90 }
88 } 91
92 tm->tm_mon = x0; /* 0..11 */
93 tm->tm_mday = yday - mon_yday[x0] + 1; /* 1..31 */
94 tm->tm_wday = (d + 1) % 7; /* 0..6 */
95}
96
97struct tm * gmtime_r(const time_t *timep, struct tm *tm)
98{
99 time_t t = *timep;
100
101 int d = t / 86400; /* day number (-24856..24855) */
102 int s = t - (time_t)d * 86400; /* second # of day (0..86399) */
89 103
90 seconds -= days_in_month[i]*DAY_SECONDS; 104 if (s < 0) {
105 /* round towards 0 -> floored division */
106 d--;
107 s += 86400;
91 } 108 }
92 109
93 /* Month Day */ 110 unsigned int x;
94 mday = seconds/DAY_SECONDS;
95 seconds -= mday*DAY_SECONDS;
96 tm->tm_mday = mday + 1; /* 1 ... 31 */
97 111
98 /* Hour */ 112 x = s / 3600;
99 hour = seconds/HOUR_SECONDS; 113 tm->tm_hour = x; /* 0..23 */
100 seconds -= hour*HOUR_SECONDS;
101 tm->tm_hour = hour;
102 114
103 /* Minute */ 115 s -= x * 3600;
104 min = seconds/MINUTE_SECONDS; 116 x = s / 60;
105 seconds -= min*MINUTE_SECONDS; 117 tm->tm_min = x; /* 0..59 */
106 tm->tm_min = min;
107 118
108 /* Second */ 119 s -= x * 60;
109 tm->tm_sec = seconds; 120 tm->tm_sec = s; /* 0..59 */
110 121
111 tm->tm_yday = 0; /* Not implemented for now */ 122 /* not implemented right now */
112 tm->tm_isdst = -1; /* Not implemented for now */ 123 tm->tm_isdst = -1;
124
125 get_tmdate(d + UNIX_EPOCH_DAY_NUM, tm);
113 126
114 return tm; 127 return tm;
115} 128}
116 129
130struct tm * gmtime(const time_t *timep)
131{
132 static struct tm time;
133 return gmtime_r(timep, &time);
134}