// Generating a daily random quotation for a web page.
// The code will generate the same quotation for 24-hours.
// Uses GMT elapsedTime. Flip will happen at midnight GMT or 4 PM PST, 5 PM PDT.
// Reproducible every time we run this macro that day.

import java.io.UnsupportedEncodingException;
import java.util.zip.CRC32;

...

/**
  * 86,400,000 the number of milliseconds in 24 hour day. Easily fits into an int.
  * We change the quotations every 24 hours, UTC.
  */
private static final int CHANGE_INTERVAL_IN_MILLIS = 24 * 60 * 60 * 1000;

...

// get an array of candidate quotations.
final String[] candidates = getQuotations();

// assign one of the quotes
final String chosenQuote = candidates[ assignChoiceForFile( fileBeingProcessed, candidates.length, CHANGE_INTERVAL_IN_MILLIS ) ];
...

/**
 * Assign a random ad or quotation to a file.
 *
 * @param fileBeingProcessed     File where we will insert the ad or quotation.
 * @param possibleChoices        how many different ads or quotations there are.
 * @param changeIntervalInMillis how often to change the ad or quotation.
 * @param prime                  a unique prime number to scramble the results to ensure
 *                               this differs from other choices on the same page.
 * @return number 0..possibleChoices-1, "random" but deterministically based on the current time and the file name.
 * @throws UnsupportedEncodingException
 */
static int assignChoiceForFile( File fileBeingProcessed, int possibleChoices, long changeIntervalInMillis, int prime )
   throws UnsupportedEncodingException
{
   final CRC32 digester = new CRC32();
   final int seed = ( int ) ( System.currentTimeMillis() / changeIntervalInMillis );
   /// digester.update(int) not clearly documented, probably wants ubyte.
   digester.update( ( byte ) ( ( prime >>> 24 ) & 0xff ) );
   digester.update( ( byte ) ( ( prime >>> 16 ) & 0xff ) );
   digester.update( ( byte ) ( ( prime >>> 8 ) & 0xff ) );
   digester.update( ( byte ) ( prime & 0xff ) );
   // process seed in big-endian order.
   digester.update( ( byte ) ( ( seed >>> 24 ) & 0xff ) );
   digester.update( ( byte ) ( ( seed >>> 16 ) & 0xff ) );
   digester.update( ( byte ) ( ( seed >>> 8 ) & 0xff ) );
   digester.update( ( byte ) ( seed & 0xff ) );
   digester.update( fileBeingProcessed.getPath().getBytes( "UTF-8" ) );

   // Ensure we have a positive digest before taking modulus. Mask because Math.abs can't handle Integer.MIN_VALUE.
   return( ( ( int ) digester.getValue() ) & 0x7fffffff ) % possibleChoices;
}