A bijective function that maps elements from one set to random elements from the same set is called a permutation. A pseudorandom permutation (PRP) is a family of permutations where a (uniformly) randomly chosen member of that set is indistinguishable from an ideal random permutation.
Block ciphers are modeled as PRPs. Choosing a secret unpredictable key is analogous to choosing a random member of a PRP. If you were to encrypt a sequence of 128-bit blocks $(0, 1, 2, ... 2^{128} - 1)$ using a 128-bit block cipher then the sequence of ciphertext blocks would contain every number in the range $[0, 2^{128}-1]$ exactly once in a (pseudo)random order. These new blocks can be converted back to the original values if you know the key, by decrypting each block.
Using a block cipher (with no padding or mode of operations*) gives you a function that is collision free. The relationship should be unpredictable to anyone that doesn't know the key.
If you look at block ciphers then you'll notice that almost all of them work on a fixed number of bits. The range of the inputs/outputs are a power of two. "Format preserving encryption" (FPE) is the name given to encryption methods designed to work on data of a specific format and leave ciphertext in the same format as the plaintext.
One generic FPE scheme you can use to create a PRP with a range that isn't a power of two is to create a kind of Feistel cipher. (Note that you should encrypt the numeric value of a timestamp, not the ASCII value.)
Example
You need two variables. Split a 8 digit number into two 4 digit numbers as follows.
x = n % 10000;
y = n / 10000;
Then you'll want to perform four rounds like so
x = (x + F(k, 0, y)) % 1000;
y = (y + F(k, 1, x)) % 1000;
x = (x + F(k, 2, y)) % 1000;
y = (y + F(k, 3, y)) % 1000;
Where F is some function that returns a random number in the range [0, 9999], k
is a secret key, and the second parameter of F is the round number.
Then convert x
and y
back to a 10 digit number. For example,
n = 10000 * y + x;
F
needs to have a range that is a multiple of 10,000. If security is not a concern then you can just return a random 64-bit number mod 10,000. There is some bias because 10,000 does not divide the range evenly, but it wouldn't be noticeable to casual observers. F
could be defined like
$$F(key, round, n) = \operatorname{mod}(\operatorname{AES}_{key}(n + 10000 \cdot round), 10000)$$
* Normally block ciphers are used with a mode of operation and a single use initialization value. For normal encryption use a standard authenticated mode of operation. Do not use ECB.