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

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions