From 1e95f0656bf62155ab4975b8d40e111baa3ec2d3 Mon Sep 17 00:00:00 2001 From: Don Cross Date: Thu, 6 Oct 2022 16:28:55 -0400 Subject: [PATCH] C, C#, Python: Support formatting calendar years -999999 to +999999. --- demo/python/astronomy.py | 12 ++++++++---- generate/dotnet/csharp_test/csharp_test.cs | 4 ++-- generate/template/astronomy.c | 22 ++++++++++------------ generate/template/astronomy.cs | 12 ++++++++---- generate/template/astronomy.py | 12 ++++++++---- generate/test.py | 8 ++++---- source/c/astronomy.c | 22 ++++++++++------------ source/csharp/astronomy.cs | 12 ++++++++---- source/python/astronomy/astronomy.py | 12 ++++++++---- 9 files changed, 66 insertions(+), 50 deletions(-) diff --git a/demo/python/astronomy.py b/demo/python/astronomy.py index 7c4650b3..dd86e5ec 100644 --- a/demo/python/astronomy.py +++ b/demo/python/astronomy.py @@ -577,7 +577,7 @@ def _UniversalTime(tt): return ut dt += err -_TimeRegex = re.compile(r'^([\+\-]?[0-9]{1,5})-([0-9]{2})-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2}(\.[0-9]+)?))?Z)?$') +_TimeRegex = re.compile(r'^([\+\-]?[0-9]+)-([0-9]{2})-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2}(\.[0-9]+)?))?Z)?$') class Time: """Represents a date and time used for performing astronomy calculations. @@ -809,9 +809,13 @@ class Time: month = month + 2 - 12 * k year = 100 * (n - 49) + m + k millis = max(0, min(59999, round(1000.0 * second))) - text = '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}Z'.format(year, month, day, hour, minute, millis // 1000, millis % 1000) - if year > 9999: - text = '+' + text + if year < 0: + text = '-{:06d}'.format(-year) + elif year <= 9999: + text = '{:04d}'.format(year) + else: + text = '+{:06d}'.format(year) + text += '-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}Z'.format(month, day, hour, minute, millis // 1000, millis % 1000) return text def Utc(self): diff --git a/generate/dotnet/csharp_test/csharp_test.cs b/generate/dotnet/csharp_test/csharp_test.cs index 987b043c..83d68cbc 100644 --- a/generate/dotnet/csharp_test/csharp_test.cs +++ b/generate/dotnet/csharp_test/csharp_test.cs @@ -423,8 +423,8 @@ namespace csharp_test // Make sure we can handle dates outside the range supported by System.DateTime. // https://github.com/cosinekitty/astronomy/issues/250 if (0 != CheckDecemberSolstice( 2022, "2022-12-21T21:47:58.189Z")) return 1; - if (0 != CheckDecemberSolstice(-2300, "-2300-12-19T16:22:26.325Z")) return 1; - if (0 != CheckDecemberSolstice(12345, "+12345-12-11T13:30:10.041Z")) return 1; + if (0 != CheckDecemberSolstice(-2300, "-002300-12-19T16:22:26.325Z")) return 1; + if (0 != CheckDecemberSolstice(12345, "+012345-12-11T13:30:10.041Z")) return 1; Console.WriteLine("C# DatesIssue250: PASS"); return 0; } diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 80f9a44b..81395d4a 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1117,24 +1117,22 @@ astro_status_t Astronomy_FormatTime( /* Convert linear J2000 days to Gregorian UTC date/time. */ utc = Astronomy_UtcFromTime(time); + if (utc.year < -999999 || utc.year > +999999) + return ASTRO_BAD_TIME; - if (utc.year >= 0 && utc.year <= 9999) + 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 { - /* ISO 8600 allows for years outside the usual range. */ - /* In this case, try to fit the year in a 6-digit format, with a + or - prefix. */ - if (utc.year < -999999 || utc.year > +999999) - return ASTRO_BAD_TIME; - - if (utc.year < 0) - snprintf(ytext, sizeof(ytext), "%07d", utc.year); - else - snprintf(ytext, sizeof(ytext), "+%06d", utc.year); - - min_size += 3; /* two extra year digits and an extra +/- prefix. */ + snprintf(ytext, sizeof(ytext), "+%06d", utc.year); + min_size += 3; /* '+' prefix and two extra year digits. */ } /* Check for insufficient buffer size. */ diff --git a/generate/template/astronomy.cs b/generate/template/astronomy.cs index 80881efe..ef79e3b7 100644 --- a/generate/template/astronomy.cs +++ b/generate/template/astronomy.cs @@ -290,10 +290,14 @@ namespace CosineKitty { var d = new CalendarDateTime(ut); int millis = Math.Max(0, Math.Min(59999, (int)Math.Round(d.second * 1000.0))); - string s = $"{d.year:0000}-{d.month:00}-{d.day:00}T{d.hour:00}:{d.minute:00}:{millis/1000:00}.{millis%1000:000}Z"; - if (d.year > 9999) - s = "+" + s; - return s; + string y; + if (d.year < 0) + y = "-" + (-d.year).ToString("000000"); + else if (d.year <= 9999) + y = d.year.ToString("0000"); + else + y = "+" + d.year.ToString("000000"); + return $"{y}-{d.month:00}-{d.day:00}T{d.hour:00}:{d.minute:00}:{millis/1000:00}.{millis%1000:000}Z"; } /// diff --git a/generate/template/astronomy.py b/generate/template/astronomy.py index 99b0a43a..7c908617 100644 --- a/generate/template/astronomy.py +++ b/generate/template/astronomy.py @@ -577,7 +577,7 @@ def _UniversalTime(tt): return ut dt += err -_TimeRegex = re.compile(r'^([\+\-]?[0-9]{1,5})-([0-9]{2})-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2}(\.[0-9]+)?))?Z)?$') +_TimeRegex = re.compile(r'^([\+\-]?[0-9]+)-([0-9]{2})-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2}(\.[0-9]+)?))?Z)?$') class Time: """Represents a date and time used for performing astronomy calculations. @@ -809,9 +809,13 @@ class Time: month = month + 2 - 12 * k year = 100 * (n - 49) + m + k millis = max(0, min(59999, round(1000.0 * second))) - text = '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}Z'.format(year, month, day, hour, minute, millis // 1000, millis % 1000) - if year > 9999: - text = '+' + text + if year < 0: + text = '-{:06d}'.format(-year) + elif year <= 9999: + text = '{:04d}'.format(year) + else: + text = '+{:06d}'.format(year) + text += '-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}Z'.format(month, day, hour, minute, millis // 1000, millis % 1000) return text def Utc(self): diff --git a/generate/test.py b/generate/test.py index 89f6cebd..00e415f8 100755 --- a/generate/test.py +++ b/generate/test.py @@ -95,8 +95,8 @@ def AstroTime(): AssertBadTime('1971-12-31T23:00:60Z') AssertBadTime('1971-03-17T03:30:55.976') # Extreme year values... - AssertGoodTime('-2300-12-19T16:22:26.325Z', '-2300-12-19T16:22:26.325Z') - AssertGoodTime('+12345-12-11T13:30:10.041Z', '+12345-12-11T13:30:10.041Z') + AssertGoodTime('-2300-12-19T16:22:26.325Z', '-002300-12-19T16:22:26.325Z') + AssertGoodTime('+12345-12-11T13:30:10.041Z', '+012345-12-11T13:30:10.041Z') return 0 #----------------------------------------------------------------------------------------------------------- @@ -2867,8 +2867,8 @@ def DatesIssue250(): # https://github.com/cosinekitty/astronomy/issues/250 return ( CheckDecemberSolstice( 2022, "2022-12-21T21:47:58.189Z") or - CheckDecemberSolstice(-2300, "-2300-12-19T16:22:26.325Z") or - CheckDecemberSolstice(12345, "+12345-12-11T13:30:10.041Z") or + CheckDecemberSolstice(-2300, "-002300-12-19T16:22:26.325Z") or + CheckDecemberSolstice(12345, "+012345-12-11T13:30:10.041Z") or Pass('DatesIssue250') ) diff --git a/source/c/astronomy.c b/source/c/astronomy.c index 350035ed..9576194e 100644 --- a/source/c/astronomy.c +++ b/source/c/astronomy.c @@ -1123,24 +1123,22 @@ astro_status_t Astronomy_FormatTime( /* Convert linear J2000 days to Gregorian UTC date/time. */ utc = Astronomy_UtcFromTime(time); + if (utc.year < -999999 || utc.year > +999999) + return ASTRO_BAD_TIME; - if (utc.year >= 0 && utc.year <= 9999) + 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 { - /* ISO 8600 allows for years outside the usual range. */ - /* In this case, try to fit the year in a 6-digit format, with a + or - prefix. */ - if (utc.year < -999999 || utc.year > +999999) - return ASTRO_BAD_TIME; - - if (utc.year < 0) - snprintf(ytext, sizeof(ytext), "%07d", utc.year); - else - snprintf(ytext, sizeof(ytext), "+%06d", utc.year); - - min_size += 3; /* two extra year digits and an extra +/- prefix. */ + snprintf(ytext, sizeof(ytext), "+%06d", utc.year); + min_size += 3; /* '+' prefix and two extra year digits. */ } /* Check for insufficient buffer size. */ diff --git a/source/csharp/astronomy.cs b/source/csharp/astronomy.cs index 32f42faa..16535626 100644 --- a/source/csharp/astronomy.cs +++ b/source/csharp/astronomy.cs @@ -290,10 +290,14 @@ namespace CosineKitty { var d = new CalendarDateTime(ut); int millis = Math.Max(0, Math.Min(59999, (int)Math.Round(d.second * 1000.0))); - string s = $"{d.year:0000}-{d.month:00}-{d.day:00}T{d.hour:00}:{d.minute:00}:{millis/1000:00}.{millis%1000:000}Z"; - if (d.year > 9999) - s = "+" + s; - return s; + string y; + if (d.year < 0) + y = "-" + (-d.year).ToString("000000"); + else if (d.year <= 9999) + y = d.year.ToString("0000"); + else + y = "+" + d.year.ToString("000000"); + return $"{y}-{d.month:00}-{d.day:00}T{d.hour:00}:{d.minute:00}:{millis/1000:00}.{millis%1000:000}Z"; } /// diff --git a/source/python/astronomy/astronomy.py b/source/python/astronomy/astronomy.py index 7c4650b3..dd86e5ec 100644 --- a/source/python/astronomy/astronomy.py +++ b/source/python/astronomy/astronomy.py @@ -577,7 +577,7 @@ def _UniversalTime(tt): return ut dt += err -_TimeRegex = re.compile(r'^([\+\-]?[0-9]{1,5})-([0-9]{2})-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2}(\.[0-9]+)?))?Z)?$') +_TimeRegex = re.compile(r'^([\+\-]?[0-9]+)-([0-9]{2})-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2}(\.[0-9]+)?))?Z)?$') class Time: """Represents a date and time used for performing astronomy calculations. @@ -809,9 +809,13 @@ class Time: month = month + 2 - 12 * k year = 100 * (n - 49) + m + k millis = max(0, min(59999, round(1000.0 * second))) - text = '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}Z'.format(year, month, day, hour, minute, millis // 1000, millis % 1000) - if year > 9999: - text = '+' + text + if year < 0: + text = '-{:06d}'.format(-year) + elif year <= 9999: + text = '{:04d}'.format(year) + else: + text = '+{:06d}'.format(year) + text += '-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}Z'.format(month, day, hour, minute, millis // 1000, millis % 1000) return text def Utc(self):