Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Casting Between Dates And Numeric Dates In Lucee CFML 5.3.7.48

By Ben Nadel on
Tags: ColdFusion

One of the curious features of ColdFusion is the fact that you can represent Date/Time values as numbers. These numbers reference the fractional days since the "ColdFusion Epoch", which is 12/30/1899 00:00:00 for "reasons". I don't use these "numeric dates" very often. But, just this past week, I had to group a bunch of date/time values into "day buckets"; and, I found it quite convenient to call floor( date ) in order to get a normalized, numeric version. This brought up fond memories of the 2000-aughts when I was fascinated by "Date Math" in ColdFusion. As such, I wanted to take a moment and wax nostalgic about casting between Dates and Numbers in Lucee CFML 5.3.7.48.

A numeric date in ColdFusion is a Double in which the integer portion is the number of days since the ColdFusion Epoch; and, the decimal portion is the factional time of the day. So, when I mentioned above that I was calling:

floor( dateTimeValue )

... this was performing two operations:

  • An implicit casting of the dateTimeValue input to a Double.

  • An explicit stripping of the fractional portion, leaving me with an Integer number of days.

With that said, there are a number of ways to convert Date/Time values to Numbers and Numbers to Date/Time values. First, let's look at casting Date/Time values to numbers:

<cfscript>

	echoLine( now() );

	// Use the official method for converting dates to numeric representation.
	echoLine( getNumericDate( now() ) );

	// Use implicit casting of dates to numeric representation.
	echoLine( now() * 1 );
	echoLine( + now() );

	// Dip down into the Java implementation to grab the numeric representation.
	// --
	// CAUTION: Only works if the value is an actual Date/Time instance - the value will
	// not implicitly get cast to a Date/Time instance from other types.
	echoLine( now().toDoubleValue() );

	// Dip down into the Lucee implementation layer. This is actually what the
	// getNumericDate() function is doing under the hood.
	echoLine( createObject( "java", "lucee.runtime.op.Caster" ).toDoubleValue( now() ) );

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	public void function echoLine( required any input ) {

		echo( input & "<br />" );

	}

</cfscript>

The getNumericDate() function is the "official" way to cast a Date/Time value to its numeric representation. But, as you can see, by treating a Date/Time value as a "number", Lucee CFML will automatically cast the Date/Time value to a numeric date as part of the expression evaluation. Then, of course, there are undocumented ways to dip into the Java layer.

That said, when we run this ColdFusion code, we get the following output:

{ts '2021-03-20 06:56:23'}
44275.28916663194
44275.28916663194
44275.28916663194
44275.28916663194
44275.28916663194

As you can see, we get the same output for each casting operation.

When it comes to casting a numeric date back into a Date/Time value, there doesn't appear to be an "official" way to do this. Meaning, there's no inverse of the getNumericDate() function - at least none that I could find. As such, we have to get a little trickier:

<cfscript>

	// Numeric representation of "2021-03-20 06:08:44".
	value = 44275.25607354167;

	// By adding ZERO days to the given value, Lucee will implicitly cast the numeric
	// representation back to a date/time value.
	echoLine( dateAdd( "d", 0, value ) );

	// The dateTimeFormat() function will implicitly cast the numeric representation to a
	// string representation; then, we can parse that string representation back to an
	// actual date/time value.
	echoLine( parseDateTime( dateTimeFormat( value ) ) );

	// We can explicitly create a date/time value by plucking the relevant date parts out
	// of the numeric representation.
	echoLine(
		createDateTime(
			datePart( "yyyy", value ),
			datePart( "m", value ),
			datePart( "d", value ),
			datePart( "h", value ),
			datePart( "n", value ),
			datePart( "s", value )
		)
	);

	// Dip down into the Lucee implementation layer.
	echoLine( createObject( "java", "lucee.runtime.op.Caster" ).toDate( value ) );

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	public void function echoLine( required any input ) {

		echo( input & "<br />" );

	}

</cfscript>

My goto approach for this is the dateAdd() function wherein I just add zero-days to the given numeric date. This will implicitly cast the value back into a Date/Time instance. That said, we can format it and parse; and, dip down into the Java layer. And, when we run this ColdFusion code, we get the following output:

{ts '2021-03-20 06:08:44'}
{ts '2021-03-20 06:08:44'}
{ts '2021-03-20 06:08:44'}
{ts '2021-03-20 06:08:44'}

As you can see, we get the same output for each casting operation.

Once you embrace the notion that you can represent Date/Time values as numbers, you can start to do some interesting things. For example, you can randomly select a time during the day. Remember, since the numeric representation is a Double in which the decimal portion is the fractional day, we can generate random times by adding a random decimal to an integer.

To see this in action, let's pick 10 random times today:

<cfscript>

	// Get the Integer portion of today's numeric date.
	today = fix( now() );

	loop times = 10 {

		// Rand() is a pseudo-random number generator that returns a decimal value
		// between 0-1. Which, when combined with "numeric dates", gives us a way to
		// generate random "fractional days".
		randomTimeOfDay = ( today + rand() );

		echo( dateTimeFormat( randomTimeOfDay ) & "<br />" );

	}

</cfscript>

As you can see, we're using the rand() function to generate random "fractional days", which we're then adding to the numeric representation of 12AM this morning. And, when we run this ColdFusion code, we get the following output:

20-Mar-2021 19:22:14
20-Mar-2021 21:21:42
20-Mar-2021 20:49:17
20-Mar-2021 19:50:24
20-Mar-2021 10:20:12
20-Mar-2021 14:32:26
20-Mar-2021 20:06:08
20-Mar-2021 16:55:17
20-Mar-2021 14:42:46
20-Mar-2021 13:30:49

That's pretty cool!

Like I said, I don't actually use the numeric representation of dates all that often in ColdFusion. But, understanding that there is a numeric representation of dates means that it's a tool you can reach for if and when you need it. And, understanding how it works allows you to do some interesting things, like generating random dates.

Epilogue: Not To Be Confused With The Milliseconds Representation Of Dates

Just to be clear, "numeric dates" in ColdFusion are a separate concept from the fact that Date/Time values are all essentially facades that represent the number of milliseconds since Unix Epoch / Unix Time. This is true in most programming languages.

ASIDE: In JavaScript, you can do Date.now() or new Date().getTime() to get the current UTC milliseconds.

In Lucee CFML, you can access the milliseconds representation of a Date/Time value by calling .getTime() on it. And, you can always access the current milliseconds using getTickCount() - which I use all the time!

<cfscript>

	// getTickCount() returns the current Unix time. I USE THIS ALL THE TIME (no pun intended).
	echoLine( getTickCount() );

	// For actual Date/Time objects, you can dip down into the Java layer and grab the
	// underlying milliseconds representation of the date.
	echoLine( now().getTime() );

	// To use high-level ColdFusion, you can get the difference between a date and Epoch.
	// That said, dateDiff() only has SECONDS granularity, which requires us to multiple
	// by 1,000 to obtain milliseconds.
	echoLine( dateDiff( "s", dateConvert( "utc2local", "1970-01-01 00:00:00" ), now() ) * 1000 );

	// To get milliseconds from a non-Date/Time value, you just have to cast the value to
	// a native Date/Time first.
	echoLine( parseDateTime( "2021-03-20 08:05:00" ).getTime() );

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	public void function echoLine( required any input ) {

		echo( input & "<br />" );

	}

</cfscript>

Running this ColdFusion code, we get the following output:

1616241900915
1616241900915
1616241900000
1616241900000

Parsing date/time strings always get a little confusing when having to convert back-and-forth between UTC and the server's Local time.



Reader Comments

What has two thumbs and hopes you leave a comment? This Guy! (Ben Nadel).

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.