diff --git a/src/lib/util/coreutil.cpp b/src/lib/util/coreutil.cpp index 8ff9585d476..bd10802ef5a 100644 --- a/src/lib/util/coreutil.cpp +++ b/src/lib/util/coreutil.cpp @@ -58,39 +58,6 @@ uint32_t bcd_2_dec(uint32_t a) -/*************************************************************************** - GREGORIAN CALENDAR HELPERS -***************************************************************************/ - -int gregorian_is_leap_year(int year) -{ - return !((year % 100) ? (year % 4) : (year % 400)); -} - - -/* months are one counted */ - -/** - * @fn int gregorian_days_in_month(int month, int year) - * - * @brief Gregorian days in month. - * - * @param month The month. - * @param year The year. - * - * @return An int. - */ - -int gregorian_days_in_month(int month, int year) -{ - assert(month >= 1 && month <= 12); - - int days[] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; - days[1] += gregorian_is_leap_year(year) ? 1 : 0; - return days[month-1]; -} - - /*************************************************************************** MISC ***************************************************************************/ diff --git a/src/lib/util/coreutil.h b/src/lib/util/coreutil.h index 167f696970e..4e5526fc6f0 100644 --- a/src/lib/util/coreutil.h +++ b/src/lib/util/coreutil.h @@ -29,8 +29,48 @@ uint32_t bcd_2_dec(uint32_t a); GREGORIAN CALENDAR HELPERS ***************************************************************************/ -int gregorian_is_leap_year(int year); -int gregorian_days_in_month(int month, int year); +constexpr bool gregorian_is_leap_year(int year) +{ + return !((year % 100) ? (year % 4) : (year % 400)); +} + + + +//------------------------------------------------- +// gregorian_days_in_month - given a year and a one-counted +// month, return the amount of days in that month +//------------------------------------------------- + +inline int gregorian_days_in_month(int month, int year) +{ + int result; + switch (month) + { + case 4: case 6: + case 9: case 11: + // Thirty days have September, April, June, and November. + result = 30; + break; + + case 1: case 3: + case 5: case 7: + case 8: case 10: + case 12: + // All the rest have Thirty One + result = 31; + break; + + case 2: + // No exceptions, but save one: Twenty Eight hath February + // in fine, and each leap year Twenty Nine + result = gregorian_is_leap_year(year) ? 29 : 28; + break; + + default: + throw false; + } + return result; +} /*************************************************************************** diff --git a/src/lib/util/timeconv.cpp b/src/lib/util/timeconv.cpp index e0d41fbde75..20f4e29a03e 100644 --- a/src/lib/util/timeconv.cpp +++ b/src/lib/util/timeconv.cpp @@ -14,47 +14,48 @@ namespace util { -namespace { /*************************************************************************** PROTOTYPES ***************************************************************************/ -util::ntfs_duration calculate_ntfs_offset(); +static std::chrono::system_clock::duration calculate_system_clock_adjustment(); /*************************************************************************** GLOBAL VARIABLES ***************************************************************************/ -util::ntfs_duration f_ntfs_offset(calculate_ntfs_offset()); +std::chrono::system_clock::duration system_clock_adjustment(calculate_system_clock_adjustment()); /*************************************************************************** IMPLEMENTATION ***************************************************************************/ -util::ntfs_duration calculate_ntfs_offset() +static std::chrono::system_clock::duration calculate_system_clock_adjustment() { constexpr auto days_in_year(365); constexpr auto days_in_four_years((days_in_year * 4) + 1); constexpr auto days_in_century((days_in_four_years * 25) - 1); constexpr auto days_in_four_centuries((days_in_century * 4) + 1); - constexpr ntfs_duration day(std::chrono::hours(24)); - constexpr ntfs_duration year(day * days_in_year); - constexpr ntfs_duration four_years(day * days_in_four_years); - constexpr ntfs_duration century(day * days_in_century); - constexpr ntfs_duration four_centuries(day * days_in_four_centuries); + // can't use std::chrono::system_clock::duration here, out of fear of integer overflow + typedef std::chrono::duration > int64_second_duration; + constexpr int64_second_duration day(std::chrono::hours(24)); + constexpr int64_second_duration year(day * days_in_year); + constexpr int64_second_duration four_years(day * days_in_four_years); + constexpr int64_second_duration century(day * days_in_century); + constexpr int64_second_duration four_centuries(day * days_in_four_centuries); std::time_t const zero(0); std::tm const epoch(*std::gmtime(&zero)); - ntfs_duration result(day * epoch.tm_yday); + std::chrono::system_clock::duration result(day * epoch.tm_yday); result += std::chrono::hours(epoch.tm_hour); result += std::chrono::minutes(epoch.tm_min); result += std::chrono::seconds(epoch.tm_sec); - int years(1900 - 1601 + epoch.tm_year); + int years(1900 - 1970 + epoch.tm_year); result += four_centuries * (years / 400); years %= 400; result += century * (years / 100); @@ -63,11 +64,9 @@ util::ntfs_duration calculate_ntfs_offset() years %= 4; result += year * years; - return result; + return result - std::chrono::system_clock::from_time_t(0).time_since_epoch(); } -} // anonymous namespace - // ------------------------------------------------- @@ -76,7 +75,9 @@ util::ntfs_duration calculate_ntfs_offset() std::chrono::system_clock::time_point system_clock_time_point_from_ntfs_duration(ntfs_duration d) { - return std::chrono::system_clock::from_time_t(0) + std::chrono::duration_cast(d - f_ntfs_offset); + typedef arbitrary_clock > ntfs_clock; + const std::chrono::time_point tp(d); + return ntfs_clock::to_system_clock(tp); } } // namespace util diff --git a/src/lib/util/timeconv.h b/src/lib/util/timeconv.h index ab8fd50fdcd..e2f6a987135 100644 --- a/src/lib/util/timeconv.h +++ b/src/lib/util/timeconv.h @@ -14,11 +14,19 @@ #pragma once #include "osdcore.h" +#include "coreutil.h" #include namespace util { +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +extern std::chrono::system_clock::duration system_clock_adjustment; + + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ @@ -26,6 +34,212 @@ namespace util { typedef std::chrono::duration > ntfs_duration; +//--------------------------------------------------------- +// arbitrary_clock - an std::chrono clock that "knows" the +// date of the epoch's begining +//--------------------------------------------------------- + +template +class arbitrary_clock +{ +public: + typedef Rep rep; + typedef Ratio period; + typedef std::chrono::duration duration; + typedef std::chrono::time_point time_point; + static constexpr int base_year = Y; + static constexpr int base_month = M; + static constexpr int base_day = D; + static constexpr int base_hour = H; + static constexpr int base_minute = N; + static constexpr int base_second = S; + + //--------------------------------------------------------- + // from_arbitrary_time_point - converts an arbitrary_clock + // with a different scale to this arbitrary_clock's scale + //--------------------------------------------------------- + + template + static time_point from_arbitrary_time_point(const std::chrono::time_point > &tp) + { + const int64_t our_absolute_day = absolute_day(Y, M, D); + const int64_t their_absolute_day = absolute_day(Y2, M2, D2); + + const auto our_fract_day = std::chrono::hours(H) + std::chrono::minutes(N) + std::chrono::seconds(S); + const auto their_fract_day = std::chrono::hours(H2) + std::chrono::minutes(N2) + std::chrono::seconds(S2); + + const std::chrono::duration adjustment(std::chrono::hours(24) * (their_absolute_day - our_absolute_day) + (their_fract_day - our_fract_day)); + const duration result_duration = std::chrono::duration_cast(tp.time_since_epoch() + adjustment); + return time_point(result_duration); + } + + + //--------------------------------------------------------- + // to_arbitrary_time_point - converts an arbitrary_clock + // of this scale to one of different scale + //--------------------------------------------------------- + + template + static std::chrono::time_point > to_arbitrary_time_point(const time_point &tp) + { + return arbitrary_clock::from_arbitrary_time_point(tp); + } + + + //--------------------------------------------------------- + // to_tm - formats a structure of type 'struct tm' + //--------------------------------------------------------- + + static struct tm to_tm(const time_point &tp) + { + std::chrono::time_point normalized_tp = to_arbitrary_time_point< + std::int64_t, + tm_conversion_clock::base_year, + tm_conversion_clock::base_month, + tm_conversion_clock::base_day, + tm_conversion_clock::base_hour, + tm_conversion_clock::base_minute, + tm_conversion_clock::base_second, + tm_conversion_clock::period>(tp); + return internal_to_tm(normalized_tp.time_since_epoch()); + } + + + //--------------------------------------------------------- + // to_system_clock - converts to a system_clock time_point + //--------------------------------------------------------- + + static std::chrono::time_point to_system_clock(const time_point &tp) + { + auto normalized_tp = to_arbitrary_time_point< + std::int64_t, + system_conversion_clock::base_year, + system_conversion_clock::base_month, + system_conversion_clock::base_day, + system_conversion_clock::base_hour, + system_conversion_clock::base_minute, + system_conversion_clock::base_second, + system_conversion_clock::period>(tp); + return std::chrono::time_point(normalized_tp.time_since_epoch() + system_clock_adjustment); + } + + //--------------------------------------------------------- + // from_system_clock - converts from a system_clock time_point + //--------------------------------------------------------- + + static time_point from_system_clock(const std::chrono::time_point &tp) + { + std::chrono::time_point normalized_tp(tp.time_since_epoch() - system_clock_adjustment); + return from_arbitrary_time_point(normalized_tp); + } + +private: + // By positioning the base year at 1601, we can ensure that: + // + // * years with leap years are at the end of every quadyear + // * quadyears without leap years are at the end of every century + // * centuries where the last quadyear has a leap year at the end are at the + // end of every quadcentury + typedef arbitrary_clock > tm_conversion_clock; + + //--------------------------------------------------------- + // internal_to_tm - formats a structure of type 'struct tm' + // based on a normalized clock + //--------------------------------------------------------- + + static struct tm internal_to_tm(std::chrono::duration > duration) + { + constexpr int days_in_year(365); + constexpr int days_in_four_years((days_in_year * 4) + 1); + constexpr int days_in_century((days_in_four_years * 25) - 1); + constexpr int days_in_four_centuries((days_in_century * 4) + 1); + + constexpr tm_conversion_clock::duration day(std::chrono::hours(24)); + constexpr tm_conversion_clock::duration year(day * days_in_year); + constexpr tm_conversion_clock::duration four_years(day * days_in_four_years); + constexpr tm_conversion_clock::duration century(day * days_in_century); + constexpr tm_conversion_clock::duration four_centuries(day * days_in_four_centuries); + + // figure out the day of week (note that 0 is Sunday, but January 1st 1601 is + // a Monday, so we have to adjust by one day) + const int day_of_week = int((duration + std::chrono::hours(24)) / day % 7); + + // figure out the year + const int four_centuries_count = int(duration / four_centuries); + duration -= four_centuries_count * four_centuries; + const int century_count = std::min(int(duration / century), 3); + duration -= century_count * century; + const int four_years_count = std::min(int(duration / four_years), 25); + duration -= four_years_count * four_years; + const int year_count = int(duration / year); + duration -= year_count * year; + const int actual_year = tm_conversion_clock::base_year + four_centuries_count * 400 + century_count * 100 + four_years_count * 4 + year_count; + + // figure out the day in the year + const int day_in_year = int(duration / day); + duration -= day_in_year * day; + + // figure out the month + int month, day_in_month = day_in_year; + for (month = 0; month < 12; month++) + { + int days_in_this_month = gregorian_days_in_month(month + 1, actual_year); + if (day_in_month < days_in_this_month) + break; + day_in_month -= days_in_this_month; + } + if (month >= 12) + throw false; + + // figure out the time + const int hour = int(duration / std::chrono::hours(1)); + duration -= std::chrono::hours(hour); + const int minute = int(duration / std::chrono::minutes(1)); + duration -= std::chrono::minutes(minute); + const int second = int(duration / std::chrono::seconds(1)); + duration -= std::chrono::seconds(second); + + // populate the result and return + struct tm result; + memset(&result, 0, sizeof(result)); + result.tm_year = actual_year - 1900; + result.tm_mon = month; + result.tm_mday = day_in_month + 1; + result.tm_yday = day_in_year; + result.tm_sec = second; + result.tm_min = minute; + result.tm_hour = hour; + result.tm_wday = day_of_week; + return result; + } + + + typedef arbitrary_clock system_conversion_clock; + + //------------------------------------------------- + // absolute_day - returns the absolute day count + // for the specified year/month/day + //------------------------------------------------- + + static int64_t absolute_day(int year, int month, int day) + { + // first factor the year + int64_t result = (year - 1) * 365; + result += (year - 1) / 4; + result -= (year - 1) / 100; + result += (year - 1) / 400; + + // then the month + for (int i = 1; i < month; i++) + result += gregorian_days_in_month(i, year); + + // then the day + result += day - 1; + return result; + } +}; + + /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ diff --git a/src/tools/imgtool/modules/macutil.cpp b/src/tools/imgtool/modules/macutil.cpp index 3765f976319..1bab14487cd 100644 --- a/src/tools/imgtool/modules/macutil.cpp +++ b/src/tools/imgtool/modules/macutil.cpp @@ -2,7 +2,7 @@ // copyright-holders:Raphael Nabet /**************************************************************************** - macutil.c + macutil.cpp Imgtool Utility code for manipulating certain Apple/Mac data structures and conventions @@ -10,20 +10,25 @@ *****************************************************************************/ #include "macutil.h" +#include "timeconv.h" +typedef util::arbitrary_clock > classic_mac_clock; + time_t mac_crack_time(uint32_t t) { - /* not sure if this is correct... */ - return t - (((1970 - 1904) * 365) + 17) * 24 * 60 * 60; + classic_mac_clock::duration d(t); + std::chrono::time_point tp = classic_mac_clock::to_system_clock(std::chrono::time_point(d)); + return std::chrono::system_clock::to_time_t(tp); } uint32_t mac_setup_time(time_t t) { - /* not sure if this is correct... */ - return t + (((1970 - 1904) * 365) + 17) * 24 * 60 * 60; + auto system_time_point = std::chrono::system_clock::from_time_t(t); + auto mac_time_point = classic_mac_clock::from_system_clock(system_time_point); + return mac_time_point.time_since_epoch().count(); }