From 6fe11eaa57aa2d2086b5d59d4f352ef05f96f881 Mon Sep 17 00:00:00 2001 From: Trevor Pilley Date: Tue, 5 Nov 2024 17:52:38 +0000 Subject: [PATCH] Add NumberToDialFrom --- README.md | 17 ++++ src/PhoneNumbers/PhoneNumberExtensions.cs | 77 ++++++++++++++++++- .../PhoneNumberExtensionsTests.cs | 61 +++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c96b6c11e..ff1e965ce 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,23 @@ To out out of specific countries but still use any new ones added in future vers ParseOptions.Default.Countries.Remove(CountryInfo.X); ``` +## Dial helper + +The library contains helper methods to determine the number that needs to be dialled for a call between two phone numbers. + +```csharp +var callerNumber = PhoneNumber.Parse("+441142726444"); + +PhoneNumber.Parse("+441146548866").NumberToDialFrom(callerNumber); // 6548866 UK local dialling within same NDC +PhoneNumber.Parse("+447106865391").NumberToDialFrom(callerNumber); // 07106865391 UK mobile from UK landline +PhoneNumber.Parse("+33140477283").NumberToDialFrom(callerNumber); // 0033140477283 French mobile from UK landline + +// Alternatively a destination number from a specific country +PhoneNumber.Parse("+441146548866").NumberToDialFrom(CountryInfo.UnitedKingdom); // 01146548866 UK landline from UK +PhoneNumber.Parse("+447106865391").NumberToDialFrom(CountryInfo.UnitedKingdom); // 07106865391 UK mobile from UK +PhoneNumber.Parse("+33140477283").NumberToDialFrom(CountryInfo.UnitedKingdom); // 0033140477283 French mobile from UK +``` + ## Versioning The library adheres to [Semantic Versioning](https://semver.org) and [release notes](https://github.com/TrevorPilley/phone-number-parser/releases) are provided for every published version. diff --git a/src/PhoneNumbers/PhoneNumberExtensions.cs b/src/PhoneNumbers/PhoneNumberExtensions.cs index 013cab90d..7b0cab382 100644 --- a/src/PhoneNumbers/PhoneNumberExtensions.cs +++ b/src/PhoneNumbers/PhoneNumberExtensions.cs @@ -3,8 +3,79 @@ namespace PhoneNumbers; /// /// A class containing extension methods for the class. /// -internal static class PhoneNumberExtensions +public static class PhoneNumberExtensions { + /// + /// Determines the number needed to dial a from another . + /// + /// The to dial. + /// The to dial from. + /// Thrown if the specified or is null. + /// Returns the number needed to dial a from another . + public static string NumberToDialFrom(this PhoneNumber destination, CountryInfo countryInfo) + { + if (destination is null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (countryInfo is null) + { + throw new ArgumentNullException(nameof(countryInfo)); + } + + if (destination.Country.SharesCallingCodeWith(countryInfo)) + { + return destination.ToString("U"); + } + + if (countryInfo.InternationalCallPrefixes.TryGetValue(destination.Country.CallingCode, out var callPrefix)) + { + return $"{callPrefix}{destination.NationalSignificantNumber}"; + } + + if (countryInfo == CountryInfo.Italy && destination.IsSanMarinoLandline()) + { + return !destination.SubscriberNumber.StartsWith("0549", StringComparison.Ordinal) + ? $"0549{destination.SubscriberNumber}" + : destination.SubscriberNumber; + } + + return $"{countryInfo.InternationalCallPrefix}{destination.Country.CallingCode}{destination.NationalSignificantNumber}"; + } + + /// + /// Determines the number needed to dial a from another . + /// + /// The to dial. + /// The to dial from. + /// Thrown if the specified or is null. + /// Returns the number needed to dial a from another . + public static string NumberToDialFrom(this PhoneNumber destination, PhoneNumber source) + { + if (destination is null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (source.Country.SharesCallingCodeWith(destination.Country)) + { + if (source.SharesNdcWith(destination) && source.NdcIsOptional()) + { + return destination.SubscriberNumber; + } + + return destination.ToString("U"); + } + + return NumberToDialFrom(destination, source.Country); + } + /// /// Gets a value indicating if the National Destination Code is optional for the specified instance. /// @@ -15,4 +86,8 @@ internal static bool NdcIsOptional(this PhoneNumber phoneNumber) => phoneNumber.Country.AllowsLocalGeographicDialling && phoneNumber.Kind == PhoneNumberKind.GeographicPhoneNumber && !((GeographicPhoneNumber)phoneNumber).NationalDiallingOnly; + + private static bool IsSanMarinoLandline(this PhoneNumber phoneNumber) => + phoneNumber.Country == CountryInfo.SanMarino && + (phoneNumber.SubscriberNumber[0] == '0' || phoneNumber.SubscriberNumber[0] == '7' || phoneNumber.SubscriberNumber[0] == '8'); } diff --git a/test/PhoneNumbers.Tests/PhoneNumberExtensionsTests.cs b/test/PhoneNumbers.Tests/PhoneNumberExtensionsTests.cs index 014bb771d..be2289af0 100644 --- a/test/PhoneNumbers.Tests/PhoneNumberExtensionsTests.cs +++ b/test/PhoneNumbers.Tests/PhoneNumberExtensionsTests.cs @@ -9,4 +9,65 @@ public void NdcIsOptional_False_For_Geographic_Number_With_Local_Dialling_Allowe [Fact] public void NdcIsOptional_True_For_Geographic_Number_With_Local_Dialling_Allowed_Where_NDC_Is_Not_National_Dialling_Only() => Assert.True(TestHelper.CreateGeographicPhoneNumber("0", "1", "123", true, PhoneNumberHint.None).NdcIsOptional()); + + [Theory] + [InlineData("GB", "+447106865391", "07106865391")] // UK number from UK + [InlineData("GB", "+441481717000", "01481717000")] // Guernsey number from UK (countries share calling code) + [InlineData("GB", "+33140477283", "0033140477283")] // France number from UK + [InlineData("US", "+33140477283", "01133140477283")] // France number from US + [InlineData("GB", "+12124841200", "0012124841200")] // US number from UK + [InlineData("US", "+447106865391", "011447106865391")] // UK number from US + [InlineData("TZ", "+25420424200", "00520424200")] // Kenya from Tanzania (uses 005 instead of +255) + [InlineData("KE", "+255222199760", "007222199760")] // Tanzania from Kenya (uses 007 instead of +254) + [InlineData("IT", "+3780549882555", "0549882555")] // San Marino NonGeo with Italy NDC from Italy + [InlineData("IT", "+378882555", "0549882555")] // San Marino NonGeo with Italy NDC from Italy + [InlineData("IT", "+378693247", "00378693247")] // San Marino Mobile from Italy + [InlineData("IT", "+378598765", "00378598765")] // San Marino Mobile from Italy + public void NumberToDialFrom_CountryInfo(string sourceCountryCode, string destination, string expected) => + Assert.Equal( + expected, + PhoneNumber.Parse(destination).NumberToDialFrom(ParseOptions.Default.GetCountryInfo(sourceCountryCode))); + + [Fact] + public void NumberToDialFrom_CountryInfo_Throws_If_CountryInfo_Null() + { + var phoneNumber1 = TestHelper.CreateMobilePhoneNumber(null, "1", "123"); + Assert.Throws(() => phoneNumber1.NumberToDialFrom(default(CountryInfo))); + } + + [Fact] + public void NumberToDialFrom_CountryInfo_Throws_If_PhoneNumber_Null() => + Assert.Throws(() => default(PhoneNumber).NumberToDialFrom(CountryInfo.UnitedKingdom)); + + [Fact] + public void NumberToDialFrom_PhoneNumber_Throws_If_CountryInfo_Null() + { + Assert.Throws(() => default(PhoneNumber).NumberToDialFrom(TestHelper.CreateMobilePhoneNumber(null, "1", "123"))); + Assert.Throws(() => TestHelper.CreateMobilePhoneNumber(null, "1", "123").NumberToDialFrom(default(PhoneNumber))); + } + + [Theory] + [InlineData("+441142726444", "+441146548866", "6548866")] // UK Geo to Geo within NDC, local dialling permitted, NDC not required + [InlineData("+441202454877", "+441202653887", "01202653887")] // UK Geo to Geo within NDC, local dialling permitted, NDC required for local dialling due to number shortages + [InlineData("+441142726444", "+441773878912", "01773878912")] // UK Geo to Geo different NDC + [InlineData("+441142726444", "+443038709123", "03038709123")] // UK Geo to NonGeo + [InlineData("+441142726444", "+447106865391", "07106865391")] // UK Geo to Mobile + [InlineData("+441142726444", "+441481717000", "01481717000")] // UK Geo to Guernsey Geo, countries share calling code + [InlineData("+447106865391", "+441142726444", "01142726444")] // UK Mobile to Geo + [InlineData("+447106865391", "+447712674523", "07712674523")] // UK Mobile to Mobile + [InlineData("+447106865391", "+443038709123", "03038709123")] // UK Mobile to NonGeo + [InlineData("+447106865391", "+33140477283", "0033140477283")] // UK Mobile to France Geo + [InlineData("+447106865391", "+33601876543", "0033601876543")] // UK Mobile to France Mobile + [InlineData("+12124841200", "+447712674523", "011447712674523")] // US to UK Mobile + [InlineData("+85229615432", "+85229616333", "29616333")] // HK to HK + [InlineData("+25420424200", "+255222199760", "007222199760")] // Kenya to Tanzania (uses 007 instead of +255) + [InlineData("+255222199760", "+25420424200", "00520424200")] // Tanzania to Kenya (uses 005 instead of +254) + [InlineData("+393492525255", "+3780549882555", "0549882555")] // Italy to San Marino NonGeo with Italy NDC + [InlineData("+393492525255", "+378882555", "0549882555")] // Italy to San Marino NonGeo without Italy NDC + [InlineData("+393492525255", "+378693247", "00378693247")] // Italy to San Marino Mobile + [InlineData("+393492525255", "+378598765", "00378598765")] // Italy to San Marino IP + public void NumberToDialFrom_PhoneNumber(string source, string destination, string expected) => + Assert.Equal( + expected, + PhoneNumber.Parse(destination).NumberToDialFrom(PhoneNumber.Parse(source))); }