| 
									
										
										
										
											2017-04-27 20:18:03 +02:00
										 |  |  | // SPDX-License-Identifier: GPL-2.0
 | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include "dive.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * The date handling internally works in seconds since | 
					
						
							|  |  |  |  * Jan 1, 1900. That avoids negative numbers which avoids | 
					
						
							|  |  |  |  * some silly problems. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * But we then use the same base epoch base (Jan 1, 1970) | 
					
						
							|  |  |  |  * that POSIX uses, so that we can use the normal date | 
					
						
							|  |  |  |  * handling functions for getting current time etc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * There's 25567 dats from Jan 1, 1900 to Jan 1, 1970. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * NOTE! The SEC_PER_DAY is not so much because the | 
					
						
							|  |  |  |  * number is complicated, as to make sure we always | 
					
						
							|  |  |  |  * expand the type to "timestamp_t" in the arithmetic. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define SEC_PER_DAY  ((timestamp_t) 24*60*60)
 | 
					
						
							|  |  |  | #define EPOCH_OFFSET (25567 * SEC_PER_DAY)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Convert 64-bit timestamp to 'struct tm' in UTC. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * On 32-bit machines, only do 64-bit arithmetic for the seconds | 
					
						
							|  |  |  |  * part, after that we do everything in 'long'. 64-bit divides | 
					
						
							|  |  |  |  * are unnecessary once you're counting minutes (32-bit minutes: | 
					
						
							|  |  |  |  * 8000+ years). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void utc_mkdate(timestamp_t timestamp, struct tm *tm) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-03-09 18:59:26 -08:00
										 |  |  | 	static const unsigned int mdays[] = { | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 		31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2016-03-09 18:59:26 -08:00
										 |  |  | 	static const unsigned int mdays_leap[] = { | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 		31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	unsigned long val; | 
					
						
							|  |  |  | 	unsigned int leapyears; | 
					
						
							|  |  |  | 	int m; | 
					
						
							| 
									
										
										
										
											2016-03-09 18:59:26 -08:00
										 |  |  | 	const unsigned int *mp; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	memset(tm, 0, sizeof(*tm)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	// Midnight at Jan 1, 1970 means "no date"
 | 
					
						
							|  |  |  | 	if (!timestamp) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Convert to seconds since 1900 */ | 
					
						
							|  |  |  | 	timestamp += EPOCH_OFFSET; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* minutes since 1900 */ | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 	tm->tm_sec = timestamp % 60; | 
					
						
							|  |  |  | 	val = timestamp /= 60; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Do the simple stuff */ | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	tm->tm_min = val % 60; | 
					
						
							|  |  |  | 	val /= 60; | 
					
						
							|  |  |  | 	tm->tm_hour = val % 24; | 
					
						
							|  |  |  | 	val /= 24; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	/* Jan 1, 1900 was a Monday (tm_wday=1) */ | 
					
						
							|  |  |  | 	tm->tm_wday = (val + 1) % 7; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	 * Now we're in "days since Jan 1, 1900". To make things easier, | 
					
						
							|  |  |  | 	 * let's make it "days since Jan 1, 1904", since that's a leap-year. | 
					
						
							|  |  |  | 	 * 1900 itself was not. The following logic will get 1900-1903 | 
					
						
							|  |  |  | 	 * wrong. If you were diving back then, you're kind of screwed. | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	val -= 365*4; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* This only works up until 2099 (2100 isn't a leap-year) */ | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	leapyears = val / (365 * 4 + 1); | 
					
						
							|  |  |  | 	val %= (365 * 4 + 1); | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	tm->tm_year = 1904 + leapyears * 4; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Handle the leap-year itself */ | 
					
						
							|  |  |  | 	mp = mdays_leap; | 
					
						
							|  |  |  | 	if (val > 365) { | 
					
						
							|  |  |  | 		tm->tm_year++; | 
					
						
							|  |  |  | 		val -= 366; | 
					
						
							|  |  |  | 		tm->tm_year += val / 365; | 
					
						
							|  |  |  | 		val %= 365; | 
					
						
							|  |  |  | 		mp = mdays; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (m = 0; m < 12; m++) { | 
					
						
							|  |  |  | 		if (val < *mp) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		val -= *mp++; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	tm->tm_mday = val + 1; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 	tm->tm_mon = m; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | timestamp_t utc_mktime(struct tm *tm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	static const int mdays[] = { | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 		0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 	int year = tm->tm_year; | 
					
						
							|  |  |  | 	int month = tm->tm_mon; | 
					
						
							|  |  |  | 	int day = tm->tm_mday; | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	int days_since_1900; | 
					
						
							|  |  |  | 	timestamp_t when; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* First normalize relative to 1900 */ | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	if (year < 50) | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 		year += 100; | 
					
						
							| 
									
										
										
										
											2017-02-17 12:47:09 -08:00
										 |  |  | 	else if (year >= 1900) | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 		year -= 1900; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	if (year < 0 || year > 129) /* algo only works for 1900-2099 */ | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 	if (month < 0 || month > 11) /* array bounds */ | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 	if (month < 2 || (year && year % 4)) | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | 		day--; | 
					
						
							|  |  |  | 	if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0) | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* This works until 2099 */ | 
					
						
							|  |  |  | 	days_since_1900 = year * 365 + (year - 1) / 4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Note the 'day' fixup for non-leapyears above */ | 
					
						
							|  |  |  | 	days_since_1900 += mdays[month] + day; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Now add it all up, making sure to do this part in "timestamp_t" */ | 
					
						
							|  |  |  | 	when = days_since_1900 * SEC_PER_DAY; | 
					
						
							|  |  |  | 	when += tm->tm_hour * 60 * 60 + tm->tm_min * 60 + tm->tm_sec; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return when - EPOCH_OFFSET; | 
					
						
							| 
									
										
										
										
											2012-09-19 17:35:52 -07:00
										 |  |  | } |