@@ -462,7 +462,7 @@ bool DateTime::isValid() const {
462462*/
463463/* *************************************************************************/
464464
465- char *DateTime::toString (char *buffer) {
465+ char *DateTime::toString (char *buffer) const {
466466 uint8_t apTag =
467467 (strstr (buffer, " ap" ) != nullptr ) || (strstr (buffer, " AP" ) != nullptr );
468468 uint8_t hourReformatted = 0 , isPM = false ;
@@ -713,7 +713,7 @@ bool DateTime::operator==(const DateTime &right) const {
713713 @return Timestamp string, e.g. "2020-04-16T18:34:56".
714714*/
715715/* *************************************************************************/
716- String DateTime::timestamp (timestampOpt opt) {
716+ String DateTime::timestamp (timestampOpt opt) const {
717717 char buffer[25 ]; // large enough for any DateTime, including invalid ones
718718
719719 // Generate timestamp according to opt
@@ -765,6 +765,150 @@ TimeSpan::TimeSpan(int16_t days, int8_t hours, int8_t minutes, int8_t seconds)
765765/* *************************************************************************/
766766TimeSpan::TimeSpan (const TimeSpan ©) : _seconds(copy._seconds) {}
767767
768+ /* !
769+ @brief Create a new TimeSpan from an iso8601 formatted period string
770+
771+ If the string is entirely malformed (doesn't begin with P or -) this will
772+ construct a TimeSpan of 0 seconds. If parsing fails before the end of the
773+ string, this constuctor will interpret its argument as the longest
774+ well-formed prefix string.
775+
776+ For example, the string P5M10~ will parse the same as P5M
777+
778+ @param iso8601 the formatted string
779+ */
780+ TimeSpan::TimeSpan (const char *iso8601) : _seconds(0 ) {
781+ int32_t sign = 1 ;
782+ const char *cursor = iso8601;
783+ if (*cursor == ' -' ) {
784+ sign = -1 ;
785+ cursor++;
786+ }
787+ if (*cursor == ' P' ) {
788+ cursor++;
789+ } else {
790+ return ;
791+ }
792+
793+ char *rest;
794+ long num = strtol (cursor, &rest, 10 );
795+ cursor = rest;
796+ if (*cursor == ' D' ) {
797+ _seconds += sign * SECONDS_PER_DAY * num;
798+ cursor++;
799+ }
800+
801+ if (*cursor == ' T' ) {
802+ cursor++;
803+ num = strtol (cursor, &rest, 10 );
804+ cursor = rest;
805+ } else if (*cursor == ' \0 ' ) {
806+ return ;
807+ } else {
808+ // error: malformed iso8601
809+ return ;
810+ }
811+
812+ if (*cursor == ' H' ) {
813+ _seconds += sign * SECS_PER_MIN * MINS_PER_HOUR * num;
814+ cursor++;
815+ num = strtol (cursor, &rest, 10 );
816+ cursor = rest;
817+ }
818+
819+ if (*cursor == ' M' ) {
820+ _seconds += sign * SECS_PER_MIN * num;
821+ cursor++;
822+ num = strtol (cursor, &rest, 10 );
823+ cursor = rest;
824+ }
825+
826+ if (*cursor == ' S' ) {
827+ _seconds += sign * num;
828+ }
829+ }
830+
831+ /* !
832+ @brief Formats this TimeSpan according to iso8601
833+
834+ See toCharArray for more details on the format
835+
836+ @return String of formatted TimeSpan
837+ */
838+ String TimeSpan::toString () const {
839+ constexpr size_t buflen = 19 ;
840+ char buf[buflen];
841+ this ->toCharArray (buf, buflen);
842+ return String (buf);
843+ }
844+
845+ /* !
846+ @brief Formats this TimeSpan according to iso8601
847+
848+ Fails (returning 0) if buf is too small to store the result. buf size 19
849+ will never fail.
850+ Example: TimeSpan t(32, 23, 54, 11) formats to "P32DT23H54M11S".
851+
852+ @param buf char array to write output. Always contains trailing '\0'.
853+ @param len the length of buf. Must be at least 1.
854+ @return number of bytes written excluding trailing '\0' or 0 on failure
855+ */
856+ size_t TimeSpan::toCharArray (char *buf, size_t len) const {
857+ size_t written = 0 ;
858+
859+ if (_seconds == 0 ) {
860+ written += snprintf (buf, len, " PT0S" );
861+ if (written >= len) {
862+ buf[len - 1 ] = ' \0 ' ;
863+ return 0 ;
864+ } else {
865+ return written;
866+ }
867+ }
868+
869+ // Keep sign separate from tmp to prevent overflow caused by -1 * INT_MIN
870+ int8_t sign = _seconds > 0 ? 1 : -1 ;
871+ int32_t tmp = _seconds;
872+ int8_t seconds = sign * (tmp % SECS_PER_MIN);
873+ // Fold in sign - while dividing by SECS_PER_MIN, tmp can no longer overflow
874+ tmp /= sign * SECS_PER_MIN;
875+ int8_t minutes = tmp % MINS_PER_HOUR;
876+ tmp /= MINS_PER_HOUR;
877+ int8_t hours = tmp % HOURS_PER_DAY;
878+ int16_t days = tmp / HOURS_PER_DAY;
879+ if (_seconds < 0 ) {
880+ written += snprintf (buf + written, len - written, " -P" );
881+ } else {
882+ written += snprintf (buf + written, len - written, " P" );
883+ }
884+
885+ if (days > 0 ) {
886+ written += snprintf (buf + written, len - written, " %dD" , days);
887+ }
888+
889+ if (hours > 0 || minutes > 0 || seconds > 0 ) {
890+ written += snprintf (buf + written, len - written, " %s" , " T" );
891+
892+ if (hours > 0 ) {
893+ written += snprintf (buf + written, len - written, " %dH" , hours);
894+ }
895+ if (minutes > 0 ) {
896+ written += snprintf (buf + written, len - written, " %dM" , minutes);
897+ }
898+ if (seconds > 0 ) {
899+ written += snprintf (buf + written, len - written, " %dS" , seconds);
900+ }
901+ }
902+
903+ if (written >= len) {
904+ buf[len - 1 ] = ' \0 ' ;
905+ return 0 ;
906+ } else {
907+ buf[written] = ' \0 ' ;
908+ return written;
909+ }
910+ }
911+
768912/* *************************************************************************/
769913/* !
770914 @brief Add two TimeSpans
0 commit comments