xref: /linux/fs/udf/udftime.c (revision 613f21505b25a4f43f33de00f11afc059bedde2b)
1  // SPDX-License-Identifier: LGPL-2.0+
2  /* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
3     This file is part of the GNU C Library.
4     Contributed by Paul Eggert (eggert@twinsun.com). */
5  
6  /*
7   * dgb 10/02/98: ripped this from glibc source to help convert timestamps
8   *               to unix time
9   *     10/04/98: added new table-based lookup after seeing how ugly
10   *               the gnu code is
11   * blf 09/27/99: ripped out all the old code and inserted new table from
12   *		 John Brockmeyer (without leap second corrections)
13   *		 rewrote udf_stamp_to_time and fixed timezone accounting in
14   *		 udf_time_to_stamp.
15   */
16  
17  /*
18   * We don't take into account leap seconds. This may be correct or incorrect.
19   * For more NIST information (especially dealing with leap seconds), see:
20   * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm
21   */
22  
23  #include "udfdecl.h"
24  
25  #include <linux/types.h>
26  #include <linux/kernel.h>
27  #include <linux/time.h>
28  
29  void
30  udf_disk_stamp_to_time(struct timespec64 *dest, struct timestamp src)
31  {
32  	u16 typeAndTimezone = le16_to_cpu(src.typeAndTimezone);
33  	u16 year = le16_to_cpu(src.year);
34  	uint8_t type = typeAndTimezone >> 12;
35  	int16_t offset;
36  
37  	if (type == 1) {
38  		offset = typeAndTimezone << 4;
39  		/* sign extent offset */
40  		offset = (offset >> 4);
41  		if (offset == -2047) /* unspecified offset */
42  			offset = 0;
43  	} else
44  		offset = 0;
45  
46  	dest->tv_sec = mktime64(year, src.month, src.day, src.hour, src.minute,
47  			src.second);
48  	dest->tv_sec -= offset * 60;
49  
50  	/*
51  	 * Sanitize nanosecond field since reportedly some filesystems are
52  	 * recorded with bogus sub-second values.
53  	 */
54  	if (src.centiseconds < 100 && src.hundredsOfMicroseconds < 100 &&
55  	    src.microseconds < 100) {
56  		dest->tv_nsec = 1000 * (src.centiseconds * 10000 +
57  			src.hundredsOfMicroseconds * 100 + src.microseconds);
58  	} else {
59  		dest->tv_nsec = 0;
60  	}
61  }
62  
63  void
64  udf_time_to_disk_stamp(struct timestamp *dest, struct timespec64 ts)
65  {
66  	time64_t seconds;
67  	int16_t offset;
68  	struct tm tm;
69  
70  	offset = -sys_tz.tz_minuteswest;
71  
72  	dest->typeAndTimezone = cpu_to_le16(0x1000 | (offset & 0x0FFF));
73  
74  	seconds = ts.tv_sec + offset * 60;
75  	time64_to_tm(seconds, 0, &tm);
76  	dest->year = cpu_to_le16(tm.tm_year + 1900);
77  	dest->month = tm.tm_mon + 1;
78  	dest->day = tm.tm_mday;
79  	dest->hour = tm.tm_hour;
80  	dest->minute = tm.tm_min;
81  	dest->second = tm.tm_sec;
82  	dest->centiseconds = ts.tv_nsec / 10000000;
83  	dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 -
84  					dest->centiseconds * 10000) / 100;
85  	dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 -
86  			      dest->hundredsOfMicroseconds * 100);
87  }
88  
89  /* EOF */
90