LCOV - code coverage report
Current view: top level - src - dosdatetime.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 122 122 100.0 %
Date: 2019-04-24 14:10:30 Functions: 20 20 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      21             : /** \file
      22             :  * \brief Implementation of the zipios::DOSDateTime class.
      23             :  *
      24             :  * This file is the implementation of the zipios::DOSDateTime class
      25             :  * which converts dates between Unix timestamps and DOS Date Time
      26             :  * timestamps.
      27             :  *
      28             :  * Keep in mind that the dates in a zip file use your local time,
      29             :  * whatever that is at the time you create the file. The get/set Unix
      30             :  * timestamp functions adjust the date to UTC as required.
      31             :  *
      32             :  * \sa https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-dosdatetimetofiletime
      33             :  */
      34             : 
      35             : #include "zipios/dosdatetime.hpp"
      36             : 
      37             : #include "zipios/zipiosexceptions.hpp"
      38             : 
      39             : 
      40             : namespace zipios
      41             : {
      42             : 
      43             : 
      44             : DOSDateTime::dosdatetime_t const  DOSDateTime::g_min_dosdatetime;     // Jan  1, 1980  00:00:00
      45             : DOSDateTime::dosdatetime_t const  DOSDateTime::g_max_dosdatetime;     // Dec 31, 2107  23:59:59
      46             : 
      47             : 
      48             : 
      49             : 
      50             : /** \brief Union used to convert the uint32_t to fields and vice versa.
      51             :  *
      52             :  * This union is used by the functions below to convert the basic
      53             :  * uint32_t dosdatetime_t values in a list of 6 fields.
      54             :  *
      55             :  * \todo
      56             :  * The fields are endian specific, we need to find the correct #ifdef
      57             :  * and swap the fields so it works on bigendian machines
      58             :  */
      59             : union dosdatetime_convert_t
      60             : {
      61             :     DOSDateTime::dosdatetime_t      m_dosdatetime;
      62             :     struct fields
      63             :     {
      64             :         DOSDateTime::dosdatetime_t      m_second : 5;   // WARNING: the precision is every 2 seconds (0, 2, 4, etc.)
      65             :         DOSDateTime::dosdatetime_t      m_minute : 6;
      66             :         DOSDateTime::dosdatetime_t      m_hour   : 5;
      67             :         DOSDateTime::dosdatetime_t      m_mday   : 5;  // 1 to 31
      68             :         DOSDateTime::dosdatetime_t      m_month  : 4;  // 1 to 12
      69             :         DOSDateTime::dosdatetime_t      m_year   : 7;  // add 1980
      70             :     } m_fields;
      71             : };
      72             : 
      73             : 
      74             : 
      75             : namespace
      76             : {
      77             : 
      78             : /** \brief Number of days in a month.
      79             :  *
      80             :  * This table is used in the daysInMonth() function to determine the number
      81             :  * of days in the month. It is ignored if the month is February.
      82             :  */
      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             : 
     119             : /** \brief Check whether this DOS Date & Date is valid.
     120             :  *
     121             :  * This function verifies to see whether the DOS Date & Time it holds is
     122             :  * valid. By default, the value is set to zero which represents an invalid
     123             :  * date (_not set_).
     124             :  *
     125             :  * \note
     126             :  * Remember that Zip file Date & Time are saved in an old MS-DOS format
     127             :  * which did not respect UTC. It instead represents local time. This
     128             :  * function returns true when the local time is valid, since the same
     129             :  * time will be shared around the globe, it will alway sbe considered
     130             :  * valid, but the Unix timestamp can look like a mismatch (the minimum
     131             :  * and maximum possible time stamps represented in a Unix time_t variable
     132             :  * will vary depending on your timezone settings.)
     133             :  *
     134             :  * \return true if the date is considered valid (represents an actual date
     135             :  * and time).
     136             :  *
     137             :  * \sa daysInMonth()
     138             :  */
     139      668293 : bool DOSDateTime::isValid() const
     140             : {
     141             :     dosdatetime_convert_t conv;
     142      668293 :     conv.m_dosdatetime = m_dosdatetime;
     143      668293 :     return conv.m_fields.m_second < 30  // remember we only keep `sec / 2` in a DOS time field
     144      668293 :         && conv.m_fields.m_minute < 60
     145      668293 :         && conv.m_fields.m_hour < 24
     146      668293 :         && conv.m_fields.m_mday > 0
     147      668149 :         && conv.m_fields.m_mday <= daysInMonth()
     148      668149 :         && conv.m_fields.m_month > 0
     149     1336442 :         && conv.m_fields.m_month < 13;
     150             : }
     151             : 
     152             : 
     153             : /** \brief Calculate the number of days in this date's month.
     154             :  *
     155             :  * This function uses this object's current month to determine how
     156             :  * many days are expected in that month.
     157             :  *
     158             :  * If the month field is invalid (not a number between 1 and 12 inclusive)
     159             :  * then the function returns -1 to indicate an error.
     160             :  *
     161             :  * The number of days is always 30 or 31 except for the month of February
     162             :  * in which case the function may return 28 or 29. For this reason, your
     163             :  * date must have the month and year you want to use to get a valid result
     164             :  * when calling this function.
     165             :  *
     166             :  * \note
     167             :  * This function is used to verify that the date month day is valid.
     168             :  *
     169             :  * \return The number of days in this date's current month.
     170             :  *
     171             :  * \sa isValid()
     172             :  */
     173      668150 : int DOSDateTime::daysInMonth() const
     174             : {
     175             :     dosdatetime_convert_t conv;
     176      668150 :     conv.m_dosdatetime = m_dosdatetime;
     177             : 
     178      668150 :     if(conv.m_fields.m_month == 0
     179      668149 :     || conv.m_fields.m_month > 12)
     180             :     {
     181           1 :         return -1;
     182             :     }
     183             : 
     184      668149 :     if(conv.m_fields.m_month == 2)
     185             :     {
     186             :         // Feb. depends on the year
     187             :         //
     188       19038 :         int year = conv.m_fields.m_year + 1980;
     189             : 
     190       19038 :         return ((year) % 400) == 0
     191             :                     ? 29
     192       18877 :                     : (((year) % 100) == 0
     193             :                         ? 28
     194       18727 :                         : (((year) % 4) == 0
     195             :                             ? 29
     196       56642 :                             : 28));
     197             :     }
     198             : 
     199      649111 :     return g_days_in_month[conv.m_fields.m_month - 1];
     200             : }
     201             : 
     202             : 
     203             : /** \brief Get the second.
     204             :  *
     205             :  * This function returns the second this DOSDateTime object represents.
     206             :  *
     207             :  * The second is between 0 and 59 inclusive.
     208             :  *
     209             :  * \note
     210             :  * The DOSDateTime format only supports 5 bits for seconds. In other words,
     211             :  * the number can't go all the way from 0 to 59. Instead, the second
     212             :  * is saved without bit 0. In other words, only an even number of second
     213             :  * is saved. In other words, 0, 2, 4, 6, up to 58.
     214             :  *
     215             :  * \return The second this DOS Date & Time represents.
     216             :  */
     217         142 : int DOSDateTime::getSecond() const
     218             : {
     219             :     dosdatetime_convert_t conv;
     220         142 :     conv.m_dosdatetime = m_dosdatetime;
     221         142 :     return conv.m_fields.m_second * 2;
     222             : }
     223             : 
     224             : 
     225             : /** \brief Get the minute.
     226             :  *
     227             :  * This function returns the minute this DOSDateTime object represents.
     228             :  *
     229             :  * The minute is between 0 and 59 inclusive.
     230             :  *
     231             :  * \return The minute this DOS Date & Time represents.
     232             :  */
     233         142 : int DOSDateTime::getMinute() const
     234             : {
     235             :     dosdatetime_convert_t conv;
     236         142 :     conv.m_dosdatetime = m_dosdatetime;
     237         142 :     return conv.m_fields.m_minute;
     238             : }
     239             : 
     240             : 
     241             : /** \brief Get the hour.
     242             :  *
     243             :  * This function returns the hour this DOSDateTime object represents.
     244             :  *
     245             :  * The hour is between 0 and 23 inclusive.
     246             :  *
     247             :  * \return The hour this DOS Date & Time represents.
     248             :  */
     249         106 : int DOSDateTime::getHour() const
     250             : {
     251             :     dosdatetime_convert_t conv;
     252         106 :     conv.m_dosdatetime = m_dosdatetime;
     253         106 :     return conv.m_fields.m_hour;
     254             : }
     255             : 
     256             : 
     257             : /** \brief Get the day of the month.
     258             :  *
     259             :  * This function returns the day of month this DOSDateTime object represents.
     260             :  *
     261             :  * The day is between 1 and 31. To know whether a day is valid, use the
     262             :  * daysInMonth() function which returns the maximum number of days for a
     263             :  * given month.
     264             :  *
     265             :  * \return The day of the month this DOS Date & Time represents.
     266             :  */
     267         115 : int DOSDateTime::getMDay() const
     268             : {
     269             :     dosdatetime_convert_t conv;
     270         115 :     conv.m_dosdatetime = m_dosdatetime;
     271         115 :     return conv.m_fields.m_mday;
     272             : }
     273             : 
     274             : 
     275             : /** \brief Get the month.
     276             :  *
     277             :  * This function returns the month this DOSDateTime object represents.
     278             :  *
     279             :  * The month is between 1 and 12.
     280             :  *
     281             :  * \return The month this DOS Date & Time represents.
     282             :  */
     283          95 : int DOSDateTime::getMonth() const
     284             : {
     285             :     dosdatetime_convert_t conv;
     286          95 :     conv.m_dosdatetime = m_dosdatetime;
     287          95 :     return conv.m_fields.m_month;
     288             : }
     289             : 
     290             : 
     291             : /** \brief Get the year.
     292             :  *
     293             :  * This function returns the year this DOSDateTime object represents.
     294             :  *
     295             :  * The year is limited between 1980 and 2107.
     296             :  *
     297             :  * \return The year this DOS Date & Time represents.
     298             :  */
     299        2274 : int DOSDateTime::getYear() const
     300             : {
     301             :     dosdatetime_convert_t conv;
     302        2274 :     conv.m_dosdatetime = m_dosdatetime;
     303        2274 :     return conv.m_fields.m_year + 1980;
     304             : }
     305             : 
     306             : 
     307             : /** \brief Set the second.
     308             :  *
     309             :  * This function can be used to only change the second of a DOSDateTime
     310             :  * object.
     311             :  *
     312             :  * \note
     313             :  * The DOSDateTime format only supports 5 bits for seconds. In other words,
     314             :  * the number can't go all the way from 0 to 59. Instead, the second
     315             :  * is saved without bit 0. In other words, only an even number of second
     316             :  * is saved. In other words, 0, 2, 4, 6, up to 58.
     317             :  *
     318             :  * \important
     319             :  * Leap seconds are not supported. Trying to pass 60 to this function
     320             :  * raises an InvalidException error.
     321             :  *
     322             :  * \exception InvalidException
     323             :  * The second is expected to be set to a number between 0 and 59 inclusive.
     324             :  * No exception is raised if an odd number is passed down. Bit 0 is simply
     325             :  * ignored.
     326             :  *
     327             :  * \param[in] second  The new DOSDateTime number of seconds.
     328             :  */
     329         144 : void DOSDateTime::setSecond(int second)
     330             : {
     331         144 :     if(second < 0
     332         124 :     || second > 59)
     333             :     {
     334          41 :         throw InvalidException("Second is out of range for an MS-DOS Date & Time object. Range is [0, 59].");
     335             :     }
     336             : 
     337             :     dosdatetime_convert_t conv;
     338         103 :     conv.m_dosdatetime = m_dosdatetime;
     339         103 :     conv.m_fields.m_second = second / 2;
     340         103 :     m_dosdatetime = conv.m_dosdatetime;
     341         103 : }
     342             : 
     343             : 
     344             : /** \brief Set the minute.
     345             :  *
     346             :  * This function can be used to only change the minute of a DOSDateTime
     347             :  * object.
     348             :  *
     349             :  * \exception InvalidException
     350             :  * The minute is expected to be set to a number between 0 and 59 inclusive.
     351             :  *
     352             :  * \param[in] minute  The new DOSDateTime number of minutes.
     353             :  */
     354         144 : void DOSDateTime::setMinute(int minute)
     355             : {
     356         144 :     if(minute < 0
     357         124 :     || minute > 59)
     358             :     {
     359          41 :         throw InvalidException("Minute is out of range for an MS-DOS Date & Time object. Range is [0, 59].");
     360             :     }
     361             : 
     362             :     dosdatetime_convert_t conv;
     363         103 :     conv.m_dosdatetime = m_dosdatetime;
     364         103 :     conv.m_fields.m_minute = minute;
     365         103 :     m_dosdatetime = conv.m_dosdatetime;
     366         103 : }
     367             : 
     368             : 
     369             : /** \brief Set the hour.
     370             :  *
     371             :  * This function can be used to only change the hour of a DOSDateTime
     372             :  * object.
     373             :  *
     374             :  * \exception InvalidException
     375             :  * The hour is expected to be set to a number between 0 and 23 inclusive.
     376             :  *
     377             :  * \param[in] hour  The new DOSDateTime number of hours.
     378             :  */
     379         108 : void DOSDateTime::setHour(int hour)
     380             : {
     381         108 :     if(hour < 0
     382          88 :     || hour > 23)
     383             :     {
     384          41 :         throw InvalidException("Hour is out of range for an MS-DOS Date & Time object. Range is [0, 23].");
     385             :     }
     386             : 
     387             :     dosdatetime_convert_t conv;
     388          67 :     conv.m_dosdatetime = m_dosdatetime;
     389          67 :     conv.m_fields.m_hour = hour;
     390          67 :     m_dosdatetime = conv.m_dosdatetime;
     391          67 : }
     392             : 
     393             : 
     394             : /** \brief Set the day of the month.
     395             :  *
     396             :  * This function can be used to only change the day of the month of a
     397             :  * DOSDateTime object.
     398             :  *
     399             :  * \exception InvalidException
     400             :  * The day of the month is expected to be set to a number between 1 and 31
     401             :  * inclusive. An exception is raised if out of that range. Note that no
     402             :  * exception is raised if the day is invalid for a certain month because
     403             :  * at this point we can't be sure what the month will be. To verify once
     404             :  * you are done setting all the individual values, call isValid() to do
     405             :  * a final verification.
     406             :  *
     407             :  * \param[in] mday  The new DOSDateTime day of the month.
     408             :  */
     409         117 : void DOSDateTime::setMDay(int mday)
     410             : {
     411         117 :     if(mday < 1
     412          96 :     || mday > 31)
     413             :     {
     414          42 :         throw InvalidException("Day of the month is out of range for an MS-DOS Date & Time object. Range is [1, 31].");
     415             :     }
     416             : 
     417             :     dosdatetime_convert_t conv;
     418          75 :     conv.m_dosdatetime = m_dosdatetime;
     419          75 :     conv.m_fields.m_mday = mday;
     420          75 :     m_dosdatetime = conv.m_dosdatetime;
     421          75 : }
     422             : 
     423             : 
     424             : /** \brief Set the month.
     425             :  *
     426             :  * This function can be used to only change the month of a DOSDateTime object.
     427             :  *
     428             :  * \exception InvalidException
     429             :  * The month is expected to be set to a number between 1 and 12 inclusive.
     430             :  *
     431             :  * \param[in] month  The ne DOSDateTime month.
     432             :  */
     433          97 : void DOSDateTime::setMonth(int month)
     434             : {
     435          97 :     if(month < 1
     436          76 :     || month > 12)
     437             :     {
     438          42 :         throw InvalidException("Month out of range for an MS-DOS Date & Time object. Range is [1, 12].");
     439             :     }
     440             : 
     441             :     dosdatetime_convert_t conv;
     442          55 :     conv.m_dosdatetime = m_dosdatetime;
     443          55 :     conv.m_fields.m_month = month;
     444          55 :     m_dosdatetime = conv.m_dosdatetime;
     445          55 : }
     446             : 
     447             : 
     448             : /** \brief Set the year.
     449             :  *
     450             :  * This function can be used to only change the year this DOSDateTime object
     451             :  * represents.
     452             :  *
     453             :  * \exception InvalidException
     454             :  * The year is limited between 1980 and 2107. This exception is raised if the
     455             :  * year to out of this range.
     456             :  *
     457             :  * \return The year this DOS Date & Time represents.
     458             :  */
     459        2276 : void DOSDateTime::setYear(int year)
     460             : {
     461        2276 :     if(year < 1980
     462        1296 :     || year > 2107)
     463             :     {
     464        1073 :         throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107].");
     465             :     }
     466             : 
     467             :     dosdatetime_convert_t conv;
     468        1203 :     conv.m_dosdatetime = m_dosdatetime;
     469        1203 :     conv.m_fields.m_year = year - 1980;
     470        1203 :     m_dosdatetime = conv.m_dosdatetime;
     471        1203 : }
     472             : 
     473             : 
     474             : /** \brief Retrieve the DOSDateTime value as is.
     475             :  *
     476             :  * This function returns the DOSDateTime value as is. It can then be used
     477             :  * in the zip file.
     478             :  *
     479             :  * \return The dosdatetime_t timestamp.
     480             :  */
     481     1027824 : DOSDateTime::dosdatetime_t DOSDateTime::getDOSDateTime() const
     482             : {
     483     1027824 :     return m_dosdatetime;
     484             : }
     485             : 
     486             : 
     487             : /** \brief Set the DOSDateTime value as is.
     488             :  *
     489             :  * This function sets this DOSDateTime object's timestamp to the specified
     490             :  * value. Any value is accepted by this function. To verify that the new
     491             :  * value is a valid date, use the isValid() function.
     492             :  *
     493             :  * \param[in] datetime  The DOS Date & Time value.
     494             :  */
     495      560902 : void DOSDateTime::setDOSDateTime(dosdatetime_t datetime)
     496             : {
     497      560902 :     m_dosdatetime = datetime;
     498      560902 : }
     499             : 
     500             : 
     501             : /** \brief Set the DOSDateTime value from a Unix timestamp.
     502             :  *
     503             :  * This function accepts a Unix timestamp that gets converted to a
     504             :  * DOSDateTime object.
     505             :  *
     506             :  * A Unix timestamp is a time_t number representing seconds. 0 represents
     507             :  * the date Jan 1, 1970 at 00:00:00.
     508             :  *
     509             :  * The smallest Unix timestamp that can be represented in a DOSDateTime
     510             :  * object is 315532800 (0x12cea600) in UTC. Since the timestamp gets
     511             :  * converted to local time, though, the boundaries vary with the user's
     512             :  * timezone.
     513             :  *
     514             :  * The minimum date is represented as 0x00210000 in a DOSDateTime object.
     515             :  * This represents Jan 1, 1980 at 00:00:00, local time.
     516             :  *
     517             :  * The largest Unix timestamp that can be represented in a DOSDateTime
     518             :  * object is 4354819199 (0x10391447f) in UTC, since the timestamp gets
     519             :  * converted to local time, though, the boundaries vary with the user's
     520             :  * timezone.
     521             :  *
     522             :  * The maximum date is represented as 0xff9fbf7d in a DOSDateTime object.
     523             :  * Note that the Unix timestamp requires a 64 bit `time_t` representation
     524             :  * in order to reach the maximum DOSDateTime. With a 32 bit number,
     525             :  * the maximum is around 2037, about 70 years short. This maximum date
     526             :  * represents Dec 31, 2107 at 23:59:59, local time.
     527             :  *
     528             :  * \important
     529             :  * The DOSDateTime object only holds even seconds. Odd seconds are lost
     530             :  * at the time this function gets called.
     531             :  *
     532             :  * \exception InvalidException
     533             :  * In 64 bits, a Unix timestamp can represent very large dates in both
     534             :  * directions (in the past and future.) If the Unix timestamp represents a
     535             :  * local date and time before Jan 1, 1980 at 00:00:00 or after
     536             :  * Dec 31, 2107 at 23:59:58, then this function raises this exception.
     537             :  * It is likely to raise an exception on Dec 31, 2107 at 23:59:59 because
     538             :  * we round the time to the next even second.
     539             :  *
     540             :  * \param[in] unix_timestamp  The time and stamp in Unix format.
     541             :  */
     542     1047271 : void DOSDateTime::setUnixTimestamp(std::time_t unix_timestamp)
     543             : {
     544             :     // round up to the next second
     545             :     //
     546     1047271 :     unix_timestamp += 1;
     547     1047271 :     unix_timestamp &= -2;
     548             : 
     549             :     struct tm t;
     550     1047271 :     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     1047271 :     if(t.tm_year < 1980 - 1900
     558     1027961 :     || t.tm_year > 2107 - 1900)
     559             :     {
     560       19767 :         throw InvalidException("Year out of range for an MS-DOS Date & Time object. Range is [1980, 2107].");
     561             :     }
     562             : 
     563             :     dosdatetime_convert_t conv;
     564     1027504 :     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     1027504 :     conv.m_fields.m_minute = t.tm_min;
     566     1027504 :     conv.m_fields.m_hour   = t.tm_hour;
     567     1027504 :     conv.m_fields.m_mday   = t.tm_mday;
     568     1027504 :     conv.m_fields.m_month  = t.tm_mon + 1;
     569     1027504 :     conv.m_fields.m_year   = t.tm_year + 1900 - 1980;
     570             : 
     571     1027504 :     m_dosdatetime = conv.m_dosdatetime;
     572     1027504 : }
     573             : 
     574             : 
     575             : /** \brief Retrieve the DOSDateTime as a Unix timestamp.
     576             :  *
     577             :  * This function returns the DOSDateTime converted to a Unix timestamp.
     578             :  * On 64 bit platforms, all DOSDateTime can be converted to a Unix timestamp.
     579             :  * On a 32 bit platform, however, dates after 2037 can't be represented by
     580             :  * the Unix timestamp so this function throws (note that we did not check
     581             :  * the exact threshold because at this point I don't think it's too important.)
     582             :  *
     583             :  * \exception InvalidException
     584             :  * On 32 bit platform, dates that can't be represented in a Unix timestamp
     585             :  * throw this exception.
     586             :  *
     587             :  * \return The Unix timestamp representing the DOSDateTime object.
     588             :  *
     589             :  * \sa setUnixTimestamp()
     590             :  */
     591      668085 : std::time_t DOSDateTime::getUnixTimestamp() const
     592             : {
     593      668085 :     if(isValid())
     594             :     {
     595             :         dosdatetime_convert_t conv;
     596      668065 :         conv.m_dosdatetime = m_dosdatetime;
     597             : 
     598             :         struct tm t;
     599      668065 :         t.tm_sec   = conv.m_fields.m_second * 2;      // we lost the bottom bit, nothing we can do about it here
     600      668065 :         t.tm_min   = conv.m_fields.m_minute;
     601      668065 :         t.tm_hour  = conv.m_fields.m_hour;
     602      668065 :         t.tm_mday  = conv.m_fields.m_mday;
     603      668065 :         t.tm_mon   = conv.m_fields.m_month - 1;
     604      668065 :         t.tm_year  = conv.m_fields.m_year + 1980 - 1900;
     605      668065 :         t.tm_wday  = 0;
     606      668065 :         t.tm_yday  = 0;
     607      668065 :         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      668065 :         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          20 :     return 0;
     656             : }
     657             : 
     658             : 
     659             : 
     660             : 
     661           3 : } // 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

Generated by: LCOV version 1.12