diff --git a/csharp/PhoneNumbers/PhoneNumberUtil.cs b/csharp/PhoneNumbers/PhoneNumberUtil.cs
index 951e87f3c..799486722 100644
--- a/csharp/PhoneNumbers/PhoneNumberUtil.cs
+++ b/csharp/PhoneNumbers/PhoneNumberUtil.cs
@@ -19,6 +19,7 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Numerics;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
@@ -224,7 +225,7 @@ private static bool IsValidAlphaPhone(StringBuilder number)
private const string RFC3966_DOMAINNAME =
"^(" + RFC3966_DOMAINLABEL + "\\.)*" + RFC3966_TOPLABEL + "\\.?$";
private static readonly PhoneRegex RFC3966_DOMAINNAME_PATTERN = new PhoneRegex(RFC3966_DOMAINNAME);
-
+
///
/// Helper initialiser method to create the regular-expression pattern to match extensions.
/// Note that there are currently six capturing groups for the extension itself. If this number is
@@ -1200,7 +1201,107 @@ private bool HasValidCountryCallingCode(int countryCallingCode)
return countryCallingCodeToRegionCodeMap.ContainsKey(countryCallingCode);
}
+#if NET6_0_OR_GREATER
///
+ /// Formats a phone number in the specified format using default rules. Note that this does not
+ /// promise to produce a phone number that the user can dial from where they are - although we do
+ /// format in either 'national' or 'international' format depending on what the client asks for, we
+ /// do not currently support a more abbreviated format, such as for users in the same "area" who
+ /// could potentially dial the number without area code. Note that if the phone number has a
+ /// country calling code of 0 or an otherwise invalid country calling code, we cannot work out
+ /// which formatting rules to apply so we return the national significant number with no formatting
+ /// applied.
+ ///
+ /// The phone number to be formatted.
+ /// The format the phone number should be formatted into.
+ /// The formatted phone number.
+ public string Format(PhoneNumber number, PhoneNumberFormat numberFormat)
+ {
+ if (number.NationalNumber == 0 && number.HasRawInput)
+ {
+ var rawInput = number.RawInput;
+ if (rawInput.Length > 0)
+ {
+ return rawInput;
+ }
+ }
+
+ Span formattedNumber = stackalloc char[90];
+ var index = 0;
+ Format(ref formattedNumber, ref index, number, numberFormat);
+ return new string(formattedNumber.Slice(0, index));
+ }
+
+ private void Format(ref Span span, ref int index, PhoneNumber number, PhoneNumberFormat numberFormat)
+ {
+ Span nationalSignificantNumber = stackalloc char[20];
+ SetNationalSignificantNumberToSpan(ref nationalSignificantNumber, number, out var nationalSignificantNumberLength);
+
+ var countryCallingCode = number.CountryCode;
+ if (numberFormat == PhoneNumberFormat.E164)
+ {
+ // Early exit for E164 case since no formatting of the national number needs to be applied.
+ // Extensions are not formatted.
+ PrefixNumberWithCountryCallingCode(ref span, ref index, countryCallingCode, PhoneNumberFormat.E164);
+ for (var i = 0; i < nationalSignificantNumberLength; i++)
+ {
+ span[index++] = nationalSignificantNumber[i];
+ }
+
+ return;
+ }
+
+ // Note getRegionCodeForCountryCode() is used because formatting information for regions which
+ // share a country calling code is contained by only one region for performance reasons. For
+ // example, for NANPA regions it will be contained in the metadata for US.
+ var regionCode = GetRegionCodeForCountryCode(countryCallingCode);
+ if (!HasValidCountryCallingCode(countryCallingCode))
+ {
+ for (var i = 0; i < nationalSignificantNumberLength; i++)
+ {
+ span[index++] = nationalSignificantNumber[i];
+ }
+
+ return;
+ }
+
+ PrefixNumberWithCountryCallingCode(ref span, ref index, countryCallingCode, numberFormat);
+ var metadata = GetMetadataForRegionOrCallingCode(countryCallingCode, regionCode);
+ AppendToSpan(ref span, ref index, FormatNsn(new string(nationalSignificantNumber.Slice(0, nationalSignificantNumberLength)), metadata, numberFormat));
+ MaybeAppendFormattedExtension(ref span, ref index, number, metadata, numberFormat);
+ }
+
+ /**
+ * Appends the formatted extension of a phone number to formattedNumber, if the phone number had
+ * an extension specified.
+ */
+ private static void MaybeAppendFormattedExtension(ref Span span, ref int index,PhoneNumber number, PhoneMetadata metadata,
+ PhoneNumberFormat numberFormat)
+ {
+ if (!number.HasExtension || number.Extension.Length <= 0)
+ {
+ return;
+ }
+
+ if (numberFormat == PhoneNumberFormat.RFC3966)
+ {
+ AppendToSpan(ref span, ref index, RFC3966_EXTN_PREFIX);
+ AppendToSpan(ref span, ref index, number.Extension);
+ return;
+ }
+
+ if (metadata.HasPreferredExtnPrefix)
+ {
+ AppendToSpan(ref span, ref index, metadata.PreferredExtnPrefix);
+ AppendToSpan(ref span, ref index, number.Extension);
+ return;
+ }
+
+ AppendToSpan(ref span, ref index, DEFAULT_EXTN_PREFIX);
+ AppendToSpan(ref span, ref index, number.Extension);
+ }
+#else
+///
/// Formats a phone number in the specified format using default rules. Note that this does not
/// promise to produce a phone number that the user can dial from where they are - although we do
/// format in either 'national' or 'international' format depending on what the client asks for, we
@@ -1266,6 +1367,7 @@ public void Format(PhoneNumber number, PhoneNumberFormat numberFormat,
MaybeAppendFormattedExtension(number, metadata, numberFormat, formattedNumber);
PrefixNumberWithCountryCallingCode(countryCallingCode, numberFormat, formattedNumber);
}
+#endif
///
/// Formats a phone number in the specified format using client-defined formatting rules. Note that
@@ -1857,6 +1959,123 @@ public string FormatOutOfCountryKeepingAlphaChars(PhoneNumber number, string reg
return formattedNumber.ToString();
}
+#if NET6_0_OR_GREATER
+ ///
+ /// Gets the national significant number of the a phone number. Note a national significant number
+ /// doesn't contain a national prefix or any formatting.
+ ///
+ /// The PhoneNumber object for which the national significant number is needed.
+ /// The national significant number of the PhoneNumber object passed in.
+ public string GetNationalSignificantNumber(PhoneNumber number)
+ {
+ // If a leading zero(s) has been set, we prefix this now. Note this is not a national prefix.
+ Span nationalSignificantNumber = stackalloc char[20];
+ SetNationalSignificantNumberToSpan(ref nationalSignificantNumber, number, out var length);
+ return new string(nationalSignificantNumber.Slice(0, length));
+ }
+
+ private static void SetNationalSignificantNumberToSpan(ref Span nationalNumber, PhoneNumber number, out int nationalSignificantNumberLength)
+ {
+ nationalSignificantNumberLength = 0;
+ if (number.ItalianLeadingZero)
+ {
+ for (int i = 0; i < number.NumberOfLeadingZeros; i++)
+ {
+ nationalNumber[nationalSignificantNumberLength++] = '0';
+ }
+ }
+
+ AppendNumberToSpan(ref nationalNumber, ref nationalSignificantNumberLength, number.NationalNumber);
+ }
+
+ private static void AppendNumberToSpan(ref Span span, ref int index, ulong numberToAppend)
+ {
+ if (numberToAppend == 0)
+ {
+ span[index++] = '0';
+ }
+
+ var numberLength = numberToAppend switch
+ {
+ < 10 => 1,
+ < 100 => 2,
+ < 1_000 => 3,
+ _ => (int)Math.Floor(Math.Log10(numberToAppend)) + 1,
+ };
+
+ var maxIndex = index + numberLength - 1;
+ var startIndex = index;
+ while (numberToAppend > 0)
+ {
+ span[maxIndex + startIndex - index++] = (char)('0' + numberToAppend % 10);
+ numberToAppend /= 10;
+ }
+ }
+
+ private static void AppendNumberToSpan(ref Span span, ref int index, int numberToAppend)
+ {
+ if (numberToAppend == 0)
+ {
+ span[index++] = '0';
+ }
+
+ var numberLength = numberToAppend switch
+ {
+ < 10 => 1,
+ < 100 => 2,
+ < 1_000 => 3,
+ _ => (int)Math.Floor(Math.Log10(numberToAppend)) + 1,
+ };
+
+ var maxIndex = index + numberLength - 1;
+ var startIndex = index;
+ while (numberToAppend > 0)
+ {
+ span[maxIndex + startIndex - index++] = (char)('0' + numberToAppend % 10);
+ numberToAppend /= 10;
+ }
+ }
+
+ /**
+ * A helper function that is used by format and formatByPattern.
+ */
+ private void PrefixNumberWithCountryCallingCode(ref Span span,
+ ref int currentIndex,
+ int countryCallingCode,
+ PhoneNumberFormat numberFormat)
+ {
+ switch (numberFormat)
+ {
+ case PhoneNumberFormat.E164:
+ span[currentIndex++] = PLUS_SIGN;
+ AppendNumberToSpan(ref span, ref currentIndex, countryCallingCode);
+ return;
+ case PhoneNumberFormat.INTERNATIONAL:
+ span[currentIndex++] = PLUS_SIGN;
+ AppendNumberToSpan(ref span, ref currentIndex, countryCallingCode);
+ span[currentIndex++] = ' ';
+ return;
+ case PhoneNumberFormat.RFC3966:
+ AppendToSpan(ref span, ref currentIndex, RFC3966_PREFIX);
+ span[currentIndex++] = PLUS_SIGN;
+ AppendNumberToSpan(ref span, ref currentIndex, countryCallingCode);
+ span[currentIndex++] = '-';
+ return;
+ case PhoneNumberFormat.NATIONAL:
+ return;
+ default:
+ return;
+ }
+ }
+
+ private static void AppendToSpan(ref Span destination, ref int index, string valueToAppend)
+ {
+ foreach (var c in valueToAppend)
+ {
+ destination[index++] = c;
+ }
+ }
+#else
///
/// Gets the national significant number of the a phone number. Note a national significant number
/// doesn't contain a national prefix or any formatting.
@@ -1872,7 +2091,7 @@ public string GetNationalSignificantNumber(PhoneNumber number)
nationalNumber.Append(number.NationalNumber);
return nationalNumber.ToString();
}
-
+#endif
/**
* A helper function that is used by format and formatByPattern.
*/
@@ -1889,7 +2108,7 @@ private void PrefixNumberWithCountryCallingCode(int countryCallingCode,
return;
case PhoneNumberFormat.RFC3966:
formattedNumber.Insert(0, "-").Insert(0, countryCallingCode).Insert(0, PLUS_SIGN)
- .Insert(0, RFC3966_PREFIX);
+ .Insert(0, RFC3966_PREFIX);
return;
case PhoneNumberFormat.NATIONAL:
return;