Convert to and from Clock & Data format

Convert Decimal to Clock and Data Format in C# - Hex Byte Manipulation

This is one of those niche problems that sounds oddly specific until you run into it — and then you really need it. The "Clock and Data" format (sometimes called "Clk & Data" in smart card or NFC contexts) is a compact binary encoding used in certain RFID, NFC, and low-level data protocols. When you're working with .NET and need to convert decimal values into this byte-swapped hex format (and back), there's no built-in framework method for it. You have to build it yourself.

I hit this while integrating with a card reader system that expected values in this specific encoding. The data looked like a garbled hex string until I realized the bytes were in little-endian order. Once I understood the format, the conversion logic was pretty elegant in C#.

What is the Clock & Data Format?

The Clock & Data format encodes a decimal number into a fixed-width hexadecimal representation where the bytes are in reverse order (little-endian). For example, the decimal value 12345678 might become 4E61BC00 in big-endian hex, but in Clock & Data format, it's stored as 00BC614E — the bytes are flipped.

This format is commonly used in:

  • RFID and NFC card readers for encoding card numbers
  • Wiegand access control protocols
  • Some legacy industrial communication systems
  • Low-level hardware interfaces that use byte-serial transmission

Converting TO Clock & Data Format

Here's the logic: parse the decimal, break it into its internal 128-bit representation, convert each 32-bit chunk to hex, and then reverse the byte order of the resulting hex string:

using System;
using System.Text;
using System.Collections.Generic;

public class ClockDataConverter
{
    public static string Convert2ClockAndData(string text)
    {
        if (!decimal.TryParse(text, out decimal d))
        {
            return null;
        }

        int[] dBits = decimal.GetBits(d);

        // Convert each part to hex and apply necessary padding
        string part3 = dBits[3].ToString("X2");   // Sign and scale factor (2 chars)
        string part2 = dBits[2].ToString("X2");   // Most significant 32 bits
        string part1 = dBits[1].ToString("X8");   // Middle 32 bits (8 chars)
        string part0 = dBits[0].ToString("X8");   // Least significant 32 bits

        string hexData = (part3 + part2 + part1 + part0).TrimStart('0');

        // Ensure even-length hex string
        if (hexData.Length % 2 != 0)
        {
            hexData = hexData.StartsWith("0") ? hexData[1..] : "0" + hexData;
        }

        // Reverse byte order (two characters at a time)
        string reversedHex = "";
        for (int i = 0; i < hexData.Length; i += 2)
        {
            reversedHex = hexData.Substring(i, 2) + reversedHex;
        }

        return reversedHex;
    }
}

Converting FROM Clock & Data Format

The reverse operation: take the byte-reversed hex, flip the bytes back to big-endian, then reconstruct the decimal value from the 4 integer parts:

    public static string ConvertFromClockAndData(string clockAndData)
    {
        // Reverse the bytes back to big-endian
        string hexData = "";
        for (int i = 0; i < clockAndData.Length; i += 2)
        {
            hexData = clockAndData.Substring(i, 2) + hexData;
        }

        // Pad to 20 hex characters (5 bytes = 160 bits, but decimal uses 4 ints = 128 bits)
        hexData = hexData.PadLeft(20, '0');

        // Extract the 4 parts (each 32-bit integer)
        string part0 = hexData[^8..];
        string part1 = hexData[^16..^8];
        string part2 = hexData[^18..^16];
        string part3 = hexData[..^18];

        int[] dBits = new int[4];
        dBits[0] = Convert.ToInt32(part0, 16);
        dBits[1] = Convert.ToInt32(part1, 16);
        dBits[2] = Convert.ToInt32(part2, 16);
        dBits[3] = Convert.ToInt32(part3, 16);

        decimal result = new decimal(dBits);
        return result.ToString();
    }

Testing It

Here's how you'd verify the round-trip works correctly:

// Test round-trip
string original = "123456789";
string encoded = ClockDataConverter.Convert2ClockAndData(original);
string decoded = ClockDataConverter.ConvertFromClockAndData(encoded);

Console.WriteLine($"Original:  {original}");
Console.WriteLine($"Encoded:   {encoded}");
Console.WriteLine($"Decoded:   {decoded}");
Console.WriteLine($"Match:     {original == decoded}");

// Output:
// Original:  123456789
// Encoded:   15CD5B07
// Decoded:   123456789
// Match:     True

Edge Cases to Watch Out For

A few things that can trip you up:

  • Zero value: Converting 0 will give you an empty string after trimming — you may want to return "00" explicitly to keep things consistent.
  • Odd-length hex strings: The padding logic handles this, but make sure your input doesn't include spaces or non-hex characters.
  • Large decimals: decimal.GetBits() returns a 4-element array for the full 128-bit representation. Make sure you're not accidentally truncating high-value inputs.
  • Culture-specific parsing: If your decimal string might come with a locale-specific separator (e.g., a comma instead of a period), use CultureInfo.InvariantCulture in TryParse.

Summary

The Clock & Data format is a little-endian byte representation of decimal numbers, used in certain RFID and access control protocols. C#'s decimal.GetBits() method gives you the raw 4-integer internal representation, which you can then assemble and reverse to produce the encoded value. The reverse operation is equally straightforward. This small utility can save hours of head-scratching when dealing with card readers or other hardware that speaks this format.

Post a Comment

Previous Post Next Post