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 < 1908
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
Define a type to manage date and time in MS-DOS format.
std::time_t getUnixTimestamp() const
Retrieve the DOSDateTime as a Unix timestamp.
DOSDateTime::dosdatetime_t m_dosdatetime
Definition: dosdatetime.cpp:61
The zipios namespace includes the Zipios library definitions.
Definition: backbuffer.cpp:35
int getHour() const
Get the hour.
void setMDay(int mday)
Set the day of the month.
Various exceptions used throughout the Zipios library, all based on zipios::Exception.
dosdatetime_t getDOSDateTime() const
Retrieve the DOSDateTime value as is.
void setUnixTimestamp(std::time_t unix_timestamp)
Set the DOSDateTime value from a Unix timestamp.
int getMonth() const
Get the month.
static dosdatetime_t const g_max_dosdatetime
Definition: dosdatetime.hpp:50
DOSDateTime::dosdatetime_t m_second
Definition: dosdatetime.cpp:64
bool isValid() const
Check whether this DOS Date & Date is valid.
int daysInMonth() const
Calculate the number of days in this date&#39;s month.
int const g_days_in_month[12]
Number of days in a month.
Definition: dosdatetime.cpp:83
DOSDateTime::dosdatetime_t m_year
Definition: dosdatetime.cpp:69
void setMinute(int minute)
Set the minute.
int getMDay() const
Get the day of the month.
DOSDateTime::dosdatetime_t m_minute
Definition: dosdatetime.cpp:65
void setSecond(int second)
Set the second.
int getSecond() const
Get the second.
DOSDateTime::dosdatetime_t m_month
Definition: dosdatetime.cpp:68
int getMinute() const
Get the minute.
void setYear(int year)
Set the year.
struct zipios::dosdatetime_convert_t::fields m_fields
An InvalidException is used when invalid data is provided.
DOSDateTime::dosdatetime_t m_mday
Definition: dosdatetime.cpp:67
void setHour(int hour)
Set the hour.
int getYear() const
Get the year.
DOSDateTime::dosdatetime_t m_hour
Definition: dosdatetime.cpp:66
void setDOSDateTime(dosdatetime_t datetime)
Set the DOSDateTime value as is.
void setMonth(int month)
Set the month.
Union used to convert the uint32_t to fields and vice versa.
Definition: dosdatetime.cpp:59
static dosdatetime_t const g_min_dosdatetime
Definition: dosdatetime.hpp:49