Random Number Generators(RNGs) are really generating pseudorandom numbers, since it's impossible to actually generate a TRULY random number. The only really truly random things are acts of God, like lightning.
This wikipedia article might be able to help you out in the explanation: http://en.wikipedia.org/wiki/Random_number_generators
From what I understand, there are basically two parts of an RNG: the seed, and then the random number chosen from that seed. When you seed the RNG, you are giving it an equivalent to a starting point. That starting point then has a bunch of numbers that are "inside" of it that the program chooses from. In PHP, you can use srand() to "shuffle" the seeds, so you almost always get a different answer. You can then use rand(min, max) to go into the seed and choose a number between the min and the max, inclusive.
WARNING, POSSIBLE CHEESY ANALOGY AHEAD!
Think of each 'seed' as an ice chest, and then the random numbers as ice cubes. Let's say you have 1000 ice chests and each chest has 1000 ice cubes inside. At the county fair, they'll choose an ice chest to start using for drinks, and they can only use one ice cube. However, they only need ice cubes bigger than 1 cubic inch. So they'll choose a chest at random between those 1000 chests, and then they'll choose an ice cube inside that chest at random. If it works for the size they want, they use it. If it's not, they put it back in the chest with the others. If they want to make it a little more fun they change chests beforehand for total obliviousness, if you will!
As for how PHP actually physically chooses the seed and the random number, I don't have enough knowledge for that(which is probably what you were wondering the most about!). I wouldn't try and redo the rand() function; for most web based applications that you'll make, rand() should suffice for any random number you'll need.
Also check out linear congruential generators, this might be more of what you're looking for if you want the dirty details: http://en.wikipedia.org/wiki/Linear_congruential_generator
Hope this helps!
What you actually want to test here, I assume, is that given a specific set of results from the randomiser, the rest of your method performs correctly.
If that's what you're looking for then mock out the randomiser, to make it deterministic within the realms of the test.
I generally have mock objects for all kinds of non-deterministic or unpredictable (at the time of writing the test) data, including GUID generators and DateTime.Now.
Edit, from comments: You have to mock the PRNG (that term escaped me last night) at the lowest level possible - ie. when it generates the array of bytes, not after you turn those into Int64s. Or even at both levels, so you can test your conversion to an array of Int64 works as intended and then test separately that your conversion to an array of DateTimes works as intended. As Jonathon said, you could just do that by giving it a set seed, or you can give it the array of bytes to return.
I prefer the latter because it won't break if the framework implementation of a PRNG changes. However, one advantage to giving it the seed is that if you find a case in production that didn't work as intended, you only need to have logged one number to be able to replicate it, as opposed to the whole array.
All this said, you must remember that it's called a Pseudo Random Number Generator for a reason. There may be some bias even at that level.
Best Answer
Both Java and .net tend to favour this form of range (which is generally known as a half open range) in all circumstances, eg they also use it for selecting ranges in substring operations, and in operations on sections of arrays. The main reason for favouring it is that it means that the calculation end - start gives a result of the number of items included in the range (e.g, for a range of 1 up to 7, 7-1 = 6 values, I.e. 1, 2, 3, 4, 5, 6) when working with integers.
There is also another reason for floating point random number generators. Such a generator usually works by assigning random bits to the mantissa and leaving the exponent set to zero. This results in a value between 0 and 1-2^(-b) inclusive, where b is the number of bits in the mantissa, I.e it forms a half open range between 0 and 1. This is far simpler than having to include 1 in the range because it would require a special case in the generation code.
A further benefit is that when a floating point number of this kind is multiplied by an integer and rounded by truncation, the result has an even distribution. If it could generate 1, then in the very unlikely case that that actually happened, it would round to the maximum value, but not for any other outcome. This would be very hard to work with.