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 
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 
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.)
71  DOSDateTime::dosdatetime_t m_mday : 5; // 1 to 31
72  DOSDateTime::dosdatetime_t m_month : 4; // 1 to 12
73  DOSDateTime::dosdatetime_t m_year : 7; // add 1980
74 #endif
75  } m_fields;
76 };
77 
78 
79 
80 namespace
81 {
82 
88 int 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 
104 int 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 
334 void DOSDateTime::setSecond(int second)
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 
359 void DOSDateTime::setMinute(int minute)
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 
384 void DOSDateTime::setHour(int hour)
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 
414 void DOSDateTime::setMDay(int mday)
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 
438 void DOSDateTime::setMonth(int month)
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 
464 void DOSDateTime::setYear(int year)
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 
487 {
488  return m_dosdatetime;
489 }
490 
491 
501 {
502  m_dosdatetime = datetime;
503 }
504 
505 
547 void 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 
600 std::time_t DOSDateTime::getUnixTimestamp() const
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
Define a type to manage date and time in MS-DOS format.
dosdatetime_t m_dosdatetime
Definition: dosdatetime.hpp:70
DOSDateTime::dosdatetime_t m_dosdatetime
The zipios namespace includes the Zipios library definitions.
Definition: backbuffer.cpp:35
int getMinute() const
Get the minute.
int getMonth() const
Get the month.
void setMDay(int mday)
Set the day of the month.
dosdatetime_t getDOSDateTime() const
Retrieve the DOSDateTime value as is.
Various exceptions used throughout the Zipios library, all based on zipios::Exception.
std::time_t getUnixTimestamp() const
Retrieve the DOSDateTime as a Unix timestamp.
void setUnixTimestamp(std::time_t unix_timestamp)
Set the DOSDateTime value from a Unix timestamp.
static dosdatetime_t const g_max_dosdatetime
Definition: dosdatetime.hpp:48
DOSDateTime::dosdatetime_t m_second
bool isValid() const
Check whether this DOS Date & Date is valid.
int const g_days_in_month[12]
Number of days in a month.
int getMDay() const
Get the day of the month.
int getSecond() const
Get the second.
DOSDateTime::dosdatetime_t m_year
void setMinute(int minute)
Set the minute.
DOSDateTime::dosdatetime_t m_minute
void setSecond(int second)
Set the second.
int daysInMonth() const
Calculate the number of days in this date&#39;s month.
DOSDateTime::dosdatetime_t m_month
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
void setHour(int hour)
Set the hour.
int getYear() const
Get the year.
DOSDateTime::dosdatetime_t m_hour
void setDOSDateTime(dosdatetime_t datetime)
Set the DOSDateTime value as is.
void setMonth(int month)
Set the month.
int getHour() const
Get the hour.
Union used to convert the uint32_t to fields and vice versa.
static dosdatetime_t const g_min_dosdatetime
Definition: dosdatetime.hpp:47