Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #228: add cDate class, bump version to 0.17.0.30 #229

Merged
merged 8 commits into from
Nov 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 187 additions & 70 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/CatenaBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Copyright notice:
#define CATENA_ARDUINO_PLATFORM_VERSION_CALC(major, minor, patch, local) \
(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))

#define CATENA_ARDUINO_PLATFORM_VERSION CATENA_ARDUINO_PLATFORM_VERSION_CALC(0, 17, 0, 20) /* v0.17.0.20 */
#define CATENA_ARDUINO_PLATFORM_VERSION CATENA_ARDUINO_PLATFORM_VERSION_CALC(0, 17, 0, 41) /* v0.17.0.41 */

#define CATENA_ARDUINO_PLATFORM_VERSION_GET_MAJOR(v) \
(((v) >> 24u) & 0xFFu)
Expand Down
239 changes: 239 additions & 0 deletions src/Catena_Date.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
Module: Catena_Date.h
Function:
cDate class definition
Copyright:
See accompanying LICENSE file for copyright and license information.
Author:
Terry Moore, MCCI Corporation July 2019, November 1989
Charlie Mills, MCCI Corporation March 1996
*/

#ifndef _Catena_Date_h_
# define _Catena_Date_h_

#pragma once

#include <cstdint>
#include <Catena_limits.h>

namespace McciCatena {

/****************************************************************************\
|
| The simple date object
|
\****************************************************************************/

class cDate
{
public:
// subtract from Common seconds to get equivalent gps seconds
static constexpr std::int32_t kCommonToGpsSeconds = 315964800;
using CommonTime_t = std::int64_t;
using GpsTime_t = std::int64_t;

// the invalid GPS time value. GPS epoch is after Common, so this defines limit of negative history
static constexpr GpsTime_t kGpsTimeInvalidLow = std::numeric_limits<GpsTime_t>::lowest();
static constexpr GpsTime_t kGpsTimeInvalid = kGpsTimeInvalidLow;
static constexpr CommonTime_t kCommonTimeInvalidLow = CommonTime_t(kGpsTimeInvalidLow + kCommonToGpsSeconds);
static constexpr CommonTime_t kCommonTimeInvalid = std::numeric_limits<CommonTime_t>::lowest();
static constexpr CommonTime_t kCommonTimeInvalidHigh = cNumericLimits<CommonTime_t>::numeric_limits_max();
static constexpr GpsTime_t kGpsTimeInvalidHigh = GpsTime_t(kCommonTimeInvalidHigh - kCommonToGpsSeconds);

static constexpr bool isValidGpsTime(GpsTime_t t) { return t > kGpsTimeInvalidLow && t < kGpsTimeInvalidHigh; }
static constexpr bool isValidCommonTime(CommonTime_t t) { return t > kCommonTimeInvalidLow && t < kCommonTimeInvalidHigh; }


using Year_t = std::uint16_t;
using Month_t = std::uint8_t;
using Day_t = std::uint8_t;
using Hour_t = std::uint8_t;
using Minute_t = std::uint8_t;
using Second_t = std::uint8_t;
static constexpr Year_t kCommonYear = 1970;
static constexpr Year_t kGpsYear = 1980;
static constexpr Year_t kMinYear = 0;
static constexpr Year_t kMaxYear = cNumericLimits<Year_t>::numeric_limits_max();

// one-hot mask with 1 for zero-origin months with 31 days
static constexpr unsigned kMonthsWith31Days =
(1 << 0 | 1 << 2 | 1 << 4 | 1 << 6 | 1 << 7 | 1 << 9 | 1 << 11);

// return days since day zero of the proleptic Gregorian astronomical calendar.
// Year 0 is a leap year in this calendar.
// Assumes positive years.
static constexpr std::uint32_t daysSinceProlepticZero(Year_t y)
{
return y <= 0 ? 0
: 366 +
365 * (y-1) +
((y - 1) / 4) -
(((y - 1) / 4) / 25) +
((((y - 1) / 4) / 25) / 4);
}

static constexpr std::uint32_t kCommonDayZero() { return daysSinceProlepticZero(kCommonYear); }
static constexpr std::uint32_t kGpsDayZero() { return daysSinceProlepticZero(kGpsYear) + 5; }

// is y a leap year?
static constexpr bool isLeapYear(Year_t y)
{
return (y % 4 != 0) ? false :
(y % 100 != 0) ? true :
(y % 400 == 0);
}

static constexpr GpsTime_t getGpsTime(CommonTime_t tCommon)
{
return isValidCommonTime(tCommon) ? tCommon - kCommonToGpsSeconds : kGpsTimeInvalid;
}
static constexpr GpsTime_t getCommonTime(GpsTime_t tGps)
{
return isValidGpsTime(tGps) ? tGps + kCommonToGpsSeconds : kCommonTimeInvalid;
}

static constexpr bool isValidYearMonthDay(Year_t year, Month_t month, Day_t day)
{
return (kMinYear <= year && year <= kMaxYear) &&
(1 <= month && month <= 12) &&
(1 <= day && day <= (month == 2 ? 28 + isLeapYear(year) :
((1 << (month - 1)) & kMonthsWith31Days) != 0 ? 31 :
30));
}

static constexpr bool isValidHourMinuteSecond(Hour_t h, Minute_t m, Second_t s)
{
return (0 <= h && h <= 23) &&
(0 <= m && m <= 59) &&
(0 <= s && s <= 59);
}

// return day in year (0..364 or 0..365)
static /* constexpr */ unsigned getDayInYear(Year_t year, Month_t month, Day_t day)
{
unsigned dayInYear = 0;

if (month == 0 || month > 12 || day == 0)
return 0;

/* now, index into the year by the date */
unsigned kStopMonth = 1 << (month - 1);
for (unsigned maskMonth = 1 << 0; maskMonth < kStopMonth; maskMonth <<= 1)
{
if (maskMonth == 1 << 1)
dayInYear += 28;
else
dayInYear += maskMonth & kMonthsWith31Days ? 31 : 30;
}

/* if March or later, and it's a leap year, add 1 */
if (month >= 3 && isLeapYear(year))
++dayInYear;

return dayInYear + day - 1;
}

// default constructor
cDate() {}

// set date portion of clock, return true if valid.
bool setDate(Year_t y, Month_t m, Day_t d);

// set date portion of clock by parsing ISO8601 string yyyy-mm-dd
bool parseDateIso8601(const char *pDate, const char **ppEndPointer = nullptr);

// set time portion of clock, return true if valid.
bool setTime(Hour_t h, Minute_t m, Second_t s);

// set time portion of clock by parsing ISO8601 string hh:mm:ss
bool parseTime(const char *pTime, const char **ppEndPointer = nullptr);

// return time according to Common epoch
CommonTime_t getCommonTime() const;

// return time according to GPS epoch
GpsTime_t getGpsTime() const;

// set time from interval from Common epoch
bool setCommonTime(CommonTime_t t);

// set time from interval from GPS epoch
bool setGpsTime(GpsTime_t t);

// get year (common era)
Year_t year() const { return this->m_year; }
// get month [1..12]
Month_t month() const { return this->m_month; }
// get day [1..31]
Day_t day() const { return this->m_day; }
// get hour [0..23]
Hour_t hour() const { return this->m_hour; }
// get minute [0..59]
Minute_t minute() const { return this->m_minute; }
// get second [0..59]
Second_t second() const { return this->m_second; }

// check validity
bool isValid() const
{
return isValidYearMonthDay(this->m_year, this->m_month, this->m_day) &&
isValidHourMinuteSecond(this->m_hour, this->m_minute, this->m_second);
}

private:
// the current year: in current era.
Year_t m_year;
// month: 1..12
Month_t m_month;
// day: 1..31 (limit based on month)
Day_t m_day;
// hour: 0..23
Hour_t m_hour;
// minute: 0..59
Minute_t m_minute;
// second: 0..59
Second_t m_second;
};

// check the leap-year calculations
static_assert( cDate::isLeapYear(0), "isLeapYear() error");
static_assert(! cDate::isLeapYear(1899), "isLeapYear() error");
static_assert(! cDate::isLeapYear(1900), "isLeapYear() error");
static_assert(! cDate::isLeapYear(1901), "isLeapYear() error");
static_assert(! cDate::isLeapYear(1902), "isLeapYear() error");
static_assert(! cDate::isLeapYear(1903), "isLeapYear() error");
static_assert( cDate::isLeapYear(1904), "isLeapYear() error");
static_assert( cDate::isLeapYear(2000), "isLeapYear() error");

static_assert(cDate::daysSinceProlepticZero(0) == 0,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(1) == 366,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(4) == 4 * 365 + 1,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(5) == 5 * 365 + 2,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(99) == 99 * 365 + 99/4 + 1,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(100) == 100 * 365 + 99/4 + 1,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(101) == 101 * 365 + 100/4,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(101) == 36890,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(1901) == 694326,
"daysSinceProlepticZero() error");
static_assert(cDate::daysSinceProlepticZero(2005) == 732312,
"daysSinceProlepticZero() error");

} // namespace McciCatena



#endif /* ndef _Catena_Date_h_ */
Loading