zipios 2.3.4
Zipios -- a small C++ library providing easy access to .zip files.
src/dosdatetime.cpp
Go to the documentation of this file.
1/*
2 Zipios -- a small C++ library that provides easy access to .zip files.
3
4 Copyright (c) 2019-2022 Made to Order Software Corp. All Rights Reserved
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
36
38
39
40namespace zipios
41{
42
43
46
47
48
49
56{
58 struct fields
59 {
60#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
66 DOSDateTime::dosdatetime_t m_second : 5; // WARNING: the precision is every 2 seconds (0, 2, 4, etc.)
67#else
68 DOSDateTime::dosdatetime_t m_second : 5; // WARNING: the precision is every 2 seconds (0, 2, 4, etc.)
73 DOSDateTime::dosdatetime_t m_year : 7; // add 1980
74#endif
76};
77
78
79
80namespace
81{
82
88int const g_days_in_month[12] = {
89 /* Jan */ 31,
90 /* Feb */ 0, // special handling
91 /* Mar */ 31,
92 /* Apr */ 30,
93 /* May */ 31,
94 /* Jun */ 30,
95 /* Jul */ 31,
96 /* Aug */ 31,
97 /* Sep */ 30,
98 /* Oct */ 31,
99 /* Nov */ 30,
100 /* Dec */ 31
101};
102
103
104int const g_ydays[12] = {
105 /* Jan */ 0,
106 /* Feb */ 31,
107 /* Mar */ 31 + 0, // special handling
108 /* Apr */ 31 + 0 + 31,
109 /* May */ 31 + 0 + 31 + 30,
110 /* Jun */ 31 + 0 + 31 + 30 + 31,
111 /* Jul */ 31 + 0 + 31 + 30 + 31 + 30,
112 /* Aug */ 31 + 0 + 31 + 30 + 31 + 30 + 31,
113 /* Sep */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31,
114 /* Oct */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
115 /* Nov */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
116 /* Dec */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
117};
118
119
120}
121
122
123
145{
148 return conv.m_fields.m_second < 30 // remember we only keep `sec / 2` in a DOS time field
149 && conv.m_fields.m_minute < 60
150 && conv.m_fields.m_hour < 24
151 && conv.m_fields.m_mday > 0
152 && conv.m_fields.m_mday <= daysInMonth()
153 && conv.m_fields.m_month > 0
154 && conv.m_fields.m_month < 13;
155}
156
157
179{
182
183 if(conv.m_fields.m_month == 0
184 || conv.m_fields.m_month > 12)
185 {
186 return -1;
187 }
188
189 if(conv.m_fields.m_month == 2)
190 {
191 // Feb. depends on the year
192 //
193 int year = conv.m_fields.m_year + 1980;
194
195 return ((year) % 400) == 0
196 ? 29
197 : (((year) % 100) == 0
198 ? 28
199 : (((year) % 4) == 0
200 ? 29
201 : 28));
202 }
203
204 return g_days_in_month[conv.m_fields.m_month - 1];
205}
206
207
223{
226 return conv.m_fields.m_second * 2;
227}
228
229
239{
242 return conv.m_fields.m_minute;
243}
244
245
255{
258 return conv.m_fields.m_hour;
259}
260
261
273{
276 return conv.m_fields.m_mday;
277}
278
279
289{
292 return conv.m_fields.m_month;
293}
294
295
305{
308 return conv.m_fields.m_year + 1980;
309}
310
311
335{
336 if(second < 0
337 || second > 59)
338 {
339 throw InvalidException("Second is out of range for an MS-DOS Date & Time object. Range is [0, 59].");
340 }
341
344 conv.m_fields.m_second = second / 2;
346}
347
348
360{
361 if(minute < 0
362 || minute > 59)
363 {
364 throw InvalidException("Minute is out of range for an MS-DOS Date & Time object. Range is [0, 59].");
365 }
366
369 conv.m_fields.m_minute = minute;
371}
372
373
385{
386 if(hour < 0
387 || hour > 23)
388 {
389 throw InvalidException("Hour is out of range for an MS-DOS Date & Time object. Range is [0, 23].");
390 }
391
394 conv.m_fields.m_hour = hour;
396}
397
398
415{
416 if(mday < 1
417 || mday > 31)
418 {
419 throw InvalidException("Day of the month is out of range for an MS-DOS Date & Time object. Range is [1, 31].");
420 }
421
424 conv.m_fields.m_mday = mday;
426}
427
428
439{
440 if(month < 1
441 || month > 12)
442 {
443 throw InvalidException("Month out of range for an MS-DOS Date & Time object. Range is [1, 12].");
444 }
445
448 conv.m_fields.m_month = month;
450}
451
452
463{
464 if(year < 1980
465 || year > 2107)
466 {
467 throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107] (1).");
468 }
469
472 conv.m_fields.m_year = year - 1980;
474}
475
476
488
489
499{
500 m_dosdatetime = datetime;
501}
502
503
545void DOSDateTime::setUnixTimestamp(std::time_t unix_timestamp)
546{
547 // round up to the next second
548 //
549 unix_timestamp += 1;
550 unix_timestamp &= ~1;
551
552 struct tm t;
553#ifdef ZIPIOS_WINDOWS
554 localtime_s(&t, &unix_timestamp);
555#else
556 localtime_r(&unix_timestamp, &t);
557#endif
558
559//std::cout << "test with: " << unix_timestamp << " -- " << t.tm_year
560// << " (" << (t.tm_year < 1980 - 1900 ? 1 : 0)
561// << ", " << (t.tm_year > 2107 - 1900 ? 1 : 0)
562// << ")\n";
563
564 if(t.tm_year < 1980 - 1900
565 || t.tm_year > 2107 - 1900)
566 {
567 throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107] (2).");
568 }
569
571 conv.m_fields.m_second = t.tm_sec / 2; // already rounded up to the next second, so just divide by 2 is enough here
572 conv.m_fields.m_minute = t.tm_min;
573 conv.m_fields.m_hour = t.tm_hour;
574 conv.m_fields.m_mday = t.tm_mday;
575 conv.m_fields.m_month = t.tm_mon + 1;
576 conv.m_fields.m_year = t.tm_year + 1900 - 1980;
577
579}
580
581
599{
600 if(isValid())
601 {
604
605 struct tm t;
606 t.tm_sec = conv.m_fields.m_second * 2; // we lost the bottom bit, nothing we can do about it here
607 t.tm_min = conv.m_fields.m_minute;
608 t.tm_hour = conv.m_fields.m_hour;
609 t.tm_mday = conv.m_fields.m_mday;
610 t.tm_mon = conv.m_fields.m_month - 1;
611 t.tm_year = conv.m_fields.m_year + 1980 - 1900;
612 t.tm_wday = 0;
613 t.tm_yday = 0;
614 t.tm_isdst = -1;
615
616//std::cerr << "date to Unix timestamp: " << (t.tm_mon + 1) << " " << t.tm_mday << ", " << (t.tm_year + 1900)
617// << " " << t.tm_hour << ":" << t.tm_min << ":" << t.tm_sec << "\n";
618
619 if(sizeof(std::time_t) == 4
620 && t.tm_year >= 2038)
621 {
622 // the exact date is Jan 19, 2038 at 03:13:07 UTC
623 // see https://en.wikipedia.org/wiki/Year_2038_problem
624 //
625 // we have no problem with 64 bits, max. year is about 292,000,000,000
626 // although the tm_year is an int, so really we're limited to 2 billion
627 // years, again just fine for a DOS Date is limited to 2107...
628 //
629 throw InvalidException("Year out of range for a 32 bit Unix Timestamp object. Range is (1901, 2038).");
630 }
631
632 // the zip file format expects dates in local time, not UTC
633 // so I use mktime() directly
634 //
635 return mktime(&t);
636
637// // mktime() makes use of the timezone, here is some code that
638// // replaces mktime() with a UTC date conversion
639// //
640// time_t const year = t.tm_year + 1900;
641// time_t timestamp = (year - 1970LL) * 31536000LL
642// + ((year - 1969LL) / 4LL) * 86400LL
643// - ((year - 1901LL) / 100LL) * 86400LL
644// + ((year - 1601LL) / 400LL) * 86400LL
645// + (t.tm_mday + g_ydays[t.tm_mon] - 1) * 86400LL
646// + t.tm_hour * 3600LL
647// + t.tm_min * 60LL
648// + t.tm_sec * 1LL;
649// if(t.tm_mon >= 2)
650// {
651// // add seconds in February
652// //
653// timestamp += (year % 400 == 0
654// ? 29 // for year 2000
655// : (year % 100 == 0
656// ? 28 // for year 2100
657// : (year % 4 == 0
658// ? 29
659// : 28))) * 86400LL;
660// }
661//
662// return timestamp;
663 }
664
665 return 0;
666}
667
668
669
670
671} // zipios namespace
672
673// Local Variables:
674// mode: cpp
675// indent-tabs-mode: nil
676// c-basic-offset: 4
677// tab-width: 4
678// End:
679
680// vim: ts=4 sw=4 et
dosdatetime_t m_dosdatetime
dosdatetime_t getDOSDateTime() const
Retrieve the DOSDateTime value as is.
int getMinute() const
Get the minute.
std::time_t getUnixTimestamp() const
Retrieve the DOSDateTime as a Unix timestamp.
bool isValid() const
Check whether this DOS Date & Date is valid.
int getSecond() const
Get the second.
int daysInMonth() const
Calculate the number of days in this date's month.
void setDOSDateTime(dosdatetime_t datetime)
Set the DOSDateTime value as is.
int getMonth() const
Get the month.
static dosdatetime_t const g_max_dosdatetime
static dosdatetime_t const g_min_dosdatetime
void setSecond(int second)
Set the second.
void setYear(int year)
Set the year.
void setHour(int hour)
Set the hour.
void setMonth(int month)
Set the month.
void setUnixTimestamp(std::time_t unix_timestamp)
Set the DOSDateTime value from a Unix timestamp.
int getMDay() const
Get the day of the month.
int getYear() const
Get the year.
void setMDay(int mday)
Set the day of the month.
void setMinute(int minute)
Set the minute.
int getHour() const
Get the hour.
An InvalidException is used when invalid data is provided.
Define a type to manage date and time in MS-DOS format.
int const g_days_in_month[12]
Number of days in a month.
The zipios namespace includes the Zipios library definitions.
DOSDateTime::dosdatetime_t m_second
DOSDateTime::dosdatetime_t m_minute
Union used to convert the uint32_t to fields and vice versa.
DOSDateTime::dosdatetime_t m_dosdatetime
struct zipios::dosdatetime_convert_t::fields m_fields
Various exceptions used throughout the Zipios library, all based on zipios::Exception.