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
0will 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.InvariantCultureinTryParse.
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.