| 1 | /*	$NetBSD: clock_subr.c,v 1.27 2016/08/15 15:51:39 jakllsch Exp $	*/ | 
| 2 |  | 
| 3 | /* | 
| 4 |  * Copyright (c) 1988 University of Utah. | 
| 5 |  * Copyright (c) 1982, 1990, 1993 | 
| 6 |  *	The Regents of the University of California.  All rights reserved. | 
| 7 |  * | 
| 8 |  * This code is derived from software contributed to Berkeley by | 
| 9 |  * the Systems Programming Group of the University of Utah Computer | 
| 10 |  * Science Department. | 
| 11 |  * | 
| 12 |  * Redistribution and use in source and binary forms, with or without | 
| 13 |  * modification, are permitted provided that the following conditions | 
| 14 |  * are met: | 
| 15 |  * 1. Redistributions of source code must retain the above copyright | 
| 16 |  *    notice, this list of conditions and the following disclaimer. | 
| 17 |  * 2. Redistributions in binary form must reproduce the above copyright | 
| 18 |  *    notice, this list of conditions and the following disclaimer in the | 
| 19 |  *    documentation and/or other materials provided with the distribution. | 
| 20 |  * 3. Neither the name of the University nor the names of its contributors | 
| 21 |  *    may be used to endorse or promote products derived from this software | 
| 22 |  *    without specific prior written permission. | 
| 23 |  * | 
| 24 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
| 25 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
| 26 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
| 27 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
| 28 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
| 29 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
| 30 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
| 31 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
| 32 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
| 33 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
| 34 |  * SUCH DAMAGE. | 
| 35 |  * | 
| 36 |  * from: Utah $Hdr: clock.c 1.18 91/01/21$ | 
| 37 |  * | 
| 38 |  *	@(#)clock.c	8.2 (Berkeley) 1/12/94 | 
| 39 |  */ | 
| 40 |  | 
| 41 | /* | 
| 42 |  * Generic routines to convert between a POSIX date | 
| 43 |  * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec | 
| 44 |  * Derived from arch/hp300/hp300/clock.c | 
| 45 |  */ | 
| 46 |  | 
| 47 | #if HAVE_NBTOOL_CONFIG_H | 
| 48 | #include "nbtool_config.h" | 
| 49 | #endif /* HAVE_NBTOOL_CONFIG_H */ | 
| 50 |  | 
| 51 | #ifdef _KERNEL | 
| 52 | #include <sys/cdefs.h> | 
| 53 | __KERNEL_RCSID(0, "$NetBSD: clock_subr.c,v 1.27 2016/08/15 15:51:39 jakllsch Exp $" ); | 
| 54 |  | 
| 55 | #include <sys/param.h> | 
| 56 | #include <sys/systm.h> | 
| 57 | #include <sys/errno.h> | 
| 58 | #else /* ! _KERNEL */ | 
| 59 | #include <string.h> | 
| 60 | #include <time.h> | 
| 61 | #include <errno.h> | 
| 62 | #endif /* ! _KERNEL */ | 
| 63 |  | 
| 64 | #include "../sys/clock.h" | 
| 65 | #include <dev/clock_subr.h> | 
| 66 |  | 
| 67 | #define FEBRUARY	2 | 
| 68 |  | 
| 69 | /* for easier alignment: | 
| 70 |  * time from the epoch to 2001 (there were 8 leap years): */ | 
| 71 | #define	DAYSTO2001	(365*31+8) | 
| 72 |  | 
| 73 | /* 4 year intervals include 1 leap year */ | 
| 74 | #define	DAYS4YEARS	(365*4+1) | 
| 75 |  | 
| 76 | /* 100 year intervals include 24 leap years */ | 
| 77 | #define	DAYS100YEARS	(365*100+24) | 
| 78 |  | 
| 79 | /* 400 year intervals include 97 leap years */ | 
| 80 | #define	DAYS400YEARS	(365*400+97) | 
| 81 |  | 
| 82 | time_t | 
| 83 | clock_ymdhms_to_secs(struct clock_ymdhms *dt) | 
| 84 | { | 
| 85 | 	uint64_t secs, i, year, days; | 
| 86 |  | 
| 87 | 	year = dt->dt_year; | 
| 88 |  | 
| 89 | 	/* | 
| 90 | 	 * Compute days since start of time | 
| 91 | 	 * First from years, then from months. | 
| 92 | 	 */ | 
| 93 | 	if (year < POSIX_BASE_YEAR) | 
| 94 | 		return -1; | 
| 95 | 	days = 0; | 
| 96 | 	if (is_leap_year(year) && dt->dt_mon > FEBRUARY) | 
| 97 | 		days++; | 
| 98 |  | 
| 99 | 	if (year < 2001) { | 
| 100 | 		/* simple way for early years */ | 
| 101 | 		for (i = POSIX_BASE_YEAR; i < year; i++) | 
| 102 | 			days += days_per_year(i); | 
| 103 | 	} else { | 
| 104 | 		/* years are properly aligned */ | 
| 105 | 		days += DAYSTO2001; | 
| 106 | 		year -= 2001; | 
| 107 |  | 
| 108 | 		i = year / 400; | 
| 109 | 		days += i * DAYS400YEARS; | 
| 110 | 		year -= i * 400; | 
| 111 |  | 
| 112 | 		i = year / 100; | 
| 113 | 		days += i * DAYS100YEARS; | 
| 114 | 		year -= i * 100; | 
| 115 |  | 
| 116 | 		i = year / 4; | 
| 117 | 		days += i * DAYS4YEARS; | 
| 118 | 		year -= i * 4; | 
| 119 |  | 
| 120 | 		for (i = dt->dt_year-year; i < dt->dt_year; i++) | 
| 121 | 			days += days_per_year(i); | 
| 122 | 	} | 
| 123 |  | 
| 124 |  | 
| 125 | 	/* Months */ | 
| 126 | 	for (i = 1; i < dt->dt_mon; i++) | 
| 127 | 	  	days += days_in_month(i); | 
| 128 | 	days += (dt->dt_day - 1); | 
| 129 |  | 
| 130 | 	/* Add hours, minutes, seconds. */ | 
| 131 | 	secs = (((uint64_t)days | 
| 132 | 	    * 24 + dt->dt_hour) | 
| 133 | 	    * 60 + dt->dt_min) | 
| 134 | 	    * 60 + dt->dt_sec; | 
| 135 |  | 
| 136 | 	if ((time_t)secs < 0 || secs > __type_max(time_t)) | 
| 137 | 		return -1; | 
| 138 | 	return secs; | 
| 139 | } | 
| 140 |  | 
| 141 | int | 
| 142 | clock_secs_to_ymdhms(time_t secs, struct clock_ymdhms *dt) | 
| 143 | { | 
| 144 | 	int leap; | 
| 145 | 	uint64_t i; | 
| 146 | 	time_t days; | 
| 147 | 	time_t rsec;	/* remainder seconds */ | 
| 148 |  | 
| 149 | 	if (secs < 0) | 
| 150 | 		return EINVAL; | 
| 151 |  | 
| 152 | 	days = secs / SECS_PER_DAY; | 
| 153 | 	rsec = secs % SECS_PER_DAY; | 
| 154 |  | 
| 155 | 	/* Day of week (Note: 1/1/1970 was a Thursday) */ | 
| 156 | 	dt->dt_wday = (days + 4) % 7; | 
| 157 |  | 
| 158 | 	if (days >= DAYSTO2001) { | 
| 159 | 		days -= DAYSTO2001; | 
| 160 | 		dt->dt_year = 2001; | 
| 161 |  | 
| 162 | 		i = days / DAYS400YEARS; | 
| 163 | 		days -= i*DAYS400YEARS; | 
| 164 | 		dt->dt_year += i*400; | 
| 165 |  | 
| 166 | 		i = days / DAYS100YEARS; | 
| 167 | 		days -= i*DAYS100YEARS; | 
| 168 | 		dt->dt_year += i*100; | 
| 169 |  | 
| 170 | 		i = days / DAYS4YEARS; | 
| 171 | 		days -= i*DAYS4YEARS; | 
| 172 | 		dt->dt_year += i*4; | 
| 173 |  | 
| 174 | 		for (i = dt->dt_year; days >= days_per_year(i); i++) | 
| 175 | 			days -= days_per_year(i); | 
| 176 | 		dt->dt_year = i; | 
| 177 | 	} else { | 
| 178 | 		/* Subtract out whole years, counting them in i. */ | 
| 179 | 		for (i = POSIX_BASE_YEAR; days >= days_per_year(i); i++) | 
| 180 | 			days -= days_per_year(i); | 
| 181 | 		dt->dt_year = i; | 
| 182 | 	} | 
| 183 |  | 
| 184 | 	/* Subtract out whole months, counting them in i. */ | 
| 185 | 	for (leap = 0, i = 1; days >= days_in_month(i)+leap; i++) { | 
| 186 | 		days -= days_in_month(i)+leap; | 
| 187 | 		if (i == 1 && is_leap_year(dt->dt_year)) | 
| 188 | 			leap = 1; | 
| 189 | 		else | 
| 190 | 			leap = 0; | 
| 191 | 	} | 
| 192 | 	dt->dt_mon = i; | 
| 193 |  | 
| 194 | 	/* Days are what is left over (+1) from all that. */ | 
| 195 | 	dt->dt_day = days + 1; | 
| 196 |  | 
| 197 | 	/* Hours, minutes, seconds are easy */ | 
| 198 | 	dt->dt_hour = rsec / SECS_PER_HOUR; | 
| 199 | 	rsec = rsec % SECS_PER_HOUR; | 
| 200 | 	dt->dt_min  = rsec / SECS_PER_MINUTE; | 
| 201 | 	rsec = rsec % SECS_PER_MINUTE; | 
| 202 | 	dt->dt_sec  = rsec; | 
| 203 |  | 
| 204 | 	return 0; | 
| 205 | } | 
| 206 |  |