From be1c42345619225364b113af930fccc181a7d2e1 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 3 Nov 2023 22:30:28 -0500 Subject: [PATCH 1/9] First attempt at Astronomy_UtcFromTime_Julian --- source/c/astronomy.c | 83 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/source/c/astronomy.c b/source/c/astronomy.c index dde8c618..b2992870 100644 --- a/source/c/astronomy.c +++ b/source/c/astronomy.c @@ -1226,12 +1226,12 @@ astro_time_t Astronomy_TimeFromUtc(astro_utc_t utc) } /** - * @brief Determines the calendar year, month, day, and time from an #astro_time_t value. + * @brief Determines the Gregorian calendar year, month, day, and time from an #astro_time_t value. * * After calculating the date and time of an astronomical event in the form of * an #astro_time_t value, it is often useful to display the result in a human-readable * form. This function converts the linear time scales in the `ut` field of #astro_time_t - * into a calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * into a Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed * in UTC. * * @param time The astronomical time value to be converted to calendar date and time. @@ -1282,6 +1282,85 @@ astro_utc_t Astronomy_UtcFromTime(astro_time_t time) return utc; } +/** + * @brief Determines the Julian calendar year, month, day, and time from an #astro_time_t value. + * + * After calculating the date and time of an astronomical event in the form of + * an #astro_time_t value, it is often useful to display the result in a human-readable + * form. This function converts the linear time scales in the `ut` field of #astro_time_t + * into a Julian calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * in UTC. + * + * @param time The astronomical time value to be converted to calendar date and time. + * @return A date and time broken out into conventional year, month, day, hour, minute, and second. + */ +astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) +{ + astro_utc_t utc; + + double julianDatePlus12Hours = time.ut + 2451545 + 0.5; + int64_t J = (int64_t)floor(julianDatePlus12Hours); + + /* + Algorithm adapted from Richards, E.G. 2012, "Calendars," from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. + See: https://aa.usno.navy.mil/downloads/c15_usb_online.pdf + */ + + int64_t cycles = 0 + + if J < 0 { + cycles = -J / 1461 + 1; + J += cycles * 1461; + } + + const int64_t f = J + 1401; + const int64_t e = 4 * f + 3; + const int64_t g = (e % 1461) / 4; + const int64_t h = 5 * g + 2; + utc.day = (h % 153) / 5 + 1; + utc.month = ((h / 153 + 2) % 12) + 1; + utc.year = e / 1461 - 4716 + (12 + 2 - utc.month) / 12; + + if cycles > 0 + utc.year -= cycles * 4; + + double dayFraction = modf(julianDatePlus12Hours, NULL); + if dayFraction < 0 + dayFraction += 1; + + double hour, minute; + double hourFraction = modf(dayFraction * 24, &hour); + double minuteFraction = modf(hourFraction * 60 * 24, &minute); + + utc.hour = (int)hour; + utc.minute = (int)minute; + utc.second = minuteFraction * 60; + + return utc; +} + +/** + * @brief Determines the Julian or Gregorian calendar year, month, day, and time from an #astro_time_t value. + * + * Dates before the Julian to Gregorian calender changeover (1582-10-15) are treated + * as dates in the Julian calendar, while later dates are treated as dates in the Gregorian calendar. + + * After calculating the date and time of an astronomical event in the form of + * an #astro_time_t value, it is often useful to display the result in a human-readable + * form. This function converts the linear time scales in the `ut` field of #astro_time_t + * into a Julian or Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * in UTC. + * + * @param time The astronomical time value to be converted to calendar date and time. + * @return A date and time broken out into conventional year, month, day, hour, minute, and second. + */ +astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) +{ + if (time.ut < -746558.5) + return Astronomy_UtcFromTime_Julian(time); + else + return Astronomy_UtcFromTime(time); +} /** * @brief Formats an #astro_time_t value as an ISO 8601 string. From bb499c8300ec5bc7911665022e6ec694f9ed6790 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 3 Nov 2023 22:37:27 -0500 Subject: [PATCH 2/9] Revert "First attempt at Astronomy_UtcFromTime_Julian" This reverts commit be1c42345619225364b113af930fccc181a7d2e1. --- source/c/astronomy.c | 83 ++------------------------------------------ 1 file changed, 2 insertions(+), 81 deletions(-) diff --git a/source/c/astronomy.c b/source/c/astronomy.c index b2992870..dde8c618 100644 --- a/source/c/astronomy.c +++ b/source/c/astronomy.c @@ -1226,12 +1226,12 @@ astro_time_t Astronomy_TimeFromUtc(astro_utc_t utc) } /** - * @brief Determines the Gregorian calendar year, month, day, and time from an #astro_time_t value. + * @brief Determines the calendar year, month, day, and time from an #astro_time_t value. * * After calculating the date and time of an astronomical event in the form of * an #astro_time_t value, it is often useful to display the result in a human-readable * form. This function converts the linear time scales in the `ut` field of #astro_time_t - * into a Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * into a calendar date and time: year, month, day, hours, minutes, and seconds, expressed * in UTC. * * @param time The astronomical time value to be converted to calendar date and time. @@ -1282,85 +1282,6 @@ astro_utc_t Astronomy_UtcFromTime(astro_time_t time) return utc; } -/** - * @brief Determines the Julian calendar year, month, day, and time from an #astro_time_t value. - * - * After calculating the date and time of an astronomical event in the form of - * an #astro_time_t value, it is often useful to display the result in a human-readable - * form. This function converts the linear time scales in the `ut` field of #astro_time_t - * into a Julian calendar date and time: year, month, day, hours, minutes, and seconds, expressed - * in UTC. - * - * @param time The astronomical time value to be converted to calendar date and time. - * @return A date and time broken out into conventional year, month, day, hour, minute, and second. - */ -astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) -{ - astro_utc_t utc; - - double julianDatePlus12Hours = time.ut + 2451545 + 0.5; - int64_t J = (int64_t)floor(julianDatePlus12Hours); - - /* - Algorithm adapted from Richards, E.G. 2012, "Calendars," from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - See: https://aa.usno.navy.mil/downloads/c15_usb_online.pdf - */ - - int64_t cycles = 0 - - if J < 0 { - cycles = -J / 1461 + 1; - J += cycles * 1461; - } - - const int64_t f = J + 1401; - const int64_t e = 4 * f + 3; - const int64_t g = (e % 1461) / 4; - const int64_t h = 5 * g + 2; - utc.day = (h % 153) / 5 + 1; - utc.month = ((h / 153 + 2) % 12) + 1; - utc.year = e / 1461 - 4716 + (12 + 2 - utc.month) / 12; - - if cycles > 0 - utc.year -= cycles * 4; - - double dayFraction = modf(julianDatePlus12Hours, NULL); - if dayFraction < 0 - dayFraction += 1; - - double hour, minute; - double hourFraction = modf(dayFraction * 24, &hour); - double minuteFraction = modf(hourFraction * 60 * 24, &minute); - - utc.hour = (int)hour; - utc.minute = (int)minute; - utc.second = minuteFraction * 60; - - return utc; -} - -/** - * @brief Determines the Julian or Gregorian calendar year, month, day, and time from an #astro_time_t value. - * - * Dates before the Julian to Gregorian calender changeover (1582-10-15) are treated - * as dates in the Julian calendar, while later dates are treated as dates in the Gregorian calendar. - - * After calculating the date and time of an astronomical event in the form of - * an #astro_time_t value, it is often useful to display the result in a human-readable - * form. This function converts the linear time scales in the `ut` field of #astro_time_t - * into a Julian or Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed - * in UTC. - * - * @param time The astronomical time value to be converted to calendar date and time. - * @return A date and time broken out into conventional year, month, day, hour, minute, and second. - */ -astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) -{ - if (time.ut < -746558.5) - return Astronomy_UtcFromTime_Julian(time); - else - return Astronomy_UtcFromTime(time); -} /** * @brief Formats an #astro_time_t value as an ISO 8601 string. From 42d9c78b3c72f5b5b77df5cf3c696a8f9f3b4e9e Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 3 Nov 2023 22:40:06 -0500 Subject: [PATCH 3/9] Second attempt at Astronomy_UtcFromTime_Julian --- generate/template/astronomy.c | 83 ++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 0d7df648..47f4a83d 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1220,12 +1220,12 @@ astro_time_t Astronomy_TimeFromUtc(astro_utc_t utc) } /** - * @brief Determines the calendar year, month, day, and time from an #astro_time_t value. + * @brief Determines the Gregorian calendar year, month, day, and time from an #astro_time_t value. * * After calculating the date and time of an astronomical event in the form of * an #astro_time_t value, it is often useful to display the result in a human-readable * form. This function converts the linear time scales in the `ut` field of #astro_time_t - * into a calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * into a Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed * in UTC. * * @param time The astronomical time value to be converted to calendar date and time. @@ -1276,6 +1276,85 @@ astro_utc_t Astronomy_UtcFromTime(astro_time_t time) return utc; } +/** + * @brief Determines the Julian calendar year, month, day, and time from an #astro_time_t value. + * + * After calculating the date and time of an astronomical event in the form of + * an #astro_time_t value, it is often useful to display the result in a human-readable + * form. This function converts the linear time scales in the `ut` field of #astro_time_t + * into a Julian calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * in UTC. + * + * @param time The astronomical time value to be converted to calendar date and time. + * @return A date and time broken out into conventional year, month, day, hour, minute, and second. + */ +astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) +{ + astro_utc_t utc; + + double julianDatePlus12Hours = time.ut + 2451545 + 0.5; + int64_t J = (int64_t)floor(julianDatePlus12Hours); + + /* + Algorithm adapted from Richards, E.G. 2012, "Calendars," from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. + See: https://aa.usno.navy.mil/downloads/c15_usb_online.pdf + */ + + int64_t cycles = 0; + + if (J < 0) { + cycles = -J / 1461 + 1; + J += cycles * 1461; + } + + const int64_t f = J + 1401; + const int64_t e = 4 * f + 3; + const int64_t g = (e % 1461) / 4; + const int64_t h = 5 * g + 2; + utc.day = (h % 153) / 5 + 1; + utc.month = ((h / 153 + 2) % 12) + 1; + utc.year = e / 1461 - 4716 + (12 + 2 - utc.month) / 12; + + if (cycles > 0) + utc.year -= cycles * 4; + + double dayFraction = modf(julianDatePlus12Hours, NULL); + if (dayFraction < 0) + dayFraction += 1; + + double hour, minute; + double hourFraction = modf(dayFraction * 24, &hour); + double minuteFraction = modf(hourFraction * 60 * 24, &minute); + + utc.hour = (int)hour; + utc.minute = (int)minute; + utc.second = minuteFraction * 60; + + return utc; +} + +/** + * @brief Determines the Julian or Gregorian calendar year, month, day, and time from an #astro_time_t value. + * + * Dates before the Julian to Gregorian calender changeover (1582-10-15) are treated + * as dates in the Julian calendar, while later dates are treated as dates in the Gregorian calendar. + * + * After calculating the date and time of an astronomical event in the form of + * an #astro_time_t value, it is often useful to display the result in a human-readable + * form. This function converts the linear time scales in the `ut` field of #astro_time_t + * into a Julian or Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * in UTC. + * + * @param time The astronomical time value to be converted to calendar date and time. + * @return A date and time broken out into conventional year, month, day, hour, minute, and second. + */ +astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) +{ + if (time.ut < -746558.5) + return Astronomy_UtcFromTime_Julian(time); + else + return Astronomy_UtcFromTime(time); +} /** * @brief Formats an #astro_time_t value as an ISO 8601 string. From e1e32a2bccdb945e45ddecafba83b17ac716c17a Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 3 Nov 2023 22:52:58 -0500 Subject: [PATCH 4/9] Don't pass NULL to modf --- generate/template/astronomy.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 47f4a83d..80d14e2d 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1296,10 +1296,9 @@ astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) int64_t J = (int64_t)floor(julianDatePlus12Hours); /* - Algorithm adapted from Richards, E.G. 2012, "Calendars," from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - See: https://aa.usno.navy.mil/downloads/c15_usb_online.pdf + Algorithm adapted from Richards, E.G. 2012, "Calendars," from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. + See: https://aa.usno.navy.mil/downloads/c15_usb_online.pdf */ - int64_t cycles = 0; if (J < 0) { @@ -1318,13 +1317,14 @@ astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) if (cycles > 0) utc.year -= cycles * 4; - double dayFraction = modf(julianDatePlus12Hours, NULL); + double ignore; + double dayFraction = modf(julianDatePlus12Hours, &ignore); if (dayFraction < 0) dayFraction += 1; double hour, minute; double hourFraction = modf(dayFraction * 24, &hour); - double minuteFraction = modf(hourFraction * 60 * 24, &minute); + double minuteFraction = modf(hourFraction * 60, &minute); utc.hour = (int)hour; utc.minute = (int)minute; From 1780324f046d69e05afa2e2215d9088ce00f65e6 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 3 Nov 2023 23:03:58 -0500 Subject: [PATCH 5/9] Use correct Gregorian changeover --- generate/template/astronomy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 80d14e2d..834f9d05 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1350,7 +1350,7 @@ astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) */ astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) { - if (time.ut < -746558.5) + if (time.ut < -152384.5) return Astronomy_UtcFromTime_Julian(time); else return Astronomy_UtcFromTime(time); From 844e2733763e776d094695664a45fe1d0cb5c742 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 3 Nov 2023 23:44:20 -0500 Subject: [PATCH 6/9] Zap tab --- generate/template/astronomy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 834f9d05..08346846 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1299,7 +1299,7 @@ astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) Algorithm adapted from Richards, E.G. 2012, "Calendars," from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. See: https://aa.usno.navy.mil/downloads/c15_usb_online.pdf */ - int64_t cycles = 0; + int64_t cycles = 0; if (J < 0) { cycles = -J / 1461 + 1; From 84fb6707603da82765b583b4a6c6e5865e3597d6 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Sat, 4 Nov 2023 08:23:34 -0500 Subject: [PATCH 7/9] Add Astronomy_FormatTime_Julian --- generate/template/astronomy.c | 130 ++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 5 deletions(-) diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 08346846..8ed86d41 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1357,7 +1357,7 @@ astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) } /** - * @brief Formats an #astro_time_t value as an ISO 8601 string. + * @brief Formats an #astro_time_t value as an ISO 8601 string using the Gregorian calendar. * * Given an #astro_time_t value `time`, formats it as an ISO 8601 * string to the resolution specified by the `format` parameter. @@ -1367,8 +1367,9 @@ astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) * @param time * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. * If the civil time is outside the year range -999999 to +999999, the function fails - * and returns `ASTRO_BAD_TIME`. Years prior to 1583 are treated as if they are - * using the modern Gregorian calendar, even when the Julian calendar was actually in effect. + * and returns `ASTRO_BAD_TIME`. Dates before the Julian to Gregorian calender changeover + * (1582-10-15) are treated as dates in the Gregorian calendar even though the Julian + * calendar was actually in effect. * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. * The year 2 BC is represented by -1, etc. * @@ -1400,6 +1401,120 @@ astro_status_t Astronomy_FormatTime( astro_time_format_t format, char *text, size_t size) +{ + return FormatTime(time, format, text, size, 2); +} + +/** + * @brief Formats an #astro_time_t value as an ISO 8601 string using the Julian calendar. + * + * Given an #astro_time_t value `time`, formats it as an ISO 8601 + * string to the resolution specified by the `format` parameter. + * The result is stored in the `text` buffer whose capacity in bytes + * is specified by `size`. + * + * @param time + * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. + * If the civil time is outside the year range -999999 to +999999, the function fails + * and returns `ASTRO_BAD_TIME`. Dates after the Julian to Gregorian calender changeover + * (1582-10-15) are treated as dates in the Julian calendar even though the Gregorian + * calendar was actually in effect. + * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. + * The year 2 BC is represented by -1, etc. + * + * @param format + * Specifies the resolution to which the date and time should be formatted, + * as explained at #astro_time_format_t. + * If the value of `format` is not recognized, the function fails and + * returns `ASTRO_INVALID_PARAMETER`. + * + * @param text + * A pointer to a text buffer to receive the output. + * If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. + * If the function fails for any reason, and `text` is not `NULL`, + * and `size` is greater than 0, the `text` buffer is set to an empty string. + * + * @param size + * The size in bytes of the buffer pointed to by `text`. The buffer must + * be large enough to accomodate the output format selected by the + * `format` parameter, as specified at #astro_time_format_t. + * If `size` is too small to hold the string as specified by `format`, + * the `text` buffer is set to `""` (if possible) + * and the function returns `ASTRO_BUFFER_TOO_SMALL`. + * A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. + * + * @return `ASTRO_SUCCESS` on success; otherwise an error as described in the parameter notes. + */ +astro_status_t Astronomy_FormatTime_Julian( + astro_time_t time, + astro_time_format_t format, + char *text, + size_t size) +{ + return FormatTime(time, format, text, size, 1); +} + +/** + * @brief Formats an #astro_time_t value as an ISO 8601 string using the Julian or Gregorian calendar. + * + * Given an #astro_time_t value `time`, formats it as an ISO 8601 + * string to the resolution specified by the `format` parameter. + * The result is stored in the `text` buffer whose capacity in bytes + * is specified by `size`. + * + * @param time + * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. + * If the civil time is outside the year range -999999 to +999999, the function fails + * and returns `ASTRO_BAD_TIME`. Dates before the Julian to Gregorian calender changeover + * (1582-10-15) are treated as dates in the Julian calendar, while later dates are treated + * as dates in the Gregorian calendar. + * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. + * The year 2 BC is represented by -1, etc. + * + * @param format + * Specifies the resolution to which the date and time should be formatted, + * as explained at #astro_time_format_t. + * If the value of `format` is not recognized, the function fails and + * returns `ASTRO_INVALID_PARAMETER`. + * + * @param text + * A pointer to a text buffer to receive the output. + * If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. + * If the function fails for any reason, and `text` is not `NULL`, + * and `size` is greater than 0, the `text` buffer is set to an empty string. + * + * @param size + * The size in bytes of the buffer pointed to by `text`. The buffer must + * be large enough to accomodate the output format selected by the + * `format` parameter, as specified at #astro_time_format_t. + * If `size` is too small to hold the string as specified by `format`, + * the `text` buffer is set to `""` (if possible) + * and the function returns `ASTRO_BUFFER_TOO_SMALL`. + * A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. + * + * @return `ASTRO_SUCCESS` on success; otherwise an error as described in the parameter notes. + */ +astro_status_t Astronomy_FormatTime_Auto( + astro_time_t time, + astro_time_format_t format, + char *text, + size_t size) +{ + return FormatTime(time, format, text, size, 0); +} + +/* @param calendar + * The calendar to use for date formatting. + * 0 = automatic determination based on date + * 1 = Julian calendar + * 2 = Gregorian calendar + */ +static astro_status_t FormatTime( + astro_time_t time, + astro_time_format_t format, + char *text, + size_t size, + int calendar) { int nprinted; double rounding; @@ -1445,8 +1560,13 @@ astro_status_t Astronomy_FormatTime( /* Perform rounding. */ time.ut += rounding; - /* Convert linear J2000 days to Gregorian UTC date/time. */ - utc = Astronomy_UtcFromTime(time); + /* Convert linear J2000 days to Julian or Gregorian UTC date/time. */ + if (calendar == 0) + utc = Astronomy_UtcFromTime_Auto(time); + else if (calendar == 1) + utc = Astronomy_UtcFromTime_Julian(time); + else + utc = Astronomy_UtcFromTime(time); if (utc.year < -999999 || utc.year > +999999) return ASTRO_BAD_TIME; From 23bf669ee38223929f103218bbb7e2f4e20fe33e Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Sat, 4 Nov 2023 10:46:57 -0500 Subject: [PATCH 8/9] Move declaration of FormatTime() --- generate/template/astronomy.c | 253 +++++++++++++++++----------------- 1 file changed, 126 insertions(+), 127 deletions(-) diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 8ed86d41..3bee5b80 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1356,6 +1356,132 @@ astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) return Astronomy_UtcFromTime(time); } +/* @param calendar + * The calendar to use for date formatting. + * 0 = automatic determination based on date + * 1 = Julian calendar + * 2 = Gregorian calendar + */ +static astro_status_t FormatTime( + astro_time_t time, + astro_time_format_t format, + char *text, + size_t size, + int calendar) +{ + int nprinted; + double rounding; + size_t min_size; + astro_utc_t utc; + char ytext[20]; /* worst case: "+999999" = 8 characters including terminal '\0'. But gcc 12.2 still complains! */ + + if (text == NULL) + return ASTRO_INVALID_PARAMETER; + + if (size == 0) + return ASTRO_BUFFER_TOO_SMALL; + + text[0] = '\0'; /* initialize to empty string, in case an error occurs */ + + /* Validate 'size' parameter and perform date/time rounding. */ + switch (format) + { + case TIME_FORMAT_DAY: + min_size = 11; /* "2020-12-31" */ + rounding = 0.0; /* no rounding */ + break; + + case TIME_FORMAT_MINUTE: + min_size = 18; /* "2020-12-31T15:47Z" */ + rounding = 0.5 / (24.0 * 60.0); /* round to nearest minute */ + break; + + case TIME_FORMAT_SECOND: + min_size = 21; /* "2020-12-31T15:47:59Z" */ + rounding = 0.5 / (24.0 * 3600.0); /* round to nearest second */ + break; + + case TIME_FORMAT_MILLI: + min_size = 25; /* "2020-12-31T15:47:59.123Z" */ + rounding = 0.5 / (24.0 * 3600000.0); /* round to nearest millisecond */ + break; + + default: + return ASTRO_INVALID_PARAMETER; + } + + /* Perform rounding. */ + time.ut += rounding; + + /* Convert linear J2000 days to Julian or Gregorian UTC date/time. */ + if (calendar == 0) + utc = Astronomy_UtcFromTime_Auto(time); + else if (calendar == 1) + utc = Astronomy_UtcFromTime_Julian(time); + else + utc = Astronomy_UtcFromTime(time); + if (utc.year < -999999 || utc.year > +999999) + return ASTRO_BAD_TIME; + + if (utc.year < 0) + { + snprintf(ytext, sizeof(ytext), "-%06d", -utc.year); + min_size += 3; /* '-' prefix and two extra year digits. */ + } + else if (utc.year <= 9999) + { + snprintf(ytext, sizeof(ytext), "%04d", utc.year); + } + else + { + snprintf(ytext, sizeof(ytext), "+%06d", utc.year); + min_size += 3; /* '+' prefix and two extra year digits. */ + } + + /* Check for insufficient buffer size. */ + if (size < min_size) + return ASTRO_BUFFER_TOO_SMALL; + + /* Format the string. */ + switch (format) + { + case TIME_FORMAT_DAY: + nprinted = snprintf(text, size, "%s-%02d-%02d", + ytext, utc.month, utc.day); + break; + + case TIME_FORMAT_MINUTE: + nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02dZ", + ytext, utc.month, utc.day, + utc.hour, utc.minute); + break; + + case TIME_FORMAT_SECOND: + nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02d:%02.0lfZ", + ytext, utc.month, utc.day, + utc.hour, utc.minute, floor(utc.second)); + break; + + case TIME_FORMAT_MILLI: + nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02d:%06.3lfZ", + ytext, utc.month, utc.day, + utc.hour, utc.minute, floor(1000.0 * utc.second) / 1000.0); + break; + + default: + /* We should have already failed for any unknown 'format' value. */ + return ASTRO_INTERNAL_ERROR; + } + + if (nprinted < 0) + return ASTRO_INTERNAL_ERROR; /* should not be possible for snprintf to return a negative number */ + + if ((size_t)(1+nprinted) != min_size) + return ASTRO_INTERNAL_ERROR; /* there must be a bug calculating min_size or formatting the string */ + + return ASTRO_SUCCESS; +} + /** * @brief Formats an #astro_time_t value as an ISO 8601 string using the Gregorian calendar. * @@ -1503,133 +1629,6 @@ astro_status_t Astronomy_FormatTime_Auto( return FormatTime(time, format, text, size, 0); } -/* @param calendar - * The calendar to use for date formatting. - * 0 = automatic determination based on date - * 1 = Julian calendar - * 2 = Gregorian calendar - */ -static astro_status_t FormatTime( - astro_time_t time, - astro_time_format_t format, - char *text, - size_t size, - int calendar) -{ - int nprinted; - double rounding; - size_t min_size; - astro_utc_t utc; - char ytext[20]; /* worst case: "+999999" = 8 characters including terminal '\0'. But gcc 12.2 still complains! */ - - if (text == NULL) - return ASTRO_INVALID_PARAMETER; - - if (size == 0) - return ASTRO_BUFFER_TOO_SMALL; - - text[0] = '\0'; /* initialize to empty string, in case an error occurs */ - - /* Validate 'size' parameter and perform date/time rounding. */ - switch (format) - { - case TIME_FORMAT_DAY: - min_size = 11; /* "2020-12-31" */ - rounding = 0.0; /* no rounding */ - break; - - case TIME_FORMAT_MINUTE: - min_size = 18; /* "2020-12-31T15:47Z" */ - rounding = 0.5 / (24.0 * 60.0); /* round to nearest minute */ - break; - - case TIME_FORMAT_SECOND: - min_size = 21; /* "2020-12-31T15:47:59Z" */ - rounding = 0.5 / (24.0 * 3600.0); /* round to nearest second */ - break; - - case TIME_FORMAT_MILLI: - min_size = 25; /* "2020-12-31T15:47:59.123Z" */ - rounding = 0.5 / (24.0 * 3600000.0); /* round to nearest millisecond */ - break; - - default: - return ASTRO_INVALID_PARAMETER; - } - - /* Perform rounding. */ - time.ut += rounding; - - /* Convert linear J2000 days to Julian or Gregorian UTC date/time. */ - if (calendar == 0) - utc = Astronomy_UtcFromTime_Auto(time); - else if (calendar == 1) - utc = Astronomy_UtcFromTime_Julian(time); - else - utc = Astronomy_UtcFromTime(time); - if (utc.year < -999999 || utc.year > +999999) - return ASTRO_BAD_TIME; - - if (utc.year < 0) - { - snprintf(ytext, sizeof(ytext), "-%06d", -utc.year); - min_size += 3; /* '-' prefix and two extra year digits. */ - } - else if (utc.year <= 9999) - { - snprintf(ytext, sizeof(ytext), "%04d", utc.year); - } - else - { - snprintf(ytext, sizeof(ytext), "+%06d", utc.year); - min_size += 3; /* '+' prefix and two extra year digits. */ - } - - /* Check for insufficient buffer size. */ - if (size < min_size) - return ASTRO_BUFFER_TOO_SMALL; - - /* Format the string. */ - switch (format) - { - case TIME_FORMAT_DAY: - nprinted = snprintf(text, size, "%s-%02d-%02d", - ytext, utc.month, utc.day); - break; - - case TIME_FORMAT_MINUTE: - nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02dZ", - ytext, utc.month, utc.day, - utc.hour, utc.minute); - break; - - case TIME_FORMAT_SECOND: - nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02d:%02.0lfZ", - ytext, utc.month, utc.day, - utc.hour, utc.minute, floor(utc.second)); - break; - - case TIME_FORMAT_MILLI: - nprinted = snprintf(text, size, "%s-%02d-%02dT%02d:%02d:%06.3lfZ", - ytext, utc.month, utc.day, - utc.hour, utc.minute, floor(1000.0 * utc.second) / 1000.0); - break; - - default: - /* We should have already failed for any unknown 'format' value. */ - return ASTRO_INTERNAL_ERROR; - } - - if (nprinted < 0) - return ASTRO_INTERNAL_ERROR; /* should not be possible for snprintf to return a negative number */ - - if ((size_t)(1+nprinted) != min_size) - return ASTRO_INTERNAL_ERROR; /* there must be a bug calculating min_size or formatting the string */ - - return ASTRO_SUCCESS; -} - - /** * @brief Creates an observer object that represents a location on or near the surface of the Earth. * From 4f83a8392a74f30a29130fd82228c75e81eed4d0 Mon Sep 17 00:00:00 2001 From: Don Cross Date: Sat, 4 Nov 2023 13:53:15 -0400 Subject: [PATCH 9/9] Generated C code and ran unit tests. --- generate/template/astronomy.c | 6 +- source/c/README.md | 8 +- source/c/astronomy.c | 278 +++++++++++++++++++++++++++++----- 3 files changed, 245 insertions(+), 47 deletions(-) diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 3bee5b80..bf520ec1 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1357,7 +1357,7 @@ astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) } /* @param calendar - * The calendar to use for date formatting. + * The calendar to use for date formatting. * 0 = automatic determination based on date * 1 = Julian calendar * 2 = Gregorian calendar @@ -1494,7 +1494,7 @@ static astro_status_t FormatTime( * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. * If the civil time is outside the year range -999999 to +999999, the function fails * and returns `ASTRO_BAD_TIME`. Dates before the Julian to Gregorian calender changeover - * (1582-10-15) are treated as dates in the Gregorian calendar even though the Julian + * (1582-10-15) are treated as dates in the Gregorian calendar even though the Julian * calendar was actually in effect. * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. * The year 2 BC is represented by -1, etc. @@ -1543,7 +1543,7 @@ astro_status_t Astronomy_FormatTime( * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. * If the civil time is outside the year range -999999 to +999999, the function fails * and returns `ASTRO_BAD_TIME`. Dates after the Julian to Gregorian calender changeover - * (1582-10-15) are treated as dates in the Julian calendar even though the Gregorian + * (1582-10-15) are treated as dates in the Julian calendar even though the Gregorian * calendar was actually in effect. * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. * The year 2 BC is represented by -1, etc. diff --git a/source/c/README.md b/source/c/README.md index 505495e5..11762935 100644 --- a/source/c/README.md +++ b/source/c/README.md @@ -763,7 +763,7 @@ Correction for aberration is optional, using the `aberration` parameter. ### Astronomy_FormatTime(time, format, text, size) ⇒ [`astro_status_t`](#astro_status_t) -**Formats an [`astro_time_t`](#astro_time_t) value as an ISO 8601 string.** +**Formats an [`astro_time_t`](#astro_time_t) value as an ISO 8601 string using the Gregorian calendar.** @@ -777,7 +777,7 @@ Given an [`astro_time_t`](#astro_time_t) value `time`, formats it as an ISO 8601 | Type | Parameter | Description | | --- | --- | --- | -| [`astro_time_t`](#astro_time_t) | `time` | The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. If the civil time is outside the year range -999999 to +999999, the function fails and returns `ASTRO_BAD_TIME`. Years prior to 1583 are treated as if they are using the modern Gregorian calendar, even when the Julian calendar was actually in effect. The year before 1 AD, commonly known as 1 BC, is represented by the value 0. The year 2 BC is represented by -1, etc. | +| [`astro_time_t`](#astro_time_t) | `time` | The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. If the civil time is outside the year range -999999 to +999999, the function fails and returns `ASTRO_BAD_TIME`. Dates before the Julian to Gregorian calender changeover (1582-10-15) are treated as dates in the Gregorian calendar even though the Julian calendar was actually in effect. The year before 1 AD, commonly known as 1 BC, is represented by the value 0. The year 2 BC is represented by -1, etc. | | [`astro_time_format_t`](#astro_time_format_t) | `format` | Specifies the resolution to which the date and time should be formatted, as explained at [`astro_time_format_t`](#astro_time_format_t). If the value of `format` is not recognized, the function fails and returns `ASTRO_INVALID_PARAMETER`. | | `char *` | `text` | A pointer to a text buffer to receive the output. If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. If the function fails for any reason, and `text` is not `NULL`, and `size` is greater than 0, the `text` buffer is set to an empty string. | | `size_t` | `size` | The size in bytes of the buffer pointed to by `text`. The buffer must be large enough to accomodate the output format selected by the `format` parameter, as specified at [`astro_time_format_t`](#astro_time_format_t). If `size` is too small to hold the string as specified by `format`, the `text` buffer is set to `""` (if possible) and the function returns `ASTRO_BUFFER_TOO_SMALL`. A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. | @@ -3202,11 +3202,11 @@ This function is similar to [`Astronomy_MakeTime`](#Astronomy_MakeTime), only it ### Astronomy_UtcFromTime(time) ⇒ [`astro_utc_t`](#astro_utc_t) -**Determines the calendar year, month, day, and time from an [`astro_time_t`](#astro_time_t) value.** +**Determines the Gregorian calendar year, month, day, and time from an [`astro_time_t`](#astro_time_t) value.** -After calculating the date and time of an astronomical event in the form of an [`astro_time_t`](#astro_time_t) value, it is often useful to display the result in a human-readable form. This function converts the linear time scales in the `ut` field of [`astro_time_t`](#astro_time_t) into a calendar date and time: year, month, day, hours, minutes, and seconds, expressed in UTC. +After calculating the date and time of an astronomical event in the form of an [`astro_time_t`](#astro_time_t) value, it is often useful to display the result in a human-readable form. This function converts the linear time scales in the `ut` field of [`astro_time_t`](#astro_time_t) into a Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed in UTC. diff --git a/source/c/astronomy.c b/source/c/astronomy.c index dde8c618..b798519c 100644 --- a/source/c/astronomy.c +++ b/source/c/astronomy.c @@ -1226,12 +1226,12 @@ astro_time_t Astronomy_TimeFromUtc(astro_utc_t utc) } /** - * @brief Determines the calendar year, month, day, and time from an #astro_time_t value. + * @brief Determines the Gregorian calendar year, month, day, and time from an #astro_time_t value. * * After calculating the date and time of an astronomical event in the form of * an #astro_time_t value, it is often useful to display the result in a human-readable * form. This function converts the linear time scales in the `ut` field of #astro_time_t - * into a calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * into a Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed * in UTC. * * @param time The astronomical time value to be converted to calendar date and time. @@ -1282,51 +1282,98 @@ astro_utc_t Astronomy_UtcFromTime(astro_time_t time) return utc; } +/** + * @brief Determines the Julian calendar year, month, day, and time from an #astro_time_t value. + * + * After calculating the date and time of an astronomical event in the form of + * an #astro_time_t value, it is often useful to display the result in a human-readable + * form. This function converts the linear time scales in the `ut` field of #astro_time_t + * into a Julian calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * in UTC. + * + * @param time The astronomical time value to be converted to calendar date and time. + * @return A date and time broken out into conventional year, month, day, hour, minute, and second. + */ +astro_utc_t Astronomy_UtcFromTime_Julian(astro_time_t time) +{ + astro_utc_t utc; + + double julianDatePlus12Hours = time.ut + 2451545 + 0.5; + int64_t J = (int64_t)floor(julianDatePlus12Hours); + + /* + Algorithm adapted from Richards, E.G. 2012, "Calendars," from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. + See: https://aa.usno.navy.mil/downloads/c15_usb_online.pdf + */ + int64_t cycles = 0; + + if (J < 0) { + cycles = -J / 1461 + 1; + J += cycles * 1461; + } + + const int64_t f = J + 1401; + const int64_t e = 4 * f + 3; + const int64_t g = (e % 1461) / 4; + const int64_t h = 5 * g + 2; + utc.day = (h % 153) / 5 + 1; + utc.month = ((h / 153 + 2) % 12) + 1; + utc.year = e / 1461 - 4716 + (12 + 2 - utc.month) / 12; + + if (cycles > 0) + utc.year -= cycles * 4; + + double ignore; + double dayFraction = modf(julianDatePlus12Hours, &ignore); + if (dayFraction < 0) + dayFraction += 1; + + double hour, minute; + double hourFraction = modf(dayFraction * 24, &hour); + double minuteFraction = modf(hourFraction * 60, &minute); + + utc.hour = (int)hour; + utc.minute = (int)minute; + utc.second = minuteFraction * 60; + + return utc; +} /** - * @brief Formats an #astro_time_t value as an ISO 8601 string. + * @brief Determines the Julian or Gregorian calendar year, month, day, and time from an #astro_time_t value. * - * Given an #astro_time_t value `time`, formats it as an ISO 8601 - * string to the resolution specified by the `format` parameter. - * The result is stored in the `text` buffer whose capacity in bytes - * is specified by `size`. + * Dates before the Julian to Gregorian calender changeover (1582-10-15) are treated + * as dates in the Julian calendar, while later dates are treated as dates in the Gregorian calendar. * - * @param time - * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. - * If the civil time is outside the year range -999999 to +999999, the function fails - * and returns `ASTRO_BAD_TIME`. Years prior to 1583 are treated as if they are - * using the modern Gregorian calendar, even when the Julian calendar was actually in effect. - * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. - * The year 2 BC is represented by -1, etc. + * After calculating the date and time of an astronomical event in the form of + * an #astro_time_t value, it is often useful to display the result in a human-readable + * form. This function converts the linear time scales in the `ut` field of #astro_time_t + * into a Julian or Gregorian calendar date and time: year, month, day, hours, minutes, and seconds, expressed + * in UTC. * - * @param format - * Specifies the resolution to which the date and time should be formatted, - * as explained at #astro_time_format_t. - * If the value of `format` is not recognized, the function fails and - * returns `ASTRO_INVALID_PARAMETER`. - * - * @param text - * A pointer to a text buffer to receive the output. - * If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. - * If the function fails for any reason, and `text` is not `NULL`, - * and `size` is greater than 0, the `text` buffer is set to an empty string. - * - * @param size - * The size in bytes of the buffer pointed to by `text`. The buffer must - * be large enough to accomodate the output format selected by the - * `format` parameter, as specified at #astro_time_format_t. - * If `size` is too small to hold the string as specified by `format`, - * the `text` buffer is set to `""` (if possible) - * and the function returns `ASTRO_BUFFER_TOO_SMALL`. - * A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. - * - * @return `ASTRO_SUCCESS` on success; otherwise an error as described in the parameter notes. + * @param time The astronomical time value to be converted to calendar date and time. + * @return A date and time broken out into conventional year, month, day, hour, minute, and second. */ -astro_status_t Astronomy_FormatTime( +astro_utc_t Astronomy_UtcFromTime_Auto(astro_time_t time) +{ + if (time.ut < -152384.5) + return Astronomy_UtcFromTime_Julian(time); + else + return Astronomy_UtcFromTime(time); +} + +/* @param calendar + * The calendar to use for date formatting. + * 0 = automatic determination based on date + * 1 = Julian calendar + * 2 = Gregorian calendar + */ +static astro_status_t FormatTime( astro_time_t time, astro_time_format_t format, char *text, - size_t size) + size_t size, + int calendar) { int nprinted; double rounding; @@ -1372,8 +1419,13 @@ astro_status_t Astronomy_FormatTime( /* Perform rounding. */ time.ut += rounding; - /* Convert linear J2000 days to Gregorian UTC date/time. */ - utc = Astronomy_UtcFromTime(time); + /* Convert linear J2000 days to Julian or Gregorian UTC date/time. */ + if (calendar == 0) + utc = Astronomy_UtcFromTime_Auto(time); + else if (calendar == 1) + utc = Astronomy_UtcFromTime_Julian(time); + else + utc = Astronomy_UtcFromTime(time); if (utc.year < -999999 || utc.year > +999999) return ASTRO_BAD_TIME; @@ -1436,6 +1488,152 @@ astro_status_t Astronomy_FormatTime( return ASTRO_SUCCESS; } +/** + * @brief Formats an #astro_time_t value as an ISO 8601 string using the Gregorian calendar. + * + * Given an #astro_time_t value `time`, formats it as an ISO 8601 + * string to the resolution specified by the `format` parameter. + * The result is stored in the `text` buffer whose capacity in bytes + * is specified by `size`. + * + * @param time + * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. + * If the civil time is outside the year range -999999 to +999999, the function fails + * and returns `ASTRO_BAD_TIME`. Dates before the Julian to Gregorian calender changeover + * (1582-10-15) are treated as dates in the Gregorian calendar even though the Julian + * calendar was actually in effect. + * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. + * The year 2 BC is represented by -1, etc. + * + * @param format + * Specifies the resolution to which the date and time should be formatted, + * as explained at #astro_time_format_t. + * If the value of `format` is not recognized, the function fails and + * returns `ASTRO_INVALID_PARAMETER`. + * + * @param text + * A pointer to a text buffer to receive the output. + * If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. + * If the function fails for any reason, and `text` is not `NULL`, + * and `size` is greater than 0, the `text` buffer is set to an empty string. + * + * @param size + * The size in bytes of the buffer pointed to by `text`. The buffer must + * be large enough to accomodate the output format selected by the + * `format` parameter, as specified at #astro_time_format_t. + * If `size` is too small to hold the string as specified by `format`, + * the `text` buffer is set to `""` (if possible) + * and the function returns `ASTRO_BUFFER_TOO_SMALL`. + * A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. + * + * @return `ASTRO_SUCCESS` on success; otherwise an error as described in the parameter notes. + */ +astro_status_t Astronomy_FormatTime( + astro_time_t time, + astro_time_format_t format, + char *text, + size_t size) +{ + return FormatTime(time, format, text, size, 2); +} + +/** + * @brief Formats an #astro_time_t value as an ISO 8601 string using the Julian calendar. + * + * Given an #astro_time_t value `time`, formats it as an ISO 8601 + * string to the resolution specified by the `format` parameter. + * The result is stored in the `text` buffer whose capacity in bytes + * is specified by `size`. + * + * @param time + * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. + * If the civil time is outside the year range -999999 to +999999, the function fails + * and returns `ASTRO_BAD_TIME`. Dates after the Julian to Gregorian calender changeover + * (1582-10-15) are treated as dates in the Julian calendar even though the Gregorian + * calendar was actually in effect. + * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. + * The year 2 BC is represented by -1, etc. + * + * @param format + * Specifies the resolution to which the date and time should be formatted, + * as explained at #astro_time_format_t. + * If the value of `format` is not recognized, the function fails and + * returns `ASTRO_INVALID_PARAMETER`. + * + * @param text + * A pointer to a text buffer to receive the output. + * If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. + * If the function fails for any reason, and `text` is not `NULL`, + * and `size` is greater than 0, the `text` buffer is set to an empty string. + * + * @param size + * The size in bytes of the buffer pointed to by `text`. The buffer must + * be large enough to accomodate the output format selected by the + * `format` parameter, as specified at #astro_time_format_t. + * If `size` is too small to hold the string as specified by `format`, + * the `text` buffer is set to `""` (if possible) + * and the function returns `ASTRO_BUFFER_TOO_SMALL`. + * A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. + * + * @return `ASTRO_SUCCESS` on success; otherwise an error as described in the parameter notes. + */ +astro_status_t Astronomy_FormatTime_Julian( + astro_time_t time, + astro_time_format_t format, + char *text, + size_t size) +{ + return FormatTime(time, format, text, size, 1); +} + +/** + * @brief Formats an #astro_time_t value as an ISO 8601 string using the Julian or Gregorian calendar. + * + * Given an #astro_time_t value `time`, formats it as an ISO 8601 + * string to the resolution specified by the `format` parameter. + * The result is stored in the `text` buffer whose capacity in bytes + * is specified by `size`. + * + * @param time + * The date and time whose civil time `time.ut` is to be formatted as an ISO 8601 string. + * If the civil time is outside the year range -999999 to +999999, the function fails + * and returns `ASTRO_BAD_TIME`. Dates before the Julian to Gregorian calender changeover + * (1582-10-15) are treated as dates in the Julian calendar, while later dates are treated + * as dates in the Gregorian calendar. + * The year before 1 AD, commonly known as 1 BC, is represented by the value 0. + * The year 2 BC is represented by -1, etc. + * + * @param format + * Specifies the resolution to which the date and time should be formatted, + * as explained at #astro_time_format_t. + * If the value of `format` is not recognized, the function fails and + * returns `ASTRO_INVALID_PARAMETER`. + * + * @param text + * A pointer to a text buffer to receive the output. + * If `text` is `NULL`, this function returns `ASTRO_INVALID_PARAMETER`. + * If the function fails for any reason, and `text` is not `NULL`, + * and `size` is greater than 0, the `text` buffer is set to an empty string. + * + * @param size + * The size in bytes of the buffer pointed to by `text`. The buffer must + * be large enough to accomodate the output format selected by the + * `format` parameter, as specified at #astro_time_format_t. + * If `size` is too small to hold the string as specified by `format`, + * the `text` buffer is set to `""` (if possible) + * and the function returns `ASTRO_BUFFER_TOO_SMALL`. + * A buffer that is `TIME_TEXT_BYTES` (28) bytes or larger is always large enough for this function. + * + * @return `ASTRO_SUCCESS` on success; otherwise an error as described in the parameter notes. + */ +astro_status_t Astronomy_FormatTime_Auto( + astro_time_t time, + astro_time_format_t format, + char *text, + size_t size) +{ + return FormatTime(time, format, text, size, 0); +} /** * @brief Creates an observer object that represents a location on or near the surface of the Earth.