Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / C#

Calculating the UPS Tracking Number Check Digit

Rate me:
Please Sign up or sign in to vote.
4.17/5 (3 votes)
7 Nov 2007CPOL3 min read 130K   14   7
Algorithm for calculating the final check digit for a UPS tracking number.

Introduction

This article serves to describe the algorithm used to calculate a UPS Tracking Number. It took me a while to figure this out, researching mostly. I thought I would make it a bit easier for others by describing it here.

Using the code

The following code is a generic method that can be used to calculate a check digit for a UPS Tracking Number. The input for the method is a string, but you could rework it to use a raw char[] if wish. This method leaves the rest of the tracking number up to you. It takes a 15 character sequence, in this case a string, and calculates the check digit using this sequence. For information sake, I will describe how the company I work for generates tracking numbers. This is up to you, as only two portions are required by UPS, the rest you can make up on your own.

  1. The first two characters must be "1Z".
  2. The next 6 characters we fill with our UPS account number "XXXXXX".
  3. The next 2 characters denote the service type:
    • "01" for Next Day Air shipments.
    • "02" for Second Day Air shipments.
    • "03" for Ground shipments.
  4. The next 5 characters is our invoice number (our invoices are 6 digits; we drop the first digit, e.g., the 123456 invoice would yield 23456 characters).
  5. The next 2 digits is the package number, zero filled. E.g., package 1 is "01", 2 is "02".
  6. The last and final character is the check digit.

First of all, you will notice that the described sequence above gives us 17 characters, where as we only need 15 to calculate the check digit. To do this, we drop the "1Z" portion, and only use the last 15 characters in the method.

Next, let me take a moment to outline the algorithm used to generate the check digit. Then, you'll see the method below the outline:

  1. Start a running total.
  2. Examine each character in the sequence:
    • If the character is in an odd position (e.g., 1st, 3rd, 5th....), then:
      • If the character is numeric, then add the numeric value to the running total.
      • If the character is alpha, then:
        • Calculate n to be (ASCII value of character - 48)
        • Calculate x to be ((2 * n) - (9 * INT(n/5))) where INT(n/5) returns n/5 rounded down to the next integer (e.g., 34.3 would be 34, but 34.8 would also be 34).
        • Add x to the running total.
    • If the character is in an even position (e.g., 2nd, 4th, 6th......), then:
      • If the character is numeric, then:
        • Calculate n to be (2 * the numeric value of the character).
        • Add n to the running total.
      • If the character is alpha, then:
        • Calculate n to be (ASCII value of character - 48).
        • Add n to the running total.
  3. Calculate x to be (running total modulo 10)
    • If x = 0, then x is the check digit.
    • If x > 0, then:
      • Calculate y to be (10 - x).
      • y is the check digit.

Here is the method:

C#
//
// UPS Check Digit Calculation Method
//

private int CalculateCheckDigit(String trk)
{
   int checkdigit = 0;
   char[] chars = trk.ToCharArray();
   int charindex = 1;
   int runningtotal = 0;
   foreach(Char ch in chars)
   {
      if((charindex % 2) == 0) //Indicates character in even position
      {
         int testeven;
         if(Int32.TryParse(ch.ToString(), out testeven) == true)
         // Indicates numeric value
         {
            runningtotal += (2 * testeven);
         }
         else // Indicates alpha value
         {
            int asciivalue = System.Convert.ToInt32(ch);
            int n = asciivalue - 48;
            runningtotal += n;
         }
      }
      else // Indicates character in odd position
      {
         int testodd;
         if(Int32.TryParse(ch.ToString(), out testodd) == true)
         // Indicates numeric value
         {
            runningtotal += testodd;
         }
         else // Indicates alpha value
         {
            int asciivalue = System.Convert.ToInt32(ch);
            int n = asciivalue - 48;
            int x = ((2 * n) - (9 * (int)(n / 5)));
            runningtotal += x;
         }
      }
      charindex++;
   }
   int x = (runningtotal % 10);
   if(x == 0) checkdigit = x;
   else if(x > 0) checkdigit = (10 - x);
   return checkdigit;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSmart Labels problem Pin
atherbaig17-Dec-12 3:23
atherbaig17-Dec-12 3:23 
QuestionMy Courier Class to check for valid check digits Pin
blyxx8621-Jun-11 10:37
blyxx8621-Jun-11 10:37 
GeneralUpdates to original function Pin
heyheyitsdavid20-Nov-07 6:01
heyheyitsdavid20-Nov-07 6:01 
I was attempting to implement your function to validate UPS tracking numbers and it seemed to be fairly inconsistent in producing the correct check digit for real UPS tracking numbers that I was trying it with. It would be right about 25% of the time and off by 1 digit the rest of the time. I did a little research and found some information here that helped me make a few corrections to your function to get it working better for me.

A quick summary of the small changes I made:

1. I decided to strip out the "1z" and final (check) digit inside the function, it just seemed more convenient to pass the whole unaltered tracking number rather than stripping these 15 digits away outside the function.

2. Instead of subtracting 48 from the Alpha characters, I subtract 63 from them and then mod the result by 10. This results in the values from the Alpha to Numeric chart that can be found via the link above.

3. All even position values (both Alpha and Numeric) are multiplied by two and added to the total and all odd position values are simply added to the running total. This differs in that it does not matter if the value was derived from and Alpha or Numeric, it's multiplier is only dependent on it's position.

private int CalculateUPSCheckDigit(String trk)
{
    //Drop first 2 characters ("1Z"), take next 15 characters, drop final character (check digit)
    trk = trk.Substring(2, 15);

    int checkdigit = 0;
    char[] chars = trk.ToCharArray();
    int charindex = 1;
    int runningtotal = 0;
    foreach (Char ch in chars)
    {
        if ((charindex % 2) == 0) //Indicates character in even position
        {
            int testeven;
            if (Int32.TryParse(ch.ToString(), out testeven) == true) // Indicates numeric value
            {
                runningtotal += (2 * testeven);
            }
            else // Indicates alpha value
            {
                int asciivalue = System.Convert.ToInt32(ch);
                int n = ((asciivalue - 63) % 10) * 2; 
                runningtotal += n;
            }
        }
        else // Indicates character in odd position
        {
            int testodd;
            if (Int32.TryParse(ch.ToString(), out testodd) == true) // Indicates numeric value
            {
                runningtotal += testodd;
            }
            else // Indicates alpha value
            {
                int asciivalue = System.Convert.ToInt32(ch);
                int n = (asciivalue - 63) % 10;
                runningtotal += n;
            }
        }
        charindex++;
    }
    
    int y = (runningtotal % 10);

    if (y == 0)
    {
        checkdigit = y;
    }

    else if (y > 0)
    {
        checkdigit = (10 - y);
    }   

    return checkdigit;
}


I also used the page linked above to create a function to calculate the check digit value of FedEx tracking numbers if anyone would be interested.

Thanks,
David Bowker
GeneralRe: Updates to original function Pin
Ian77729-Aug-08 9:43
Ian77729-Aug-08 9:43 
GeneralRe: Updates to original function Pin
npaterson2-Jan-09 8:06
npaterson2-Jan-09 8:06 
GeneralRe: Updates to original function Pin
heyheyitsdavid5-Jan-09 6:31
heyheyitsdavid5-Jan-09 6:31 
GeneralRe: Updates to original function Pin
HolidayBows12-Mar-09 7:09
HolidayBows12-Mar-09 7:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.