diff --git a/README.md b/README.md
index 6efe0d7a..115944fe 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 013cab90..7b0cab38 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 014bb771..f8f3170a 100644
--- a/test/PhoneNumbers.Tests/PhoneNumberExtensionsTests.cs
+++ b/test/PhoneNumbers.Tests/PhoneNumberExtensionsTests.cs
@@ -9,4 +9,81 @@ 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("KE", "+255222199760", "007222199760")] // Tanzania from Kenya (uses 007 instead of +254)
+ [InlineData("KE", "+256414348832", "006414348832")] // Uganda from Kenya (uses 006 instead of +256)
+ [InlineData("KE", "+447106865391", "000447106865391")] // United Kingdom from Kenya
+ [InlineData("TZ", "+25420424200", "00520424200")] // Kenya from Tanzania (uses 005 instead of +255)
+ [InlineData("TZ", "+256414348832", "006414348832")] // Uganda from Tanzania (uses 006 instead of +256)
+ [InlineData("TZ", "+447106865391", "000447106865391")] // United Kingdom from Tanzania
+ [InlineData("UG", "+25420424200", "00520424200")] // Kenya from Uganda (uses 005 instead of +255)
+ [InlineData("UG", "+255222199760", "007222199760")] // Tanzania from Uganda (uses 006 instead of +256)
+ [InlineData("UG", "+447106865391", "000447106865391")] // United Kingdom from Uganda
+ [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
+ [InlineData("CA", "+12124841200", "2124841200")] // US number from Canada
+ 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("+25420424200", "+256414348832", "006414348832")] // Kenya to Uganda (uses 006 instead of +256)
+ [InlineData("+255222199760", "+25420424200", "00520424200")] // Tanzania to Kenya (uses 005 instead of +254)
+ [InlineData("+255222199760", "+256414348832", "006414348832")] // Tanzania to Uganda (uses 006 instead of +256)
+ [InlineData("+256414348832", "+25420424200", "00520424200")] // Uganda to Kenya (uses 005 instead of +254)
+ [InlineData("+256414348832", "+255222199760", "007222199760")] // Uganda to Tanzania (uses 007 instead of +255)
+ [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
+ [InlineData("+12497121234", "+12494185634", "2494185634")] // Canada within NDC, local dialling permitted, NDC required
+ [InlineData("+18797121234", "+18794185634", "4185634")] // Canada within NDC, local dialling permitted, NDC not required
+ [InlineData("+19517121234", "+19514185634", "9514185634")] // US within NDC, local dialling permitted, NDC required
+ [InlineData("+15597121234", "+15594185634", "4185634")] // US within NDC, local dailling permitted, NDC not required
+ public void NumberToDialFrom_PhoneNumber(string source, string destination, string expected) =>
+ Assert.Equal(
+ expected,
+ PhoneNumber.Parse(destination).NumberToDialFrom(PhoneNumber.Parse(source)));
}