zipios 2.3.2
Zipios -- a small C++ library that provides 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
465{
466 if(year < 1980
467 || year > 2107)
468 {
469 throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107] (1).");
470 }
471
474 conv.m_fields.m_year = year - 1980;
476}
477
478
490
491
501{
502 m_dosdatetime = datetime;
503}
504
505
547void DOSDateTime::setUnixTimestamp(std::time_t unix_timestamp)
548{
549 // round up to the next second
550 //
551 unix_timestamp += 1;
552 unix_timestamp &= ~1;
553
554 struct tm t;
555#ifdef ZIPIOS_WINDOWS
556 localtime_s(&t, &unix_timestamp);
557#else
558 localtime_r(&unix_timestamp, &t);
559#endif
560
561//std::cerr << "test with: " << unix_timestamp << " -- " << t.tm_year
562// << " (" << (t.tm_year < 1980 - 1900 ? 1 : 0)
563// << ", " << (t.tm_year > 2107 - 1900 ? 1 : 0)
564// << ")\n";
565
566 if(t.tm_year < 1980 - 1900
567 || t.tm_year > 2107 - 1900)
568 {
569 throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107] (2).");
570 }
571
573 conv.m_fields.m_second = t.tm_sec / 2; // already rounded up to the next second, so just divide by 2 is enough here
574 conv.m_fields.m_minute = t.tm_min;
575 conv.m_fields.m_hour = t.tm_hour;
576 conv.m_fields.m_mday = t.tm_mday;
577 conv.m_fields.m_month = t.tm_mon + 1;
578 conv.m_fields.m_year = t.tm_year + 1900 - 1980;
579
581}
582
583
601{
602 if(isValid())
603 {
606
607 struct tm t;
608 t.tm_sec = conv.m_fields.m_second * 2; // we lost the bottom bit, nothing we can do about it here
609 t.tm_min = conv.m_fields.m_minute;
610 t.tm_hour = conv.m_fields.m_hour;
611 t.tm_mday = conv.m_fields.m_mday;
612 t.tm_mon = conv.m_fields.m_month - 1;
613 t.tm_year = conv.m_fields.m_year + 1980 - 1900;
614 t.tm_wday = 0;
615 t.tm_yday = 0;
616 t.tm_isdst = -1;
617
618//std::cerr << "date to Unix timestamp: " << (t.tm_mon + 1) << " " << t.tm_mday << ", " << (t.tm_year + 1900)
619// << " " << t.tm_hour << ":" << t.tm_min << ":" << t.tm_sec << "\n";
620
621 if(sizeof(std::time_t) == 4
622 && t.tm_year >= 2038)
623 {
624 // the exact date is Jan 19, 2038 at 03:13:07 UTC
625 // see https://en.wikipedia.org/wiki/Year_2038_problem
626 //
627 // we have no problem with 64 bits, max. year is about 292,000,000,000
628 // although the tm_year is an int, so really we're limited to 2 billion
629 // years, again just fine for a DOS Date is limited to 2107...
630 //
631 throw InvalidException("Year out of range for a 32 bit Unix Timestamp object. Range is (1901, 2038).");
632 }
633
634 // the zip file format expects dates in local time, not UTC
635 // so I use mktime() directly
636 //
637 return mktime(&t);
638
639// // mktime() makes use of the timezone, here is some code that
640// // replaces mktime() with a UTC date conversion
641// //
642// time_t const year = t.tm_year + 1900;
643// time_t timestamp = (year - 1970LL) * 31536000LL
644// + ((year - 1969LL) / 4LL) * 86400LL
645// - ((year - 1901LL) / 100LL) * 86400LL
646// + ((year - 1601LL) / 400LL) * 86400LL
647// + (t.tm_mday + g_ydays[t.tm_mon] - 1) * 86400LL
648// + t.tm_hour * 3600LL
649// + t.tm_min * 60LL
650// + t.tm_sec * 1LL;
651// if(t.tm_mon >= 2)
652// {
653// // add seconds in February
654// //
655// timestamp += (year % 400 == 0
656// ? 29 // for year 2000
657// : (year % 100 == 0
658// ? 28 // for year 2100
659// : (year % 4 == 0
660// ? 29
661// : 28))) * 86400LL;
662// }
663//
664// return timestamp;
665 }
666
667 return 0;
668}
669
670
671
672
673} // zipios namespace
674
675// Local Variables:
676// mode: cpp
677// indent-tabs-mode: nil
678// c-basic-offset: 4
679// tab-width: 4
680// End:
681
682// 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.