Line data Source code
1 : /*
2 : dostime.c - routines for converting UNIX time to MS-DOS time and vice versa.
3 :
4 : Various Copyrights:
5 :
6 : First written by Mark Adler, Richard B. Wales, Jean-loup Gailly,
7 : Kai Uwe Rommel, Onno van der Linden and Igor Mandrichenko (1990-1997).
8 : Tweaked further by Bryan Burns (1999).
9 : Redesigned with full error checks by Alexis Wilke (2015).
10 :
11 : This program is free software; you can redistribute it and/or
12 : modify it under the terms of the GNU General Public License
13 : as published by the Free Software Foundation; either version 2
14 : of the License, or (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program; if not, write to the Free Software
23 : Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 : */
25 :
26 : /** \file
27 : * \brief Functions to convert times between Unix and MS-DOS times.
28 : *
29 : * These functions make use of the old date and time format
30 : * defined to implement the FAT file system. This is defined
31 : * on the Microsoft website here:
32 : *
33 : * https://msdn.microsoft.com/en-us/library/windows/desktop/ms724247%28v=vs.85%29.aspx
34 : *
35 : * As a quick recap, we have:
36 : *
37 : * Date (actually we view those as bit 16 to 31)
38 : *
39 : * \code
40 : * 0-4 Day of the month (1-31)
41 : * 5-8 Month (1 = January, 2 = February, and so on)
42 : * 9-15 Year offset from 1980 (add 1980 to get actual year)
43 : * \endcode
44 : *
45 : * Time
46 : *
47 : * \code
48 : * 0-4 Second divided by 2
49 : * 5-10 Minute (0-59)
50 : * 11-15 Hour (0-23 on a 24-hour clock)
51 : * \endcode
52 : *
53 : * \note
54 : * This implementation uses C code.
55 : */
56 :
57 : #include "dostime.h"
58 :
59 : #include <stdio.h>
60 : #include <stdlib.h>
61 : #include <string.h>
62 :
63 :
64 : /** \brief Return the minimum possible value.
65 : *
66 : * This function returns the minimum DOS time that can be represented
67 : * in a dostime_t parameter.
68 : *
69 : * At this time we use a 32 bit field (like the Zip archive) so the
70 : * maximum is Dec 31, 2107 23:59:59.
71 : *
72 : * \note
73 : * To get the corresponding Unix time, use the dos2unixtime() as in:
74 : *
75 : * \code
76 : * time_t min(dos2unixtime(mindostime()));
77 : * \endcode
78 : *
79 : * \return The smallest possible DOS time.
80 : */
81 1 : dostime_t mindostime()
82 : {
83 : // Jan 1, 1980 00:00:00 is the minimum
84 1 : return 0x00210000;
85 : }
86 :
87 :
88 : /** \brief Return the maximum possible value .
89 : *
90 : * This function returns the maximum DOS time that can be represented
91 : * in a dostime_t parameter.
92 : *
93 : * At this time we use a 32 bit field (like the Zip archive) so the
94 : * maximum is Dec 31, 2107 23:59:59.
95 : *
96 : * \note
97 : * To get the corresponding Unix time, use the dos2unixtime() as in:
98 : *
99 : * \code
100 : * time_t max(dos2unixtime(maxdostime()));
101 : * \endcode
102 : *
103 : * \return The largest possible DOS time.
104 : */
105 1 : dostime_t maxdostime()
106 : {
107 : // Dec 31, 2107 23:59:59 is the maximum
108 1 : return 0xFF9FBF7D;
109 : }
110 :
111 :
112 : /** \brief Convert a DOS time to a Unix time
113 : *
114 : * This function returns the Unix time_t value (GMT/UTC time) from
115 : * the DOS format (local) time dostime, where dostime is a four
116 : * byte value (date in most significant word, time in least
117 : * significant word), see dostime() function.
118 : *
119 : * If the input DOS time is invalid, then the function returns -1.
120 : *
121 : * \note
122 : * If the dostime is not valid (one of the parameters is out of
123 : * range) then the function returns -1.
124 : *
125 : * \param[in] dostime A DOS time value as found in a zip file.
126 : *
127 : * \return The DOS time converted to a Unix time or -1.
128 : *
129 : * \sa dostime()
130 : */
131 400610 : time_t dos2unixtime(dostime_t dostime)
132 : {
133 : struct tm t; /* argument for mktime() */
134 :
135 400610 : memset(&t, 0, sizeof(t));
136 :
137 400610 : t.tm_isdst = -1; /* let mktime() determine if DST is in effect */
138 : /* Convert DOS time to UNIX time_t format */
139 400610 : t.tm_sec = (((int)dostime << 1) & 0x3E);
140 400610 : t.tm_min = (((int)dostime >> 5) & 0x3F);
141 400610 : t.tm_hour = (((int)dostime >> 11) & 0x1F);
142 400610 : t.tm_mday = (((int)dostime >> 16) & 0x1F);
143 400610 : t.tm_mon = (((int)dostime >> 21) & 0x0F) - 1;
144 400610 : t.tm_year = (((int)dostime >> 25) & 0x7F) + 80;
145 :
146 400610 : if(t.tm_year < 80 || t.tm_year > 207
147 400610 : || t.tm_mon < 0 || t.tm_mon > 11
148 390828 : || t.tm_mday < 1 || t.tm_mday > 31 /** \FIXME Maximum for tm_mday depends on month/year. */
149 390828 : || t.tm_hour < 0 || t.tm_hour > 23
150 390828 : || t.tm_min < 0 || t.tm_min > 59
151 390828 : || t.tm_sec < 0 || t.tm_sec > 59)
152 : {
153 9782 : return -1;
154 : }
155 :
156 : // A full round trip between Unix date to DOS and back to Unix works
157 : // as is (without worry about the current timezone) because the DOS
158 : // format makes use of localdate() and that's 1 to 1 compatible with
159 : // mktime() which expects a local date too.
160 390828 : return mktime(&t);
161 : }
162 :
163 :
164 : /* \brief Convert a broken up date to a DOS date.
165 : *
166 : * Convert the date y/n/d and time h:m:s to a four byte DOS date and
167 : * time (date in high two bytes, time in low two bytes allowing magnitude
168 : * comparison).
169 : *
170 : * The function returns zero if the month, day, hour, minute, second are
171 : * out of range.
172 : *
173 : * \todo
174 : * The dostime_t type is 32 bits, although if it were 64 bits we would
175 : * never have an overflow with the maximum. However, the Zip archive
176 : * format only supports 32 bits as far as I know.
177 : *
178 : * \param[in] year The year.
179 : * \param[in] month The month.
180 : * \param[in] day The day.
181 : * \param[in] hour The hour.
182 : * \param[in] minute The minute.
183 : * \param[in] second The second.
184 : *
185 : * \return The date parameters transformed in a DOS time value or zero if the
186 : * date is considered invalid.
187 : */
188 719932 : dostime_t dostime(int year, int month, int day, int hour, int minute, int second)
189 : {
190 719932 : if(year < 1980 || year > 2107
191 709926 : || month < 1 || month > 12
192 709926 : || day < 1 || day > 31
193 709926 : || hour < 0 || hour > 23
194 709926 : || minute < 0 || minute > 59
195 709926 : || second < 0 || second > 59)
196 : {
197 10006 : return 0;
198 : }
199 :
200 1419852 : return (((dostime_t) year - 1980) << 25)
201 709926 : | (((dostime_t) month ) << 21)
202 709926 : | (((dostime_t) day ) << 16)
203 709926 : | (((dostime_t) hour ) << 11)
204 709926 : | (((dostime_t) minute ) << 5)
205 709926 : | (((dostime_t) second ) >> 1); // 1 every other second
206 : }
207 :
208 :
209 : /** \brief Convert a Unix date to a DOS date.
210 : *
211 : * This function return the Unix time_t converted in DOS format,
212 : * rounded up to the next even second.
213 : *
214 : * \param[in] unix_time A Unix time_t value.
215 : *
216 : * \return The Unix date in DOS format unless it is out of range for
217 : * a DOS time and date in which case zero (0) is returned.
218 : */
219 719930 : dostime_t unix2dostime(time_t unix_time)
220 : {
221 : time_t even_time;
222 : struct tm *s; /* result of localtime() */
223 :
224 719930 : even_time = (unix_time + 1) & ~1; /* Round up to even seconds. */
225 719930 : s = localtime(&even_time); /* Use local time since MSDOS does. */
226 719930 : return dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday,
227 : s->tm_hour, s->tm_min, s->tm_sec);
228 : }
229 :
230 :
231 : // Local Variables:
232 : // mode: cpp
233 : // indent-tabs-mode: nil
234 : // c-basic-offset: 4
235 : // tab-width: 4
236 : // End:
237 :
238 : // vim: ts=4 sw=4 et
|