JavaScript/Epoch time converter

This epoch time converter can convert dates into Unix epoch time stamps and vice versa. It makes use of the sophisticated date and time parsing abilities included in JavaScript.

Desktop view; an epoch time stamp is converted to a date and a time.
Mobile view; a date and a time are converted to an epoch time stamp.

The input format is automatically detected. If the input is only numerical without any other characters, it is presumed to be a UNIX epoch time stamp. Otherwise, it is presumed to be a date. A four-digit input value is presumed to be a year.

The script also detects URL hashes (characters after "#" in the URL), which can be used to enter a value as well, and for memorizing the last input. The URL hash will be updated when pressing the ↵ Enter key or clicking the "Auto-detect and convert" button. The result is previewed after each key press.

There are additional buttons for interchanging the input and the result, for calculating the current epoch time stamp, and for clearing the input field. The last adds convenience for mobile users.

The code contains comments to facilitate understanding each part.

Source code edit

<!DOCTYPE html>
<html lang="en">
<head>
	<title>Epoch time converter with automatic detection</title>
	<meta name="author" content="Elominius from Wikiversity">

	<!-- the viewport meta tag magnifies the page on mobile browsers to make it useable there -->
	<meta name="viewport" content="initial-scale=1.0, user-scalable=yes">

	<style type="text/css">
		/* modern font pack */
		body { font-family: ubuntu,'noto sans','open sans', calibri, 'segoe ui', 'trebuchet ms', arial, helvetica, verdana, tahoma, 'Bitstream Vera Sans', 'sans-serif'; }
	
		div { margin-bottom: 1em; } /* small gap between divisions */
		JS_show { opacity:0.2; pointer-events:none; } /* Initially grey out containers that are only meant to be shown with JavaScript activated, in order to provide a preview. */
		/* for completely hiding it if preferred, use: JS_show { display: none; } */

		no_select { /* for text that is not to be highlighted */
			/* vendor prefixes for compatibility, see https://developer.mozilla.org/en-US/docs/Web/CSS/user-select */
			-moz-user-select: none; /* supported since Firefox 1.0 */
			-webkit-user-select: none; /* since Chrome 1.0 */
			-ms-user-select: none; /* since Edge 12.0 */
			user-select: none; /* Chrome 54 (2016-10), Firefox 69 (2019-09) */
		}

		/* prevent buttons from sticking to input field in mobile view */
		button { margin-top: 1em; }

		/* dark theme */
		body { background-color: #333; color: #ddd; }
		a { color: lightblue; }
	</style>
</head>

<body>

<JS_show>
	<!-- JS_show container: only reveal contents if JavaScript is activated. Code implemented in JavaScript part further down. -->
<div id="time_input_container">
	<input 
		autofocus="true" 
		id="time_input" 
		placeholder="Date/time or unix time stamp" 
		onkeyup="epoch.keyup(event);"
		onkeypress="epoch.keypress(event);"
		size="30"
	>
	</input>
	<!-- The "autofocus" parameter focuses the field from the beginning, so the user can start typing immediately. -->
	<!-- The "onkeyup" event updates the calculation automatically after each character is entered. -->
	<!-- The "onkeypress" event separately implements updating the calculation and the URL hash 
by pressing the enter/return (↵) key. -->
	<!-- The placeholder text is displayed when the input field is empty. -->
	<!-- Size (input field width) set to 30 characters to fit in the placeholder text. -->

	<div id="button_container" style="display:inline-block; margin-bottom:0;">
	<!-- keeping the buttons in one row -->
		<button class="run_script" onclick="epoch.convert(); document.location.hash=time_input.value;">
			Auto-detect and convert
		</button>

		<!-- interchanges the date and time with the epoch time stamp -->
		<button class="epoch_swap" onclick="epoch.swap();">Swap</button>

		<!-- sets the input to the current date and time -->
		<button class="epoch_now" onclick="epoch.now();">Now</button>

		<!-- more convenient clearing of the input field, mainly for mobile users -->
		<button class="epoch_clear" onclick="epoch.clear();">Clear</button>
	</div>
</div>
<div id="time_output"><!-- The calculated time will appear here. --></div>
</JS_show>

<noscript>
	<!-- This guidance is shown if JavaScript is not available in the user's browser. -->
	<h2>JavaScript is unavailable</h2>
	<p>To convert dates and time stamps, please activate JavaScript or use a web browser that supports it.</p>
	<p>If you are seeing this error in the Android HTML viewer, enter the path to this HTML file into the address bar of the web browser.</p>
</noscript>

<footer>
<hr />
<div id="credit" style="font-size:small;">Originally created by <a href="https://en.wikiversity.org/wiki/User:Elominius">Elominius from Wikiversity</a>. Licensed under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-by-SA 3.0</a>.</div>
</footer>

<script type="text/javascript">
// create nodes for input field and output container
var time_input = document.getElementById("time_input");
var time_output = document.getElementById("time_output");

var epoch = {}; // create data object
var count=0,input="";input=""; // defeats JS Hint error; no functional difference

	// reveals "JS_show" containers
if ( document.getElementsByTagName("JS_show")[0] ) /* check if JS_show containers exist */ {
	for (
		count=0; // initiate counter
		count < document.getElementsByTagName("JS_show").length; // count JS_show containers
		count++ // count up. Same as count+=1 and count=count+1.
	) {
		// restore opacity and/or unhide by overriding CSS at the top using inline CSS:
		document.getElementsByTagName("JS_show")[count].setAttribute("style","display:block; opacity:1; pointer-events:all;");
	}
}

	// detects the format in the input field
epoch.detect = function(input) {
	input+=""; // force data type into string instead of number by appending empty string
	if (
		// detect literal dot, slash, colon, and alphabetical characters in a regular expression group; presume four-digit input to be a year number.
		//  To manually opt for the recognition of a four-digit value as an epoch time stamp, which is very unlikely to be necessary, a space character can be added before or after the input value.
		// separate search for dash that starts after the first character of the input to prevent matching negative numbers
		// The empty string "" is necessary to force the data type into "string" instead of "number" so the length can be measured.
		// special case: first character in input is a dash, meaning the input is a negative number and should be recognized as epoch time stamp.
		( (""+input).search(/(\.|\/|\:|[A-Z]|[a-z])/) > -1 || (""+input).search("-") > 0 ) || ( (""+input).length == 4 && (""+input).search("-") != 0 )
	) {
	return false; // probably no epoch time stamp
	} else return true; // probably an epoch time stamp
};

	// calculates the output date or time stamp
epoch.convert = function() {
	if (time_input.value == "") return false; // no calculation if the input box is empty

	if ( epoch.detect(time_input.value) ) /* epoch to date */ {
		epoch.output_type = "Date and time";
		epoch.output_text = new Date(time_input.value * 1000); // add three zeroes to convert seconds instead of milliseconds. Stored in standalone variable for the swap feature.
		time_output.innerHTML = "<no_select>"+epoch.output_type+": </no_select>"+epoch.output_text;
	} else  /* date to epoch */ {
		epoch.output_type = "Epoch time stamp";
		epoch.output_text = (new Date(time_input.value).getTime() / 1000 );
		time_output.innerHTML = "<no_select>"+epoch.output_type+": </no_select>"+epoch.output_text; /* divide by 1000 to convert seconds instead of milliseconds */
	}
	time_input.setAttribute("value",time_input.value); // Update value in DOM (document object model) – might improve compatibility.
};


// for the "Swap" button
epoch.swap = function() {
	time_input.value = epoch.output_text; // feed last result into input
	epoch.convert();
};

// for the "Now" button
epoch.now = function() {
	time_input.value = new Date(); // fill in the current date and time
	epoch.convert();
};

// for the "Clear" button
epoch.clear = function() {
	time_input.value = ''; // blank input field
	time_output.innerHTML = ''; // blank result field
	time_input.focus(); // focus on input field so user can start typing immediately
};

// implements instant results after each key press
epoch.keyup = function() {
	time_input.defeat_hash_change = true; // prevent hash change from interfering with text area
	epoch.convert();	// convert automatically on each key press inside the text box.
	time_input.defeat_hash_change = false; // unlock hash change
};

// implements "enter" button press for results and updating URL
epoch.keypress = function() {
	if (event.keyCode == 13) // Keycode 13 is for enter key, both main and on NUM pad.
	{ 
		epoch.convert(); // convert when pressing enter.
		document.location.hash = time_input.value; // update URL
	}
};

// date calculation shortcut via pre-entered hash fragment (#) after URL
if (document.location.hash) {
	/* using decodeURIComponent to prevent spaces from turning into "%20" (hexadecimal percent-encoding) */
	time_input.value = decodeURIComponent(document.location.hash.substring(1) );
	epoch.convert();
}

// detect change of value after "#" in URL
window.onhashchange = function() {
	if (!time_input.defeat_hash_change) {
	/* Only run again if URL changed manually, not input box. */ 

		time_input.value = decodeURIComponent(document.location.hash.substring(1) );
		epoch.convert();
	}
};

// fallback for web browsers without HTML input autofocus attribute support
time_input.focus();

// pre-fill with current time if no time is specified in URL
if (document.location.hash == "") epoch.now();
</script>

</body>

</html>