zipios  2.2.0
Zipios -- a small C++ library that provides easy access to .zip files.
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 Made to Order Software Corporation
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 
35 #include "zipios/dosdatetime.hpp"
36 
38 
39 
40 namespace zipios
41 {
42 
43 
45 DOSDateTime::dosdatetime_t const DOSDateTime::g_max_dosdatetime; // Dec 31, 2107 23:59:59
46 
47 
48 
49 
60 {
62  struct fields
63  {
64  DOSDateTime::dosdatetime_t m_second : 5; // WARNING: the precision is every 2 seconds (0, 2, 4, etc.)
70  } m_fields;
71 };
72 
73 
74 
75 namespace
76 {
77 
83 int const g_days_in_month[12] = {
84  /* Jan */ 31,
85  /* Feb */ 0, // special handling
86  /* Mar */ 31,
87  /* Apr */ 30,
88  /* May */ 31,
89  /* Jun */ 30,
90  /* Jul */ 31,
91  /* Aug */ 31,
92  /* Sep */ 30,
93  /* Oct */ 31,
94  /* Nov */ 30,
95  /* Dec */ 31
96 };
97 
98 
99 int const g_ydays[12] = {
100  /* Jan */ 0,
101  /* Feb */ 31,
102  /* Mar */ 31 + 0, // special handling
103  /* Apr */ 31 + 0 + 31,
104  /* May */ 31 + 0 + 31 + 30,
105  /* Jun */ 31 + 0 + 31 + 30 + 31,
106  /* Jul */ 31 + 0 + 31 + 30 + 31 + 30,
107  /* Aug */ 31 + 0 + 31 + 30 + 31 + 30 + 31,
108  /* Sep */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31,
109  /* Oct */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
110  /* Nov */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
111  /* Dec */ 31 + 0 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
112 };
113 
114 
115 }
116 
117 
118 
140 {
143  return conv.m_fields.m_second < 30 // remember we only keep `sec / 2` in a DOS time field
144  && conv.m_fields.m_minute < 60
145  && conv.m_fields.m_hour < 24
146  && conv.m_fields.m_mday > 0
147  && conv.m_fields.m_mday <= daysInMonth()
148  && conv.m_fields.m_month > 0
149  && conv.m_fields.m_month < 13;
150 }
151 
152 
174 {
177 
178  if(conv.m_fields.m_month == 0
179  || conv.m_fields.m_month > 12)
180  {
181  return -1;
182  }
183 
184  if(conv.m_fields.m_month == 2)
185  {
186  // Feb. depends on the year
187  //
188  int year = conv.m_fields.m_year + 1980;
189 
190  return ((year) % 400) == 0
191  ? 29
192  : (((year) % 100) == 0
193  ? 28
194  : (((year) % 4) == 0
195  ? 29
196  : 28));
197  }
198 
199  return g_days_in_month[conv.m_fields.m_month - 1];
200 }
201 
202 
218 {
221  return conv.m_fields.m_second * 2;
222 }
223 
224 
234 {
237  return conv.m_fields.m_minute;
238 }
239 
240 
250 {
253  return conv.m_fields.m_hour;
254 }
255 
256 
268 {
271  return conv.m_fields.m_mday;
272 }
273 
274 
284 {
287  return conv.m_fields.m_month;
288 }
289 
290 
300 {
303  return conv.m_fields.m_year + 1980;
304 }
305 
306 
329 void DOSDateTime::setSecond(int second)
330 {
331  if(second < 0
332  || second > 59)
333  {
334  throw InvalidException("Second is out of range for an MS-DOS Date & Time object. Range is [0, 59].");
335  }
336 
339  conv.m_fields.m_second = second / 2;
341 }
342 
343 
354 void DOSDateTime::setMinute(int minute)
355 {
356  if(minute < 0
357  || minute > 59)
358  {
359  throw InvalidException("Minute is out of range for an MS-DOS Date & Time object. Range is [0, 59].");
360  }
361 
364  conv.m_fields.m_minute = minute;
366 }
367 
368 
379 void DOSDateTime::setHour(int hour)
380 {
381  if(hour < 0
382  || hour > 23)
383  {
384  throw InvalidException("Hour is out of range for an MS-DOS Date & Time object. Range is [0, 23].");
385  }
386 
389  conv.m_fields.m_hour = hour;
391 }
392 
393 
409 void DOSDateTime::setMDay(int mday)
410 {
411  if(mday < 1
412  || mday > 31)
413  {
414  throw InvalidException("Day of the month is out of range for an MS-DOS Date & Time object. Range is [1, 31].");
415  }
416 
419  conv.m_fields.m_mday = mday;
421 }
422 
423 
433 void DOSDateTime::setMonth(int month)
434 {
435  if(month < 1
436  || month > 12)
437  {
438  throw InvalidException("Month out of range for an MS-DOS Date & Time object. Range is [1, 12].");
439  }
440 
443  conv.m_fields.m_month = month;
445 }
446 
447 
459 void DOSDateTime::setYear(int year)
460 {
461  if(year < 1980
462  || year > 2107)
463  {
464  throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107].");
465  }
466 
469  conv.m_fields.m_year = year - 1980;
471 }
472 
473 
482 {
483  return m_dosdatetime;
484 }
485 
486 
496 {
497  m_dosdatetime = datetime;
498 }
499 
500 
542 void DOSDateTime::setUnixTimestamp(std::time_t unix_timestamp)
543 {
544  // round up to the next second
545  //
546  unix_timestamp += 1;
547  unix_timestamp &= -2;
548 
549  struct tm t;
550  localtime_r(&unix_timestamp, &t);
551 
552 //std::cerr << "test with: " << unix_timestamp << " -- " << t.tm_year
553 // << " (" << (t.tm_year < 1980 - 1900 ? 1 : 0)
554 // << ", " << (t.tm_year > 2107 - 1900 ? 1 : 0)
555 // << ")\n";
556 
557  if(t.tm_year < 1980 - 1900
558  || t.tm_year > 2107 - 1900)
559  {
560  throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107].");
561  }
562 
564  conv.m_fields.m_second = t.tm_sec / 2; // already rounded up to the next second, so just divide by 2 is enough here
565  conv.m_fields.m_minute = t.tm_min;
566  conv.m_fields.m_hour = t.tm_hour;
567  conv.m_fields.m_mday = t.tm_mday;
568  conv.m_fields.m_month = t.tm_mon + 1;
569  conv.m_fields.m_year = t.tm_year + 1900 - 1980;
570 
572 }
573 
574 
591 std::time_t DOSDateTime::getUnixTimestamp() const
592 {
593  if(isValid())
594  {
597 
598  struct tm t;
599  t.tm_sec = conv.m_fields.m_second * 2; // we lost the bottom bit, nothing we can do about it here
600  t.tm_min = conv.m_fields.m_minute;
601  t.tm_hour = conv.m_fields.m_hour;
602  t.tm_mday = conv.m_fields.m_mday;
603  t.tm_mon = conv.m_fields.m_month - 1;
604  t.tm_year = conv.m_fields.m_year + 1980 - 1900;
605  t.tm_wday = 0;
606  t.tm_yday = 0;
607  t.tm_isdst = -1;
608 
609 //std::cerr << "date to Unix timestamp: " << (t.tm_mon + 1) << " " << t.tm_mday << ", " << (t.tm_year + 1900)
610 // << " " << t.tm_hour << ":" << t.tm_min << ":" << t.tm_sec << "\n";
611 
612  if(sizeof(std::time_t) == 4
613  && t.tm_year >= 2038)
614  {
615  // the exact date is Jan 19, 2038 at 03:13:07 UTC
616  // see https://en.wikipedia.org/wiki/Year_2038_problem
617  //
618  // we have no problem with 64 bits, max. year is about 292,000,000,000
619  // although the tm_year is an int, so really we're limited to 2 billion
620  // years, again just fine for a DOS Date is limited to 2107...
621  //
622  throw InvalidException("Year out of range for a 32 bit Unix Timestamp object. Range is (1901, 2038).");
623  }
624 
625  return mktime(&t);
626 
627 // // mktime() makes use of the timezone, here is some code that
628 // // replaces mktime() with a UTC date conversion
629 // //
630 // time_t const year = t.tm_year + 1900;
631 // time_t timestamp = (year - 1970LL) * 31536000LL
632 // + ((year - 1969LL) / 4LL) * 86400LL
633 // - ((year - 1901LL) / 100LL) * 86400LL
634 // + ((year - 1601LL) / 400LL) * 86400LL
635 // + (t.tm_mday + g_ydays[t.tm_mon] - 1) * 86400LL
636 // + t.tm_hour * 3600LL
637 // + t.tm_min * 60LL
638 // + t.tm_sec * 1LL;
639 // if(t.tm_mon >= 2)
640 // {
641 // // add seconds in February
642 // //
643 // timestamp += (year % 400 == 0
644 // ? 29 // for year 2000
645 // : (year % 100 == 0
646 // ? 28 // for year 2100
647 // : (year % 4 == 0
648 // ? 29
649 // : 28))) * 86400LL;
650 // }
651 //
652 // return timestamp;
653  }
654 
655  return 0;
656 }
657 
658 
659 
660 
661 } // zipios namespace
662 
663 // Local Variables:
664 // mode: cpp
665 // indent-tabs-mode: nil
666 // c-basic-offset: 4
667 // tab-width: 4
668 // End:
669 
670 // vim: ts=4 sw=4 et
zipios::anonymous_namespace{dosdatetime.cpp}::g_ydays
const int g_ydays[12]
Definition: dosdatetime.cpp:99
zipios::DOSDateTime::setMonth
void setMonth(int month)
Set the month.
Definition: dosdatetime.cpp:433
zipios::DOSDateTime::getMinute
int getMinute() const
Get the minute.
Definition: dosdatetime.cpp:233
zipios::dosdatetime_convert_t::fields::m_minute
DOSDateTime::dosdatetime_t m_minute
Definition: dosdatetime.cpp:65
zipios::DOSDateTime::dosdatetime_t
uint32_t dosdatetime_t
Definition: dosdatetime.hpp:47
zipios::dosdatetime_convert_t::fields
Definition: dosdatetime.cpp:62
zipios::DOSDateTime::g_min_dosdatetime
static const dosdatetime_t g_min_dosdatetime
Definition: dosdatetime.hpp:49
zipios::DOSDateTime::g_max_dosdatetime
static const dosdatetime_t g_max_dosdatetime
Definition: dosdatetime.hpp:50
zipios::DOSDateTime::getYear
int getYear() const
Get the year.
Definition: dosdatetime.cpp:299
zipios::DOSDateTime::getUnixTimestamp
std::time_t getUnixTimestamp() const
Retrieve the DOSDateTime as a Unix timestamp.
Definition: dosdatetime.cpp:591
zipios::dosdatetime_convert_t::fields::m_mday
DOSDateTime::dosdatetime_t m_mday
Definition: dosdatetime.cpp:67
zipios::DOSDateTime::setMDay
void setMDay(int mday)
Set the day of the month.
Definition: dosdatetime.cpp:409
zipios::DOSDateTime::getDOSDateTime
dosdatetime_t getDOSDateTime() const
Retrieve the DOSDateTime value as is.
Definition: dosdatetime.cpp:481
zipiosexceptions.hpp
Various exceptions used throughout the Zipios library, all based on zipios::Exception.
dosdatetime.hpp
Define a type to manage date and time in MS-DOS format.
zipios::DOSDateTime::setYear
void setYear(int year)
Set the year.
Definition: dosdatetime.cpp:459
zipios::dosdatetime_convert_t::fields::m_month
DOSDateTime::dosdatetime_t m_month
Definition: dosdatetime.cpp:68
zipios::DOSDateTime::getHour
int getHour() const
Get the hour.
Definition: dosdatetime.cpp:249
zipios::DOSDateTime::setSecond
void setSecond(int second)
Set the second.
Definition: dosdatetime.cpp:329
zipios::dosdatetime_convert_t::m_dosdatetime
DOSDateTime::dosdatetime_t m_dosdatetime
Definition: dosdatetime.cpp:61
zipios::DOSDateTime::getMDay
int getMDay() const
Get the day of the month.
Definition: dosdatetime.cpp:267
zipios::DOSDateTime::setHour
void setHour(int hour)
Set the hour.
Definition: dosdatetime.cpp:379
zipios::DOSDateTime::isValid
bool isValid() const
Check whether this DOS Date & Date is valid.
Definition: dosdatetime.cpp:139
zipios::DOSDateTime::setMinute
void setMinute(int minute)
Set the minute.
Definition: dosdatetime.cpp:354
zipios::anonymous_namespace{dosdatetime.cpp}::g_days_in_month
const int g_days_in_month[12]
Number of days in a month.
Definition: dosdatetime.cpp:83
zipios::DOSDateTime::daysInMonth
int daysInMonth() const
Calculate the number of days in this date's month.
Definition: dosdatetime.cpp:173
zipios::DOSDateTime::m_dosdatetime
dosdatetime_t m_dosdatetime
Definition: dosdatetime.hpp:72
zipios::DOSDateTime::setDOSDateTime
void setDOSDateTime(dosdatetime_t datetime)
Set the DOSDateTime value as is.
Definition: dosdatetime.cpp:495
zipios::dosdatetime_convert_t::fields::m_year
DOSDateTime::dosdatetime_t m_year
Definition: dosdatetime.cpp:69
zipios::dosdatetime_convert_t::fields::m_hour
DOSDateTime::dosdatetime_t m_hour
Definition: dosdatetime.cpp:66
zipios::DOSDateTime::getMonth
int getMonth() const
Get the month.
Definition: dosdatetime.cpp:283
zipios::DOSDateTime::getSecond
int getSecond() const
Get the second.
Definition: dosdatetime.cpp:217
zipios::InvalidException
An InvalidException is used when invalid data is provided.
Definition: zipiosexceptions.hpp:82
zipios::DOSDateTime::setUnixTimestamp
void setUnixTimestamp(std::time_t unix_timestamp)
Set the DOSDateTime value from a Unix timestamp.
Definition: dosdatetime.cpp:542
zipios::dosdatetime_convert_t::m_fields
struct zipios::dosdatetime_convert_t::fields m_fields
zipios::dosdatetime_convert_t
Union used to convert the uint32_t to fields and vice versa.
Definition: dosdatetime.cpp:59
zipios
The zipios namespace includes the Zipios library definitions.
Definition: backbuffer.cpp:35
zipios::dosdatetime_convert_t::fields::m_second
DOSDateTime::dosdatetime_t m_second
Definition: dosdatetime.cpp:64