xref: /netbsd-src/sys/dev/clock_subr.c (revision 16287fab7ed18b37c66c1ec9a7ded2325d271bc3)
1*16287fabSjakllsch /*	$NetBSD: clock_subr.c,v 1.27 2016/08/15 15:51:39 jakllsch Exp $	*/
2aad01611Sagc 
3aad01611Sagc /*
49b6bd2d9Srmind  * Copyright (c) 1988 University of Utah.
5aad01611Sagc  * Copyright (c) 1982, 1990, 1993
6aad01611Sagc  *	The Regents of the University of California.  All rights reserved.
7aad01611Sagc  *
8aad01611Sagc  * This code is derived from software contributed to Berkeley by
9aad01611Sagc  * the Systems Programming Group of the University of Utah Computer
10aad01611Sagc  * Science Department.
11aad01611Sagc  *
12aad01611Sagc  * Redistribution and use in source and binary forms, with or without
13aad01611Sagc  * modification, are permitted provided that the following conditions
14aad01611Sagc  * are met:
15aad01611Sagc  * 1. Redistributions of source code must retain the above copyright
16aad01611Sagc  *    notice, this list of conditions and the following disclaimer.
17aad01611Sagc  * 2. Redistributions in binary form must reproduce the above copyright
18aad01611Sagc  *    notice, this list of conditions and the following disclaimer in the
19aad01611Sagc  *    documentation and/or other materials provided with the distribution.
20aad01611Sagc  * 3. Neither the name of the University nor the names of its contributors
21aad01611Sagc  *    may be used to endorse or promote products derived from this software
22aad01611Sagc  *    without specific prior written permission.
23aad01611Sagc  *
24aad01611Sagc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25aad01611Sagc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26aad01611Sagc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27aad01611Sagc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28aad01611Sagc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29aad01611Sagc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30aad01611Sagc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31aad01611Sagc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32aad01611Sagc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33aad01611Sagc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34aad01611Sagc  * SUCH DAMAGE.
35aad01611Sagc  *
36aad01611Sagc  * from: Utah $Hdr: clock.c 1.18 91/01/21$
37aad01611Sagc  *
38aad01611Sagc  *	@(#)clock.c	8.2 (Berkeley) 1/12/94
39aad01611Sagc  */
40d8b59a12Sgwr 
41d8b59a12Sgwr /*
42d8b59a12Sgwr  * Generic routines to convert between a POSIX date
43d8b59a12Sgwr  * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec
44d8b59a12Sgwr  * Derived from arch/hp300/hp300/clock.c
45d8b59a12Sgwr  */
46d8b59a12Sgwr 
47d2f4f342Sapb #if HAVE_NBTOOL_CONFIG_H
48d2f4f342Sapb #include "nbtool_config.h"
49d2f4f342Sapb #endif /* HAVE_NBTOOL_CONFIG_H */
50d2f4f342Sapb 
510edf2e8fSmartin #ifdef _KERNEL
522bbe2de6Slukem #include <sys/cdefs.h>
53*16287fabSjakllsch __KERNEL_RCSID(0, "$NetBSD: clock_subr.c,v 1.27 2016/08/15 15:51:39 jakllsch Exp $");
542bbe2de6Slukem 
55303f5332Sragge #include <sys/param.h>
56d8b59a12Sgwr #include <sys/systm.h>
575621a8b9Smartin #include <sys/errno.h>
58d2f4f342Sapb #else /* ! _KERNEL */
590edf2e8fSmartin #include <string.h>
600edf2e8fSmartin #include <time.h>
615621a8b9Smartin #include <errno.h>
62d2f4f342Sapb #endif /* ! _KERNEL */
63d8b59a12Sgwr 
647e52766bSjoerg #include "../sys/clock.h"
65d8b59a12Sgwr #include <dev/clock_subr.h>
66d8b59a12Sgwr 
67d8b59a12Sgwr #define FEBRUARY	2
68d8b59a12Sgwr 
6997f844c0Smartin /* for easier alignment:
70*16287fabSjakllsch  * time from the epoch to 2001 (there were 8 leap years): */
71*16287fabSjakllsch #define	DAYSTO2001	(365*31+8)
7297f844c0Smartin 
7397f844c0Smartin /* 4 year intervals include 1 leap year */
7497f844c0Smartin #define	DAYS4YEARS	(365*4+1)
7597f844c0Smartin 
7697f844c0Smartin /* 100 year intervals include 24 leap years */
7797f844c0Smartin #define	DAYS100YEARS	(365*100+24)
7897f844c0Smartin 
7997f844c0Smartin /* 400 year intervals include 97 leap years */
8097f844c0Smartin #define	DAYS400YEARS	(365*400+97)
8197f844c0Smartin 
82d8b59a12Sgwr time_t
clock_ymdhms_to_secs(struct clock_ymdhms * dt)8388e7f0dcSperry clock_ymdhms_to_secs(struct clock_ymdhms *dt)
84d8b59a12Sgwr {
855621a8b9Smartin 	uint64_t secs, i, year, days;
86d8b59a12Sgwr 
87d8b59a12Sgwr 	year = dt->dt_year;
88d8b59a12Sgwr 
89d8b59a12Sgwr 	/*
90d8b59a12Sgwr 	 * Compute days since start of time
91d8b59a12Sgwr 	 * First from years, then from months.
92d8b59a12Sgwr 	 */
936621c5bdStsutsui 	if (year < POSIX_BASE_YEAR)
946621c5bdStsutsui 		return -1;
95d8b59a12Sgwr 	days = 0;
96a95736d4Schristos 	if (is_leap_year(year) && dt->dt_mon > FEBRUARY)
97d8b59a12Sgwr 		days++;
98d8b59a12Sgwr 
99*16287fabSjakllsch 	if (year < 2001) {
10097f844c0Smartin 		/* simple way for early years */
10197f844c0Smartin 		for (i = POSIX_BASE_YEAR; i < year; i++)
102a95736d4Schristos 			days += days_per_year(i);
10397f844c0Smartin 	} else {
10497f844c0Smartin 		/* years are properly aligned */
105*16287fabSjakllsch 		days += DAYSTO2001;
106*16287fabSjakllsch 		year -= 2001;
10797f844c0Smartin 
10897f844c0Smartin 		i = year / 400;
10997f844c0Smartin 		days += i * DAYS400YEARS;
11097f844c0Smartin 		year -= i * 400;
11197f844c0Smartin 
11297f844c0Smartin 		i = year / 100;
11397f844c0Smartin 		days += i * DAYS100YEARS;
11497f844c0Smartin 		year -= i * 100;
11597f844c0Smartin 
11697f844c0Smartin 		i = year / 4;
11797f844c0Smartin 		days += i * DAYS4YEARS;
11897f844c0Smartin 		year -= i * 4;
11997f844c0Smartin 
12097f844c0Smartin 		for (i = dt->dt_year-year; i < dt->dt_year; i++)
12153bfb03cSchristos 			days += days_per_year(i);
12297f844c0Smartin 	}
12397f844c0Smartin 
12497f844c0Smartin 
125d8b59a12Sgwr 	/* Months */
126d8b59a12Sgwr 	for (i = 1; i < dt->dt_mon; i++)
127d8b59a12Sgwr 	  	days += days_in_month(i);
128d8b59a12Sgwr 	days += (dt->dt_day - 1);
129d8b59a12Sgwr 
130d8b59a12Sgwr 	/* Add hours, minutes, seconds. */
1318f7f78f2Sbjh21 	secs = (((uint64_t)days
132d8b59a12Sgwr 	    * 24 + dt->dt_hour)
133d8b59a12Sgwr 	    * 60 + dt->dt_min)
134d8b59a12Sgwr 	    * 60 + dt->dt_sec;
135d8b59a12Sgwr 
1365b258ea3Sapb 	if ((time_t)secs < 0 || secs > __type_max(time_t))
1376621c5bdStsutsui 		return -1;
1386621c5bdStsutsui 	return secs;
139d8b59a12Sgwr }
140d8b59a12Sgwr 
1415621a8b9Smartin int
clock_secs_to_ymdhms(time_t secs,struct clock_ymdhms * dt)14288e7f0dcSperry clock_secs_to_ymdhms(time_t secs, struct clock_ymdhms *dt)
143d8b59a12Sgwr {
1445621a8b9Smartin 	int leap;
1455621a8b9Smartin 	uint64_t i;
146bf749939Stsutsui 	time_t days;
147bf749939Stsutsui 	time_t rsec;	/* remainder seconds */
148d8b59a12Sgwr 
1495621a8b9Smartin 	if (secs < 0)
1505621a8b9Smartin 		return EINVAL;
1515621a8b9Smartin 
152a95736d4Schristos 	days = secs / SECS_PER_DAY;
153a95736d4Schristos 	rsec = secs % SECS_PER_DAY;
154d8b59a12Sgwr 
155d8b59a12Sgwr 	/* Day of week (Note: 1/1/1970 was a Thursday) */
156d8b59a12Sgwr 	dt->dt_wday = (days + 4) % 7;
157d8b59a12Sgwr 
158*16287fabSjakllsch 	if (days >= DAYSTO2001) {
159*16287fabSjakllsch 		days -= DAYSTO2001;
160*16287fabSjakllsch 		dt->dt_year = 2001;
16197f844c0Smartin 
16297f844c0Smartin 		i = days / DAYS400YEARS;
16397f844c0Smartin 		days -= i*DAYS400YEARS;
16497f844c0Smartin 		dt->dt_year += i*400;
16597f844c0Smartin 
16697f844c0Smartin 		i = days / DAYS100YEARS;
16797f844c0Smartin 		days -= i*DAYS100YEARS;
16897f844c0Smartin 		dt->dt_year += i*100;
16997f844c0Smartin 
17097f844c0Smartin 		i = days / DAYS4YEARS;
17197f844c0Smartin 		days -= i*DAYS4YEARS;
17297f844c0Smartin 		dt->dt_year += i*4;
17397f844c0Smartin 
174a95736d4Schristos 		for (i = dt->dt_year; days >= days_per_year(i); i++)
175a95736d4Schristos 			days -= days_per_year(i);
17697f844c0Smartin 		dt->dt_year = i;
17797f844c0Smartin 	} else {
178d8b59a12Sgwr 		/* Subtract out whole years, counting them in i. */
179a95736d4Schristos 		for (i = POSIX_BASE_YEAR; days >= days_per_year(i); i++)
180a95736d4Schristos 			days -= days_per_year(i);
181d8b59a12Sgwr 		dt->dt_year = i;
18297f844c0Smartin 	}
183d8b59a12Sgwr 
184d8b59a12Sgwr 	/* Subtract out whole months, counting them in i. */
18597f844c0Smartin 	for (leap = 0, i = 1; days >= days_in_month(i)+leap; i++) {
18697f844c0Smartin 		days -= days_in_month(i)+leap;
187a95736d4Schristos 		if (i == 1 && is_leap_year(dt->dt_year))
18897f844c0Smartin 			leap = 1;
18997f844c0Smartin 		else
19097f844c0Smartin 			leap = 0;
19197f844c0Smartin 	}
192d8b59a12Sgwr 	dt->dt_mon = i;
193d8b59a12Sgwr 
194d8b59a12Sgwr 	/* Days are what is left over (+1) from all that. */
195d8b59a12Sgwr 	dt->dt_day = days + 1;
196d8b59a12Sgwr 
197d8b59a12Sgwr 	/* Hours, minutes, seconds are easy */
1988694ecf4Schristos 	dt->dt_hour = rsec / SECS_PER_HOUR;
1998694ecf4Schristos 	rsec = rsec % SECS_PER_HOUR;
2008694ecf4Schristos 	dt->dt_min  = rsec / SECS_PER_MINUTE;
2018694ecf4Schristos 	rsec = rsec % SECS_PER_MINUTE;
202d8b59a12Sgwr 	dt->dt_sec  = rsec;
2035621a8b9Smartin 
2045621a8b9Smartin 	return 0;
205d8b59a12Sgwr }
206