1 /*
2  * Date and time functions
3  *
4  * Copyright (C) 2011-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <byte_stream.h>
24 #include <types.h>
25 
26 #include "pyevt_datetime.h"
27 #include "pyevt_python.h"
28 
29 #include <datetime.h>
30 
31 /* Creates a new datetime object from date and time elements
32  * Returns a Python object if successful or NULL on error
33  */
pyevt_datetime_new_from_time_elements(uint16_t year,uint64_t number_of_days,uint8_t hours,uint8_t minutes,uint8_t seconds,uint8_t micro_seconds)34 PyObject *pyevt_datetime_new_from_time_elements(
35            uint16_t year,
36            uint64_t number_of_days,
37            uint8_t hours,
38            uint8_t minutes,
39            uint8_t seconds,
40            uint8_t micro_seconds )
41 {
42 	PyObject *datetime_object = NULL;
43 	static char *function     = "pyevt_datetime_new_from_time_elements";
44 	uint32_t days_in_century  = 0;
45 	uint16_t days_in_year     = 0;
46 	uint8_t day_of_month      = 0;
47 	uint8_t days_in_month     = 0;
48 	uint8_t month             = 0;
49 
50 	while( number_of_days > 0 )
51 	{
52 		if( ( year % 400 ) == 0 )
53 		{
54 			days_in_century = 36525;
55 		}
56 		else
57 		{
58 			days_in_century = 36524;
59 		}
60 		if( number_of_days <= days_in_century )
61 		{
62 			break;
63 		}
64 		number_of_days -= days_in_century;
65 
66 		year += 100;
67 	}
68 	while( number_of_days > 0 )
69 	{
70 		/* Check for a leap year
71 		 * The year is ( ( dividable by 4 ) and ( not dividable by 100 ) ) or ( dividable by 400 )
72 		 */
73 		if( ( ( ( year % 4 ) == 0 )
74 		  &&  ( ( year % 100 ) != 0 ) )
75 		 || ( ( year % 400 ) == 0 ) )
76 		{
77 			days_in_year = 366;
78 		}
79 		else
80 		{
81 			days_in_year = 365;
82 		}
83 		if( number_of_days <= days_in_year )
84 		{
85 			break;
86 		}
87 		number_of_days -= days_in_year;
88 
89 		year += 1;
90 	}
91 	/* Determine the month correct the value to days within the month
92 	 */
93 	month = 1;
94 
95 	while( number_of_days > 0 )
96 	{
97 		/* February (2)
98 		 */
99 		if( month == 2 )
100 		{
101 			if( ( ( ( year % 4 ) == 0 )
102 			  &&  ( ( year % 100 ) != 0 ) )
103 			 || ( ( year % 400 ) == 0 ) )
104 			{
105 				days_in_month = 29;
106 			}
107 			else
108 			{
109 				days_in_month = 28;
110 			}
111 		}
112 		/* April (4), June (6), September (9), November (11)
113 		 */
114 		else if( ( month == 4 )
115 		      || ( month == 6 )
116 		      || ( month == 9 )
117 		      || ( month == 11 ) )
118 		{
119 			days_in_month = 30;
120 		}
121 		/* January (1), March (3), May (5), July (7), August (8), October (10), December (12)
122 		 */
123 		else if( ( month == 1 )
124 		      || ( month == 3 )
125 		      || ( month == 5 )
126 		      || ( month == 7 )
127 		      || ( month == 8 )
128 		      || ( month == 10 )
129 		      || ( month == 12 ) )
130 		{
131 			days_in_month = 31;
132 		}
133 		/* This should never happen, but just in case
134 		 */
135 		else
136 		{
137 			PyErr_Format(
138 			 PyExc_IOError,
139 			 "%s: unsupported month: %" PRIu8 ".",
140 			 function,
141 			 month );
142 
143 			return( NULL );
144 		}
145 		if( number_of_days <= days_in_month )
146 		{
147 			break;
148 		}
149 		number_of_days -= days_in_month;
150 
151 		month += 1;
152 	}
153 	/* Determine the day
154 	 */
155 	day_of_month = (uint8_t) number_of_days;
156 
157 	PyDateTime_IMPORT;
158 
159 	datetime_object = (PyObject *) PyDateTime_FromDateAndTime(
160 	                                (int) year,
161 	                                (int) month,
162 	                                (int) day_of_month,
163 	                                (int) hours,
164 	                                (int) minutes,
165 	                                (int) seconds,
166 	                                0 );
167 
168 	return( datetime_object );
169 }
170 
171 /* Creates a new datetime object from a FAT date time
172  * Returns a Python object if successful or NULL on error
173  */
pyevt_datetime_new_from_fat_date_time(uint32_t fat_date_time)174 PyObject *pyevt_datetime_new_from_fat_date_time(
175            uint32_t fat_date_time )
176 {
177 	PyObject *datetime_object = NULL;
178 	static char *function     = "pyevt_datetime_new_from_fat_date_time";
179 	uint16_t year             = 0;
180 	uint8_t day_of_month      = 0;
181 	uint8_t days_in_month     = 0;
182 	uint8_t hours             = 0;
183 	uint8_t minutes           = 0;
184 	uint8_t month             = 0;
185 	uint8_t seconds           = 0;
186 
187 	/* The day of month is stored in the next 5 bits
188 	 */
189 	day_of_month    = fat_date_time & 0x1f;
190 	fat_date_time >>= 5;
191 
192 	/* The month is stored in the next 4 bits
193 	 */
194 	month           = fat_date_time & 0x0f;
195 	fat_date_time >>= 4;
196 
197 	/* The year is stored in the next 7 bits starting at 1980
198 	 */
199 	year            = 1980 + ( fat_date_time & 0x7f );
200 	fat_date_time >>= 7;
201 
202 	/* The number of seconds are stored in the lower 5 bits
203 	 * in intervals of 2 seconds
204 	 */
205 	seconds         = ( fat_date_time & 0x1f ) * 2;
206 	fat_date_time >>= 5;
207 
208 	/* The number of minutes are stored in the next 6 bits
209 	 */
210 	minutes         = fat_date_time & 0x3f;
211 	fat_date_time >>= 6;
212 
213 	/* The number of hours are stored in the next 5 bits
214 	 */
215 	hours = fat_date_time & 0x1f;
216 
217 	/* February (2)
218 	 */
219 	if( month == 2 )
220 	{
221 		if( ( ( ( year % 4 ) == 0 )
222 		  &&  ( ( year % 100 ) != 0 ) )
223 		 || ( ( year % 400 ) == 0 ) )
224 		{
225 			days_in_month = 29;
226 		}
227 		else
228 		{
229 			days_in_month = 28;
230 		}
231 	}
232 	/* April (4), June (6), September (9), November (11)
233 	 */
234 	else if( ( month == 4 )
235 	      || ( month == 6 )
236 	      || ( month == 9 )
237 	      || ( month == 11 ) )
238 	{
239 		days_in_month = 30;
240 	}
241 	/* January (1), March (3), May (5), July (7), August (8), October (10), December (12)
242 	 */
243 	else if( ( month == 1 )
244 	      || ( month == 3 )
245 	      || ( month == 5 )
246 	      || ( month == 7 )
247 	      || ( month == 8 )
248 	      || ( month == 10 )
249 	      || ( month == 12 ) )
250 	{
251 		days_in_month = 31;
252 	}
253 	else
254 	{
255 		PyErr_Format(
256 		 PyExc_IOError,
257 		 "%s: unsupported month: %" PRIu8 ".",
258 		 function,
259 		 month );
260 
261 		return( NULL );
262 	}
263 	if( ( day_of_month == 0 )
264 	 || ( day_of_month > days_in_month ) )
265 	{
266 		PyErr_Format(
267 		 PyExc_IOError,
268 		 "%s: unsupported day of month: %" PRIu8 ".",
269 		 function,
270 		 day_of_month );
271 
272 		return( NULL );
273 	}
274 	PyDateTime_IMPORT;
275 
276 	datetime_object = (PyObject *) PyDateTime_FromDateAndTime(
277 	                                (int) year,
278 	                                (int) month,
279 	                                (int) day_of_month,
280 	                                (int) hours,
281 	                                (int) minutes,
282 	                                (int) seconds,
283 	                                0 );
284 
285 	return( datetime_object );
286 }
287 
288 /* Creates a new datetime object from a FILETIME
289  * Returns a Python object if successful or NULL on error
290  */
pyevt_datetime_new_from_filetime(uint64_t filetime)291 PyObject *pyevt_datetime_new_from_filetime(
292            uint64_t filetime )
293 {
294 	PyObject *datetime_object = NULL;
295 	uint32_t micro_seconds    = 0;
296 	uint16_t year             = 0;
297 	uint8_t hours             = 0;
298 	uint8_t minutes           = 0;
299 	uint8_t seconds           = 0;
300 
301 	/* The timestamp is in units of 100 nano seconds correct the value to seconds
302 	 */
303 	micro_seconds = (uint32_t) ( filetime % 10000000 ) / 10;
304 	filetime      /= 10000000;
305 
306 	/* There are 60 seconds in a minute correct the value to minutes
307 	 */
308 	seconds   = (uint8_t) ( filetime % 60 );
309 	filetime /= 60;
310 
311 	/* There are 60 minutes in an hour correct the value to hours
312 	 */
313 	minutes   = (uint8_t) ( filetime % 60 );
314 	filetime /= 60;
315 
316 	/* There are 24 hours in a day correct the value to days
317 	 */
318 	hours     = (uint8_t) ( filetime % 24 );
319 	filetime /= 24;
320 
321 	/* Add 1 day to compensate that Jan 1 1601 is represented as 0
322 	 */
323 	filetime += 1;
324 
325 	/* Determine the number of years starting at '1 Jan 1601 00:00:00'
326 	 * correct the value to days within the year
327 	 */
328 	year = 1601;
329 
330 	if( filetime >= 36159 )
331 	{
332 		year = 1700;
333 
334 		filetime -= 36159;
335 	}
336 	datetime_object = pyevt_datetime_new_from_time_elements(
337 	                   year,
338 	                   filetime,
339 	                   hours,
340 	                   minutes,
341 	                   seconds,
342 	                   micro_seconds );
343 
344 	return( datetime_object );
345 }
346 
347 /* Creates a new datetime object from a floatingtime
348  * Returns a Python object if successful or NULL on error
349  */
pyevt_datetime_new_from_floatingtime(uint64_t floatingtime)350 PyObject *pyevt_datetime_new_from_floatingtime(
351            uint64_t floatingtime )
352 {
353 	byte_stream_float64_t timestamp;
354 
355 	PyObject *datetime_object = NULL;
356 	static char *function     = "pyevt_datetime_new_from_floatingtime";
357 	uint32_t days_in_century  = 0;
358 	uint32_t micro_seconds    = 0;
359 	uint16_t days_in_year     = 0;
360 	uint16_t year             = 0;
361 	uint8_t day_of_month      = 0;
362 	uint8_t days_in_month     = 0;
363 	uint8_t hours             = 0;
364 	uint8_t minutes           = 0;
365 	uint8_t month             = 0;
366 	uint8_t seconds           = 0;
367 
368 	timestamp.integer = floatingtime;
369 
370 	/* Determine the number of years starting at '30 Dec 1899 00:00:00'
371 	 * correct the value to days within the year
372 	 */
373 	year = 1899;
374 
375 	if( timestamp.floating_point >= 2 )
376 	{
377 		year = 1900;
378 
379 		timestamp.floating_point -= 2;
380 	}
381 	while( timestamp.floating_point > 0 )
382 	{
383 		if( ( year % 400 ) == 0 )
384 		{
385 			days_in_century = 36525;
386 		}
387 		else
388 		{
389 			days_in_century = 36524;
390 		}
391 		if( timestamp.floating_point <= days_in_century )
392 		{
393 			break;
394 		}
395 		timestamp.floating_point -= days_in_century;
396 
397 		year += 100;
398 	}
399 	while( timestamp.floating_point > 0 )
400 	{
401 		/* Check for a leap year
402 		 * The year is ( ( dividable by 4 ) and ( not dividable by 100 ) ) or ( dividable by 400 )
403 		 */
404 		if( ( ( ( year % 4 ) == 0 )
405 		  &&  ( ( year % 100 ) != 0 ) )
406 		 || ( ( year % 400 ) == 0 ) )
407 		{
408 			days_in_year = 366;
409 		}
410 		else
411 		{
412 			days_in_year = 365;
413 		}
414 		if( timestamp.floating_point <= days_in_year )
415 		{
416 			break;
417 		}
418 		timestamp.floating_point -= days_in_year;
419 
420 		year += 1;
421 	}
422 	/* Determine the month correct the value to days within the month
423 	 */
424 	month = 1;
425 
426 	while( timestamp.floating_point > 0 )
427 	{
428 		/* February (2)
429 		 */
430 		if( month == 2 )
431 		{
432 			if( ( ( ( year % 4 ) == 0 )
433 			  &&  ( ( year % 100 ) != 0 ) )
434 			 || ( ( year % 400 ) == 0 ) )
435 			{
436 				days_in_month = 29;
437 			}
438 			else
439 			{
440 				days_in_month = 28;
441 			}
442 		}
443 		/* April (4), June (6), September (9), November (11)
444 		 */
445 		else if( ( month == 4 )
446 		      || ( month == 6 )
447 		      || ( month == 9 )
448 		      || ( month == 11 ) )
449 		{
450 			days_in_month = 30;
451 		}
452 		/* January (1), March (3), May (5), July (7), August (8), October (10), December (12)
453 		 */
454 		else if( ( month == 1 )
455 		      || ( month == 3 )
456 		      || ( month == 5 )
457 		      || ( month == 7 )
458 		      || ( month == 8 )
459 		      || ( month == 10 )
460 		      || ( month == 12 ) )
461 		{
462 			days_in_month = 31;
463 		}
464 		/* This should never happen, but just in case
465 		 */
466 		else
467 		{
468 			PyErr_Format(
469 			 PyExc_IOError,
470 			 "%s: unsupported month: %" PRIu8 ".",
471 			 function,
472 			 month );
473 
474 			return( NULL );
475 		}
476 		if( timestamp.floating_point <= days_in_month )
477 		{
478 			break;
479 		}
480 		timestamp.floating_point -= days_in_month;
481 
482 		month += 1;
483 	}
484 	/* Determine the day
485 	 */
486 	day_of_month              = (uint8_t) timestamp.floating_point;
487 	timestamp.floating_point -= day_of_month;
488 
489 	/* There are 24 hours in a day correct the value to hours
490 	 */
491 	timestamp.floating_point *= 24;
492 	hours                     = (uint8_t) timestamp.floating_point;
493 	timestamp.floating_point -= hours;
494 
495 	/* There are 60 minutes in an hour correct the value to minutes
496 	 */
497 	timestamp.floating_point *= 60;
498 	minutes                   = (uint8_t) timestamp.floating_point;
499 	timestamp.floating_point -= minutes;
500 
501 	/* There are 60 seconds in a minute correct the value to seconds
502 	 */
503 	timestamp.floating_point *= 60;
504 	seconds                   = (uint8_t) timestamp.floating_point;
505 	timestamp.floating_point -= seconds;
506 
507 	/* There are 1000 micro seconds in a seconds correct the value to micro seconds
508 	 */
509 	timestamp.floating_point *= 1000000;
510 	micro_seconds             = (uint8_t) timestamp.floating_point;
511 	timestamp.floating_point -= micro_seconds;
512 
513 	PyDateTime_IMPORT;
514 
515 	datetime_object = (PyObject *) PyDateTime_FromDateAndTime(
516 	                                (int) year,
517 	                                (int) month,
518 	                                (int) day_of_month,
519 	                                (int) hours,
520 	                                (int) minutes,
521 	                                (int) seconds,
522 	                                (int) micro_seconds );
523 
524 	return( datetime_object );
525 }
526 
527 /* Creates a new datetime object from a HFS time
528  * Returns a Python object if successful or NULL on error
529  */
pyevt_datetime_new_from_hfs_time(uint32_t hfs_time)530 PyObject *pyevt_datetime_new_from_hfs_time(
531            uint32_t hfs_time )
532 {
533 	PyObject *datetime_object = NULL;
534 	uint16_t year             = 0;
535 	uint8_t hours             = 0;
536 	uint8_t minutes           = 0;
537 	uint8_t seconds           = 0;
538 
539 	/* There are 60 seconds in a minute correct the value to minutes
540 	 */
541 	seconds   = (uint8_t) ( hfs_time % 60 );
542 	hfs_time /= 60;
543 
544 	/* There are 60 minutes in an hour correct the value to hours
545 	 */
546 	minutes   = (uint8_t) ( hfs_time % 60 );
547 	hfs_time /= 60;
548 
549 	/* There are 24 hours in a day correct the value to days
550 	 */
551 	hours     = (uint8_t) ( hfs_time % 24 );
552 	hfs_time /= 24;
553 
554 	/* Add 1 day to compensate that Jan 1 1904 is represented as 0
555 	 */
556 	hfs_time += 1;
557 
558 	/* Determine the number of years starting at '1 Jan 1904 00:00:00'
559 	 * correct the value to days within the year
560 	 */
561 	year = 1904;
562 
563 	if( hfs_time >= 35064 )
564 	{
565 		year = 2000;
566 
567 		hfs_time -= 35064;
568 	}
569 	datetime_object = pyevt_datetime_new_from_time_elements(
570 	                   year,
571 	                   (uint64_t) hfs_time,
572 	                   hours,
573 	                   minutes,
574 	                   seconds,
575 	                   0 );
576 
577 	return( datetime_object );
578 }
579 
580 /* Creates a new datetime object from a POSIX time
581  * Returns a Python object if successful or NULL on error
582  */
pyevt_datetime_new_from_posix_time(int64_t posix_time)583 PyObject *pyevt_datetime_new_from_posix_time(
584            int64_t posix_time )
585 {
586 	PyObject *datetime_object = NULL;
587 	uint16_t year             = 0;
588 	uint8_t hours             = 0;
589 	uint8_t minutes           = 0;
590 	uint8_t seconds           = 0;
591 
592 	/* There are 60 seconds in a minute correct the value to minutes
593 	 */
594 	seconds     = posix_time % 60;
595 	posix_time /= 60;
596 
597 	/* There are 60 minutes in an hour correct the value to hours
598 	 */
599 	minutes     = posix_time % 60;
600 	posix_time /= 60;
601 
602 	/* There are 24 hours in a day correct the value to days
603 	 */
604 	hours       = posix_time % 24;
605 	posix_time /= 24;
606 
607 	/* Add 1 day to compensate that Jan 1 1601 is represented as 0
608 	 */
609 	posix_time += 1;
610 
611 	/* Determine the number of years starting at '1 Jan 1970 00:00:00'
612 	 * correct the value to days within the year
613 	 */
614 	year = 1970;
615 
616 	if( posix_time >= 10957 )
617 	{
618 		year = 2000;
619 
620 		posix_time -= 10957;
621 	}
622 	datetime_object = pyevt_datetime_new_from_time_elements(
623 	                   year,
624 	                   (uint64_t) posix_time,
625 	                   hours,
626 	                   minutes,
627 	                   seconds,
628 	                   0 );
629 
630 	return( datetime_object );
631 }
632 
633 /* Creates a new datetime object from a POSIX time in micro seconds
634  * Returns a Python object if successful or NULL on error
635  */
pyevt_datetime_new_from_posix_time_in_micro_seconds(int64_t posix_time)636 PyObject *pyevt_datetime_new_from_posix_time_in_micro_seconds(
637            int64_t posix_time )
638 {
639 	PyObject *datetime_object = NULL;
640 	uint32_t micro_seconds    = 0;
641 	uint16_t year             = 0;
642 	uint8_t hours             = 0;
643 	uint8_t minutes           = 0;
644 	uint8_t seconds           = 0;
645 
646 	/* There are 1000000 micro seconds in a second correct the value to seconds
647 	 */
648 	micro_seconds = (uint32_t) ( posix_time % 1000000 );
649 	posix_time   /= 1000000;
650 
651 	/* There are 60 seconds in a minute correct the value to minutes
652 	 */
653 	seconds     = posix_time % 60;
654 	posix_time /= 60;
655 
656 	/* There are 60 minutes in an hour correct the value to hours
657 	 */
658 	minutes     = posix_time % 60;
659 	posix_time /= 60;
660 
661 	/* There are 24 hours in a day correct the value to days
662 	 */
663 	hours       = posix_time % 24;
664 	posix_time /= 24;
665 
666 	/* Add 1 day to compensate that Jan 1 1970 is represented as 0
667 	 */
668 	posix_time += 1;
669 
670 	/* Determine the number of years starting at '1 Jan 1970 00:00:00'
671 	 * correct the value to days within the year
672 	 */
673 	year = 1970;
674 
675 	if( posix_time >= 10957 )
676 	{
677 		year = 2000;
678 
679 		posix_time -= 10957;
680 	}
681 	datetime_object = pyevt_datetime_new_from_time_elements(
682 	                   year,
683 	                   (uint64_t) posix_time,
684 	                   hours,
685 	                   minutes,
686 	                   seconds,
687 	                   micro_seconds );
688 
689 	return( datetime_object );
690 }
691 
692