xref: /freebsd/sys/kern/subr_fattime.c (revision 0fe60dc65572f4d04992c97c0e262af5c50e7408)
1b39be1b3SPoul-Henning Kamp /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni  *
4b39be1b3SPoul-Henning Kamp  * Copyright (c) 2006 Poul-Henning Kamp
5b39be1b3SPoul-Henning Kamp  * All rights reserved.
6b39be1b3SPoul-Henning Kamp  *
7b39be1b3SPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
8b39be1b3SPoul-Henning Kamp  * modification, are permitted provided that the following conditions
9b39be1b3SPoul-Henning Kamp  * are met:
10b39be1b3SPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
11b39be1b3SPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
12b39be1b3SPoul-Henning Kamp  * 2. Redistributions in binary form must reproduce the above copyright
13b39be1b3SPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer in the
14b39be1b3SPoul-Henning Kamp  *    documentation and/or other materials provided with the distribution.
15b39be1b3SPoul-Henning Kamp  *
16b39be1b3SPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17b39be1b3SPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b39be1b3SPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b39be1b3SPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20b39be1b3SPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21b39be1b3SPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22b39be1b3SPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23b39be1b3SPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24b39be1b3SPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25b39be1b3SPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26b39be1b3SPoul-Henning Kamp  * SUCH DAMAGE.
27b39be1b3SPoul-Henning Kamp  *
28b39be1b3SPoul-Henning Kamp  * Convert MS-DOS FAT format timestamps to and from unix timespecs
29b39be1b3SPoul-Henning Kamp  *
30b39be1b3SPoul-Henning Kamp  * FAT filestamps originally consisted of two 16 bit integers, encoded like
31b39be1b3SPoul-Henning Kamp  * this:
32b39be1b3SPoul-Henning Kamp  *
33b39be1b3SPoul-Henning Kamp  *	yyyyyyymmmmddddd (year - 1980, month, day)
34b39be1b3SPoul-Henning Kamp  *
35b39be1b3SPoul-Henning Kamp  *      hhhhhmmmmmmsssss (hour, minutes, seconds divided by two)
36b39be1b3SPoul-Henning Kamp  *
37b39be1b3SPoul-Henning Kamp  * Subsequently even Microsoft realized that files could be accessed in less
38b39be1b3SPoul-Henning Kamp  * than two seconds and a byte was added containing:
39b39be1b3SPoul-Henning Kamp  *
40b39be1b3SPoul-Henning Kamp  *      sfffffff	 (second mod two, 100ths of second)
41b39be1b3SPoul-Henning Kamp  *
42b39be1b3SPoul-Henning Kamp  * FAT timestamps are in the local timezone, with no indication of which
43b39be1b3SPoul-Henning Kamp  * timezone much less if daylight savings time applies.
44b39be1b3SPoul-Henning Kamp  *
45b39be1b3SPoul-Henning Kamp  * Later on again, in Windows NT, timestamps were defined relative to GMT.
46b39be1b3SPoul-Henning Kamp  *
47b39be1b3SPoul-Henning Kamp  * Purists will point out that UTC replaced GMT for such uses around
4861f26caeSWarner Losh  * half a century ago, already then.  Ironically "NT" was an abbreviation of
49b39be1b3SPoul-Henning Kamp  * "New Technology".  Anyway...
50b39be1b3SPoul-Henning Kamp  *
517ea93e91SPoul-Henning Kamp  * The 'utc' argument determines if the resulting FATTIME timestamp
52beb4f781SKonstantin Belousov  * should be on the UTC or local timezone calendar.
53b39be1b3SPoul-Henning Kamp  *
546afc7238SIan Lepore  * The conversion functions below cut time into four-year leap-year
55b39be1b3SPoul-Henning Kamp  * cycles rather than single years and uses table lookups inside those
56b39be1b3SPoul-Henning Kamp  * cycles to get the months and years sorted out.
57b39be1b3SPoul-Henning Kamp  *
58b39be1b3SPoul-Henning Kamp  * Obviously we cannot calculate the correct table index going from
59b39be1b3SPoul-Henning Kamp  * a posix seconds count to Y/M/D, but we can get pretty close by
60b39be1b3SPoul-Henning Kamp  * dividing the daycount by 32 (giving a too low index), and then
61b39be1b3SPoul-Henning Kamp  * adjusting upwards a couple of steps if necessary.
62b39be1b3SPoul-Henning Kamp  *
63b39be1b3SPoul-Henning Kamp  * FAT timestamps have 7 bits for the year and starts at 1980, so
64b39be1b3SPoul-Henning Kamp  * they can represent up to 2107 which means that the non-leap-year
65b39be1b3SPoul-Henning Kamp  * 2100 must be handled.
66b39be1b3SPoul-Henning Kamp  */
67b39be1b3SPoul-Henning Kamp 
68b39be1b3SPoul-Henning Kamp #include <sys/param.h>
69b39be1b3SPoul-Henning Kamp #include <sys/types.h>
70b39be1b3SPoul-Henning Kamp #include <sys/time.h>
71b39be1b3SPoul-Henning Kamp #include <sys/clock.h>
72b39be1b3SPoul-Henning Kamp 
737b8b613dSJosef 'Jeff' Sipek #ifdef TEST_DRIVER
747b8b613dSJosef 'Jeff' Sipek /* stub for testing */
757b8b613dSJosef 'Jeff' Sipek #define utc_offset() 0
767b8b613dSJosef 'Jeff' Sipek #endif
777b8b613dSJosef 'Jeff' Sipek 
78b39be1b3SPoul-Henning Kamp #define DAY	(24 * 60 * 60)	/* Length of day in seconds */
79b39be1b3SPoul-Henning Kamp #define YEAR	365		/* Length of normal year */
80b39be1b3SPoul-Henning Kamp #define LYC	(4 * YEAR + 1)	/* Length of 4 year leap-year cycle */
81b39be1b3SPoul-Henning Kamp #define T1980	(10 * 365 + 2)	/* Days from 1970 to 1980 */
829d1396c3SJosef 'Jeff' Sipek #define T2108	(138 * 365 + 33) /* Days from 1970 to 2108 */
83b39be1b3SPoul-Henning Kamp 
84b39be1b3SPoul-Henning Kamp /* End of month is N days from start of (normal) year */
85b39be1b3SPoul-Henning Kamp #define JAN	31
86b39be1b3SPoul-Henning Kamp #define FEB	(JAN + 28)
87b39be1b3SPoul-Henning Kamp #define MAR	(FEB + 31)
88b39be1b3SPoul-Henning Kamp #define APR	(MAR + 30)
89b39be1b3SPoul-Henning Kamp #define MAY	(APR + 31)
90b39be1b3SPoul-Henning Kamp #define JUN	(MAY + 30)
91b39be1b3SPoul-Henning Kamp #define JUL	(JUN + 31)
92b39be1b3SPoul-Henning Kamp #define AUG	(JUL + 31)
93b39be1b3SPoul-Henning Kamp #define SEP	(AUG + 30)
94b39be1b3SPoul-Henning Kamp #define OCT	(SEP + 31)
95b39be1b3SPoul-Henning Kamp #define NOV	(OCT + 30)
96b39be1b3SPoul-Henning Kamp #define DEC	(NOV + 31)
97b39be1b3SPoul-Henning Kamp 
98b39be1b3SPoul-Henning Kamp /* Table of months in a 4 year leap-year cycle */
99b39be1b3SPoul-Henning Kamp 
100b39be1b3SPoul-Henning Kamp #define ENC(y,m)	(((y) << 9) | ((m) << 5))
101b39be1b3SPoul-Henning Kamp 
102b39be1b3SPoul-Henning Kamp static const struct {
103b39be1b3SPoul-Henning Kamp 	uint16_t	days;	/* month start in days relative to cycle */
104b39be1b3SPoul-Henning Kamp 	uint16_t	coded;	/* encoded year + month information */
105b39be1b3SPoul-Henning Kamp } mtab[48] = {
106b39be1b3SPoul-Henning Kamp 	{   0 + 0 * YEAR,     ENC(0, 1)  },
107b39be1b3SPoul-Henning Kamp 
108b39be1b3SPoul-Henning Kamp 	{ JAN + 0 * YEAR,     ENC(0, 2)  }, { FEB + 0 * YEAR + 1, ENC(0, 3)  },
109b39be1b3SPoul-Henning Kamp 	{ MAR + 0 * YEAR + 1, ENC(0, 4)  }, { APR + 0 * YEAR + 1, ENC(0, 5)  },
110b39be1b3SPoul-Henning Kamp 	{ MAY + 0 * YEAR + 1, ENC(0, 6)  }, { JUN + 0 * YEAR + 1, ENC(0, 7)  },
111b39be1b3SPoul-Henning Kamp 	{ JUL + 0 * YEAR + 1, ENC(0, 8)  }, { AUG + 0 * YEAR + 1, ENC(0, 9)  },
112b39be1b3SPoul-Henning Kamp 	{ SEP + 0 * YEAR + 1, ENC(0, 10) }, { OCT + 0 * YEAR + 1, ENC(0, 11) },
113b39be1b3SPoul-Henning Kamp 	{ NOV + 0 * YEAR + 1, ENC(0, 12) }, { DEC + 0 * YEAR + 1, ENC(1, 1)  },
114b39be1b3SPoul-Henning Kamp 
115b39be1b3SPoul-Henning Kamp 	{ JAN + 1 * YEAR + 1, ENC(1, 2)  }, { FEB + 1 * YEAR + 1, ENC(1, 3)  },
116b39be1b3SPoul-Henning Kamp 	{ MAR + 1 * YEAR + 1, ENC(1, 4)  }, { APR + 1 * YEAR + 1, ENC(1, 5)  },
117b39be1b3SPoul-Henning Kamp 	{ MAY + 1 * YEAR + 1, ENC(1, 6)  }, { JUN + 1 * YEAR + 1, ENC(1, 7)  },
118b39be1b3SPoul-Henning Kamp 	{ JUL + 1 * YEAR + 1, ENC(1, 8)  }, { AUG + 1 * YEAR + 1, ENC(1, 9)  },
119b39be1b3SPoul-Henning Kamp 	{ SEP + 1 * YEAR + 1, ENC(1, 10) }, { OCT + 1 * YEAR + 1, ENC(1, 11) },
120b39be1b3SPoul-Henning Kamp 	{ NOV + 1 * YEAR + 1, ENC(1, 12) }, { DEC + 1 * YEAR + 1, ENC(2, 1)  },
121b39be1b3SPoul-Henning Kamp 
122b39be1b3SPoul-Henning Kamp 	{ JAN + 2 * YEAR + 1, ENC(2, 2)  }, { FEB + 2 * YEAR + 1, ENC(2, 3)  },
123b39be1b3SPoul-Henning Kamp 	{ MAR + 2 * YEAR + 1, ENC(2, 4)  }, { APR + 2 * YEAR + 1, ENC(2, 5)  },
124b39be1b3SPoul-Henning Kamp 	{ MAY + 2 * YEAR + 1, ENC(2, 6)  }, { JUN + 2 * YEAR + 1, ENC(2, 7)  },
125b39be1b3SPoul-Henning Kamp 	{ JUL + 2 * YEAR + 1, ENC(2, 8)  }, { AUG + 2 * YEAR + 1, ENC(2, 9)  },
126b39be1b3SPoul-Henning Kamp 	{ SEP + 2 * YEAR + 1, ENC(2, 10) }, { OCT + 2 * YEAR + 1, ENC(2, 11) },
127b39be1b3SPoul-Henning Kamp 	{ NOV + 2 * YEAR + 1, ENC(2, 12) }, { DEC + 2 * YEAR + 1, ENC(3, 1)  },
128b39be1b3SPoul-Henning Kamp 
129b39be1b3SPoul-Henning Kamp 	{ JAN + 3 * YEAR + 1, ENC(3, 2)  }, { FEB + 3 * YEAR + 1, ENC(3, 3)  },
130b39be1b3SPoul-Henning Kamp 	{ MAR + 3 * YEAR + 1, ENC(3, 4)  }, { APR + 3 * YEAR + 1, ENC(3, 5)  },
131b39be1b3SPoul-Henning Kamp 	{ MAY + 3 * YEAR + 1, ENC(3, 6)  }, { JUN + 3 * YEAR + 1, ENC(3, 7)  },
132b39be1b3SPoul-Henning Kamp 	{ JUL + 3 * YEAR + 1, ENC(3, 8)  }, { AUG + 3 * YEAR + 1, ENC(3, 9)  },
133b39be1b3SPoul-Henning Kamp 	{ SEP + 3 * YEAR + 1, ENC(3, 10) }, { OCT + 3 * YEAR + 1, ENC(3, 11) },
134b39be1b3SPoul-Henning Kamp 	{ NOV + 3 * YEAR + 1, ENC(3, 12) }
135b39be1b3SPoul-Henning Kamp };
136b39be1b3SPoul-Henning Kamp 
137b39be1b3SPoul-Henning Kamp void
138ce75945dSIan Lepore timespec2fattime(const struct timespec *tsp, int utc, uint16_t *ddp,
139ce75945dSIan Lepore     uint16_t *dtp, uint8_t *dhp)
140b39be1b3SPoul-Henning Kamp {
141b39be1b3SPoul-Henning Kamp 	time_t t1;
142b39be1b3SPoul-Henning Kamp 	unsigned t2, l, m;
143b39be1b3SPoul-Henning Kamp 
144b39be1b3SPoul-Henning Kamp 	t1 = tsp->tv_sec;
1457ea93e91SPoul-Henning Kamp 	if (!utc)
1467ea93e91SPoul-Henning Kamp 		t1 -= utc_offset();
147b39be1b3SPoul-Henning Kamp 
148b39be1b3SPoul-Henning Kamp 	if (dhp != NULL)
149b39be1b3SPoul-Henning Kamp 		*dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
150b39be1b3SPoul-Henning Kamp 	if (dtp != NULL) {
151b39be1b3SPoul-Henning Kamp 		*dtp = (t1 / 2) % 30;
152b39be1b3SPoul-Henning Kamp 		*dtp |= ((t1 / 60) % 60) << 5;
153b39be1b3SPoul-Henning Kamp 		*dtp |= ((t1 / 3600) % 24) << 11;
154b39be1b3SPoul-Henning Kamp 	}
155b39be1b3SPoul-Henning Kamp 	if (ddp != NULL) {
156b39be1b3SPoul-Henning Kamp 		t2 = t1 / DAY;
157b39be1b3SPoul-Henning Kamp 		if (t2 < T1980) {
158b39be1b3SPoul-Henning Kamp 			/* Impossible date, truncate to 1980-01-01 */
159b39be1b3SPoul-Henning Kamp 			*ddp = 0x0021;
160b39be1b3SPoul-Henning Kamp 		} else {
161b39be1b3SPoul-Henning Kamp 			t2 -= T1980;
162b39be1b3SPoul-Henning Kamp 
163*0fe60dc6SJosef 'Jeff' Sipek 			/* 2100 is not a leap year */
164b39be1b3SPoul-Henning Kamp 			if (t2 >= ((2100 - 1980) / 4 * LYC + FEB))
165b39be1b3SPoul-Henning Kamp 				t2++;
166b39be1b3SPoul-Henning Kamp 
167b39be1b3SPoul-Henning Kamp 			/* Account for full leapyear cycles */
168b39be1b3SPoul-Henning Kamp 			l = t2 / LYC;
169b39be1b3SPoul-Henning Kamp 			*ddp = (l * 4) << 9;
170b39be1b3SPoul-Henning Kamp 			t2 -= l * LYC;
171b39be1b3SPoul-Henning Kamp 
172b39be1b3SPoul-Henning Kamp 			/* Find approximate table entry */
173b39be1b3SPoul-Henning Kamp 			m = t2 / 32;
174b39be1b3SPoul-Henning Kamp 
175b39be1b3SPoul-Henning Kamp 			/* Find correct table entry */
176b39be1b3SPoul-Henning Kamp 			while (m < 47 && mtab[m + 1].days <= t2)
177b39be1b3SPoul-Henning Kamp 				m++;
178b39be1b3SPoul-Henning Kamp 
179b39be1b3SPoul-Henning Kamp 			/* Get year + month from the table */
180b39be1b3SPoul-Henning Kamp 			*ddp += mtab[m].coded;
181b39be1b3SPoul-Henning Kamp 
182b39be1b3SPoul-Henning Kamp 			/* And apply the day in the month */
183b39be1b3SPoul-Henning Kamp 			t2 -= mtab[m].days - 1;
184b39be1b3SPoul-Henning Kamp 			*ddp |= t2;
185b39be1b3SPoul-Henning Kamp 		}
186b39be1b3SPoul-Henning Kamp 	}
187b39be1b3SPoul-Henning Kamp }
188b39be1b3SPoul-Henning Kamp 
189b39be1b3SPoul-Henning Kamp /*
190b39be1b3SPoul-Henning Kamp  * Table indexed by the bottom two bits of year + four bits of the month
191b39be1b3SPoul-Henning Kamp  * from the FAT timestamp, returning number of days into 4 year long
192b39be1b3SPoul-Henning Kamp  * leap-year cycle
193b39be1b3SPoul-Henning Kamp  */
194b39be1b3SPoul-Henning Kamp 
195b39be1b3SPoul-Henning Kamp #define DCOD(m, y, l)	((m) + YEAR * (y) + (l))
196b39be1b3SPoul-Henning Kamp static const uint16_t daytab[64] = {
197b39be1b3SPoul-Henning Kamp 	0, 		 DCOD(  0, 0, 0), DCOD(JAN, 0, 0), DCOD(FEB, 0, 1),
198b39be1b3SPoul-Henning Kamp 	DCOD(MAR, 0, 1), DCOD(APR, 0, 1), DCOD(MAY, 0, 1), DCOD(JUN, 0, 1),
199b39be1b3SPoul-Henning Kamp 	DCOD(JUL, 0, 1), DCOD(AUG, 0, 1), DCOD(SEP, 0, 1), DCOD(OCT, 0, 1),
200b39be1b3SPoul-Henning Kamp 	DCOD(NOV, 0, 1), DCOD(DEC, 0, 1), 0,               0,
201b39be1b3SPoul-Henning Kamp 	0, 		 DCOD(  0, 1, 1), DCOD(JAN, 1, 1), DCOD(FEB, 1, 1),
202b39be1b3SPoul-Henning Kamp 	DCOD(MAR, 1, 1), DCOD(APR, 1, 1), DCOD(MAY, 1, 1), DCOD(JUN, 1, 1),
203b39be1b3SPoul-Henning Kamp 	DCOD(JUL, 1, 1), DCOD(AUG, 1, 1), DCOD(SEP, 1, 1), DCOD(OCT, 1, 1),
204b39be1b3SPoul-Henning Kamp 	DCOD(NOV, 1, 1), DCOD(DEC, 1, 1), 0,               0,
205b39be1b3SPoul-Henning Kamp 	0,		 DCOD(  0, 2, 1), DCOD(JAN, 2, 1), DCOD(FEB, 2, 1),
206b39be1b3SPoul-Henning Kamp 	DCOD(MAR, 2, 1), DCOD(APR, 2, 1), DCOD(MAY, 2, 1), DCOD(JUN, 2, 1),
207b39be1b3SPoul-Henning Kamp 	DCOD(JUL, 2, 1), DCOD(AUG, 2, 1), DCOD(SEP, 2, 1), DCOD(OCT, 2, 1),
208b39be1b3SPoul-Henning Kamp 	DCOD(NOV, 2, 1), DCOD(DEC, 2, 1), 0,               0,
209b39be1b3SPoul-Henning Kamp 	0,		 DCOD(  0, 3, 1), DCOD(JAN, 3, 1), DCOD(FEB, 3, 1),
210b39be1b3SPoul-Henning Kamp 	DCOD(MAR, 3, 1), DCOD(APR, 3, 1), DCOD(MAY, 3, 1), DCOD(JUN, 3, 1),
211b39be1b3SPoul-Henning Kamp 	DCOD(JUL, 3, 1), DCOD(AUG, 3, 1), DCOD(SEP, 3, 1), DCOD(OCT, 3, 1),
212b39be1b3SPoul-Henning Kamp 	DCOD(NOV, 3, 1), DCOD(DEC, 3, 1), 0,               0
213b39be1b3SPoul-Henning Kamp };
214b39be1b3SPoul-Henning Kamp 
215b39be1b3SPoul-Henning Kamp void
216ce75945dSIan Lepore fattime2timespec(unsigned dd, unsigned dt, unsigned dh, int utc,
217ce75945dSIan Lepore     struct timespec *tsp)
218b39be1b3SPoul-Henning Kamp {
219b39be1b3SPoul-Henning Kamp 	unsigned day;
220b39be1b3SPoul-Henning Kamp 
221b39be1b3SPoul-Henning Kamp 	/* Unpack time fields */
222b39be1b3SPoul-Henning Kamp 	tsp->tv_sec = (dt & 0x1f) << 1;
223b39be1b3SPoul-Henning Kamp 	tsp->tv_sec += ((dt & 0x7e0) >> 5) * 60;
224b39be1b3SPoul-Henning Kamp 	tsp->tv_sec += ((dt & 0xf800) >> 11) * 3600;
225b39be1b3SPoul-Henning Kamp 	tsp->tv_sec += dh / 100;
226b39be1b3SPoul-Henning Kamp 	tsp->tv_nsec = (dh % 100) * 10000000;
227b39be1b3SPoul-Henning Kamp 
228b39be1b3SPoul-Henning Kamp 	/* Day of month */
229b39be1b3SPoul-Henning Kamp 	day = (dd & 0x1f) - 1;
230b39be1b3SPoul-Henning Kamp 
231b39be1b3SPoul-Henning Kamp 	/* Full leap-year cycles */
232b39be1b3SPoul-Henning Kamp 	day += LYC * ((dd >> 11) & 0x1f);
233b39be1b3SPoul-Henning Kamp 
234b39be1b3SPoul-Henning Kamp 	/* Month offset from leap-year cycle */
235b39be1b3SPoul-Henning Kamp 	day += daytab[(dd >> 5) & 0x3f];
236b39be1b3SPoul-Henning Kamp 
237*0fe60dc6SJosef 'Jeff' Sipek 	/* 2100 is not a leap year */
238b39be1b3SPoul-Henning Kamp 	if (day >= ((2100 - 1980) / 4 * LYC + FEB))
239b39be1b3SPoul-Henning Kamp 		day--;
240b39be1b3SPoul-Henning Kamp 
241b39be1b3SPoul-Henning Kamp 	/* Align with time_t epoch */
242b39be1b3SPoul-Henning Kamp 	day += T1980;
243b39be1b3SPoul-Henning Kamp 
244*0fe60dc6SJosef 'Jeff' Sipek 	tsp->tv_sec += (time_t) DAY * day;
2457ea93e91SPoul-Henning Kamp 	if (!utc)
2467ea93e91SPoul-Henning Kamp 		tsp->tv_sec += utc_offset();
247b39be1b3SPoul-Henning Kamp }
248b39be1b3SPoul-Henning Kamp 
249b39be1b3SPoul-Henning Kamp #ifdef TEST_DRIVER
250b39be1b3SPoul-Henning Kamp 
251b39be1b3SPoul-Henning Kamp #include <stdio.h>
252b39be1b3SPoul-Henning Kamp #include <unistd.h>
253b39be1b3SPoul-Henning Kamp #include <stdlib.h>
254b39be1b3SPoul-Henning Kamp 
255b39be1b3SPoul-Henning Kamp int
256b39be1b3SPoul-Henning Kamp main(int argc __unused, char **argv __unused)
257b39be1b3SPoul-Henning Kamp {
258b39be1b3SPoul-Henning Kamp 	int i;
259b39be1b3SPoul-Henning Kamp 	struct timespec ts;
260b39be1b3SPoul-Henning Kamp 	struct tm tm;
261b39be1b3SPoul-Henning Kamp 	double a;
26260ae52f7SEd Schouten 	uint16_t d, t;
26360ae52f7SEd Schouten 	uint8_t p;
264b39be1b3SPoul-Henning Kamp 	char buf[100];
265b39be1b3SPoul-Henning Kamp 
266b39be1b3SPoul-Henning Kamp 	for (i = 0; i < 10000; i++) {
267b39be1b3SPoul-Henning Kamp 		do {
2689d1396c3SJosef 'Jeff' Sipek 			/*
2699d1396c3SJosef 'Jeff' Sipek 			 * 32-bits gets us to 2106-02-07 06:28:15, but we
2709d1396c3SJosef 'Jeff' Sipek 			 * need to get to the end of 2107.  So, we generate
2719d1396c3SJosef 'Jeff' Sipek 			 * a 36-bit second count to get us way past 2106.
2729d1396c3SJosef 'Jeff' Sipek 			 */
2739d1396c3SJosef 'Jeff' Sipek 			ts.tv_sec = ((time_t) arc4random() << 4) ^ arc4random();
2749d1396c3SJosef 'Jeff' Sipek 		} while ((ts.tv_sec < T1980 * 86400) || (ts.tv_sec >= T2108 * 86400ull));
2759d1396c3SJosef 'Jeff' Sipek 
276b39be1b3SPoul-Henning Kamp 		ts.tv_nsec = random() % 1000000000;
277b39be1b3SPoul-Henning Kamp 
2789d1396c3SJosef 'Jeff' Sipek 		printf("%10jd.%03ld -- ", (intmax_t) ts.tv_sec, ts.tv_nsec / 1000000);
279b39be1b3SPoul-Henning Kamp 
280b39be1b3SPoul-Henning Kamp 		gmtime_r(&ts.tv_sec, &tm);
281b39be1b3SPoul-Henning Kamp 		strftime(buf, sizeof buf, "%Y %m %d %H %M %S", &tm);
282b39be1b3SPoul-Henning Kamp 		printf("%s -- ", buf);
283b39be1b3SPoul-Henning Kamp 
284b39be1b3SPoul-Henning Kamp 		a = ts.tv_sec + ts.tv_nsec * 1e-9;
285b39be1b3SPoul-Henning Kamp 		d = t = p = 0;
2867b8b613dSJosef 'Jeff' Sipek 		timespec2fattime(&ts, 1, &d, &t, &p);
287b39be1b3SPoul-Henning Kamp 		printf("%04x %04x %02x -- ", d, t, p);
288b39be1b3SPoul-Henning Kamp 		printf("%3d %02d %02d %02d %02d %02d -- ",
289b39be1b3SPoul-Henning Kamp 		    ((d >> 9)  & 0x7f) + 1980,
290b39be1b3SPoul-Henning Kamp 		    (d >> 5)  & 0x0f,
291b39be1b3SPoul-Henning Kamp 		    (d >> 0)  & 0x1f,
292b39be1b3SPoul-Henning Kamp 		    (t >> 11) & 0x1f,
293b39be1b3SPoul-Henning Kamp 		    (t >> 5)  & 0x3f,
294b39be1b3SPoul-Henning Kamp 		    ((t >> 0)  & 0x1f) * 2);
295b39be1b3SPoul-Henning Kamp 
296b39be1b3SPoul-Henning Kamp 		ts.tv_sec = ts.tv_nsec = 0;
2977b8b613dSJosef 'Jeff' Sipek 		fattime2timespec(d, t, p, 1, &ts);
2989d1396c3SJosef 'Jeff' Sipek 		printf("%10jd.%03ld == ", (intmax_t) ts.tv_sec, ts.tv_nsec / 1000000);
299b39be1b3SPoul-Henning Kamp 		gmtime_r(&ts.tv_sec, &tm);
300b39be1b3SPoul-Henning Kamp 		strftime(buf, sizeof buf, "%Y %m %d %H %M %S", &tm);
301b39be1b3SPoul-Henning Kamp 		printf("%s -- ", buf);
302b39be1b3SPoul-Henning Kamp 		a -= ts.tv_sec + ts.tv_nsec * 1e-9;
303b39be1b3SPoul-Henning Kamp 		printf("%.3f", a);
304b39be1b3SPoul-Henning Kamp 		printf("\n");
305b39be1b3SPoul-Henning Kamp 	}
306b39be1b3SPoul-Henning Kamp 	return (0);
307b39be1b3SPoul-Henning Kamp }
308b39be1b3SPoul-Henning Kamp 
309b39be1b3SPoul-Henning Kamp #endif /* TEST_DRIVER */
310