Clever time formatting with JavaScript - javascript

I need help writing a little Regular Expression to replace integers into a time format string in a text input field.
In my form: I have a text input field where I'm supposed to write numbers. And when I write a number: I want a piece of JavaScript to convert it to a human-friendly, readable time when the input field loses focus. The thing that makes this "Clever" is that I want to be able to write as little as absolutely possible: And have it transform to the most appropriate time corresponding to my number.
Let me give you a few examples. If I write:
7 it would resolve to 07:00
15 it would resolve to 15:00
93 it would resolve to 09:30
1945 it would resolve to 19:45
143 it would resolve to 14:30
... And so on ...
I want it to do this replacement after the input field loses focus (onblur-event)
Also. I want to have a 0 prefix for night hours. Like this:
015 = 00:15
03 = 00:30
012 = 00:12
... And so on ...
I begun writing if statements to do this, but I stopped dead in my tracks because I realized it would take so many if statements, and would not be very reliable. I feel like Regular Expressions would be much smarter since it compacts my script and makes loading time quicker. I grasp the basics of Regular Expressions, but I don't know how to write a clever one for this purpose.
This is what my code got to before I decided to drop it:
var elem = document.getElementById("incidentHourStart-" + row);
if (elem.value.length === 1) {
// Must be a whole hour (0-9)
} else if (elem.value.length === 2) {
// Can be either two digits hour (10-23) or hour + whole minute (73: 07:30)
if (parseInt(elem.value) >= 10 && parseInt(elem.value) <= 23) {
// Two digits, whole hour (10-23)
} else {
// First digit is hour, and last one is whole minute (10, 20, 30, 40, 50)
}
} else if (elem.value.length === 3) {
// First digit must be an hour value, middle digit is also hour if it is lower than 23, last digit is then always whole minutes
if (parseInt(elem.value) >= 10 && parseInt(elem.value) <= 23) {
// Two digits, whole hour (10-23)
} else {
// First digit is hour, and last one is whole minute (10, 20, 30, 40, 50)
}
} else if (elem.value.length === 4) {
// First two digits must be an hour value, last digits is then fine selected minutes
}
As you can see: It looks very ugly!
UPDATE:
As stated in the comments: People have found my rules a little confusing. So here is the pseudo code of rules I want it to follow. If this can be put into a Regex in a clever way: Then awesome! If not: I will write out the if/else blocks, or split the Regex up into parts as suggested.
If text-length is equal to 1
Resolve as whole hour between 0-9
Examples:
2 = 02:00
8 = 08:00
5 = 05:00
If text-length is equal to 2 AND number is between 10 and 23
Resolve as whole hour between 10-23
Examples:
15 = 15:00
11 = 11:00
22 = 22:00
If text-length is equal to 2 AND number is NOT between 10 and 23
Resolve as whole hour and minutes incremented by 10's (10, 20, 30, 40, 50)
Examples:
73 = 07:30
24 = 02:40
95 = 09:50
If text-length is equal to 3 AND first two numbers are between 10 and 23
Resolve two first digits as hours and last digit as minutes incremented by 10's (10, 20, 30, 40, 50)
Examples:
133 = 13:30
195 = 19:50
111 = 11:10
162 = 16:20
If text-length is equal to 3 AND first two numbers are NOT between 10 and 23
Resolve first digit as whole hour, and last two as minutes.
Examples:
225 = 02:25
922 = 09:22
557 = 05:57
451 = 04:51
If text-length is equal to 1 AND first digit is equal to 0
Resolve as mid-night
Example:
0 = 00:00
If text-length is equal to 2 AND first digit is equal to 0
Resolve as mid-night + minutes incremented by 10's (10, 20, 30, 40, 50)
Examples:
02 = 00:20
05 = 00:50
03 = 00:30
If text-length is equal to 3 AND first digit is equal to 0
Resolve as mid-night + full minutes.
Examples:
024 = 00:24
011 = 00:11
056 = 00:56
If text-length is equal to 4
Resolve as regular minutes and hours without the colon (:)
Examples:
1524 = 15:24
2211 = 22:11

Don't make it harder on yourself than you need. Simply put; don't do this in one regular expression. Also don't forget to trim your input before using RegEx.
First of all check the zero-prefixed one, something like:
^0(\d+)$
Then if that doesn't match, do the check for the normal numbering and split it with the capture groups however you want:
^([^0]\d{1,3})$ // Can do negative lookbehind here, but I left it simple in this case
Regular expressions are often misused to solve a bigger problem in one pattern. It's much better to split logic if the situation asks for it. Don't overcomplicate code. It will break whoever needs to read it later's brain.

I have found a solution and had it deployed for about 1.5 months now. And so far: It works great. My co-workers love this functionality, and really saves up a lot of time! So far: No faults have been reported to me.
So here is what I did:
I bascially re-wrote my pseudo-code into actual JavaScript code using if/else blocks as suggested in the question's comments. I stuck it all into a function that I call from an onblur event on the input fields. Like this:
<input type="text" id="incidentHourStart-1" onblur="resolveTimeField(1);">
So the formatting occurs as soon as the input field loses focus.
Here is what my function looks like:
function resolveTimeField(row) {
var elem = document.getElementById("incidentHourStart-" + row);
if (elem.value.length === 1) {
// Must be a whole hour (0-9)
elem.value = "0" + elem.value + ":00";
} else if (elem.value.length === 2) {
// Can be either two digits hour (10-23) or hour + whole minute (73: 07:30)
if (parseInt(elem.value) >= 10 && parseInt(elem.value) <= 23) {
// Two digits, whole hour (10-23)
elem.value = elem.value + ":00";
} else {
// First digit is hour, and last one is whole minute (10, 20, 30, 40, 50)
var hours = elem.value.substring(0, 1);
var minutes = elem.value.substring(1, 2);
elem.value = "0" + hours + ":" + minutes + "0";
}
} else if (elem.value.length === 3) {
// First digit must be an hour value, middle digit is also hour if it is lower than 23, last digit is then always whole minutes
var firstDigits = elem.value.substring(0, 2);
if (parseInt(firstDigits) >= 10 && parseInt(firstDigits) <= 23) {
// 3 digits, first two are hours, and last digit is minutes incremented by 10's
var hours = elem.value.substring(0, 2);
var minutes = elem.value.substring(2, 3);
elem.value = hours + ":" + minutes + "0";
} else {
// First digit is hour, and last two is full minutes
var hours = elem.value.substring(0, 1);
var minutes = elem.value.substring(1, 3);
elem.value = "0" + hours + ":" + minutes;
}
} else if (elem.value.length === 4) {
// First two digits must be an hour value, last digits is then fine selected minutes
var hours = elem.value.substring(0, 2);
var minutes = elem.value.substring(2, 4);
elem.value = hours + ":" + minutes;
}
}
Here is a JSFiddle if you want to test out the code for yourself
My function takes one parameter referenced as row. This is because the fields I am working with lays within a dynamic table that lets my co-workers register more data in one go.
Currently it does not have any forms of input validation that checks if the inserted value is valid. So you could write something like 999 into the field and have it resolve to 09:99. This is not a critical feature for our environment, but I can imagine it would be fairly easy to implement shall it be required. I will update this post if I ever implement such a validation feature in the future.

Related

Is there a way to display time to the highest expanded whole number metric time with JavaScript

Is it possible to display the highest expanded whole number metric time with Javascript code,
For an application, I want to display a time but instead of having a set metric (hour, min or second) it's flexible and I want to display the highest expanded metric given a time value. The input would be in seconds, and if there's a value that exceeds 3 digits I want it to be converted to the next unit, so for example
Input: 1500s
Output: 25mins
Reason: In its most expanded form it would be 25mins
Input: 3245155s
Output: 90hrs
Reason: In its most expanded form it would be 90hrs because in mins it would be 5409 which exceeds my 3 digit limit for a whole number.
Input: 34s
Output: 34s
Reason: In its most expanded form it would remain 34s as it is still within my 3 digit range
I know it's possible to write an algorithm for this, but instead of reinventing the wheel I'd just like to know if this functionality is built-in Javascript
This is the solution that works for me, dividing the various units (therefore converting) until I reach the final unit I'd use.
const timeFormatter = (seconds) => {
if (!seconds) return "0s";
if (seconds < 60) return `${seconds}s`;
let minutes = Math.round(seconds / 60);
if (minutes < 60) return `${minutes}min`;
let hours = Math.round(minutes / 60);
if (hours < 24) return `${hours}hr`;
let days = Math.round(hours / 24);
return `${days}d`;
};

Comparison of between hours using JavaScript

In my application I want to determine if a hour:minute is bigger than 21:00h and other hour:minute is lesser than 08:00.
I am using the 24-hour format for this.
var one = "21:30";
var two = "09:51";
To get just hour from hour and minutes I use split():
var h_1 = one.split(":"); //21
var h_2 = two.split(":"); //08
if (h_1 > "21" && h_2 < "08") {
// Do something
}
The real story for the application is:
A shop has an option to deliver outside of working time (working hours start at "08:00" - "21:00").
If a customer wants to buy out of hours, do something.
So why does my approach not work properly? What is the best approach to compare the hours and minutes between two variables of h:m type?
Have you tried comparing the strings?
if (two < "08:00" && one > "21:00")
//magic
As long as your strings are always formatted with a leading zero for one-digit hours, then it woks fine.
The split() method return an array, so to get just hours (first column in your case) you should specify index 0:
var h_1 = one.split(":")[0]; //21
var h_2 = two.split(":")[0]; // 08
i want to determine if hour:minute one is biger than 21:00h and hour:minute two is lesser than 08:00
Use greater than or equal to operator >= and less than or equal to <= to exclude hours 21 and 08:
if(h_1 >= "21" && h_2 <= "08") {
// do somthing
}
Hope this helps.

setInterval and 12 hour clock in javascript

I want to write a script that changes the hour displayed inside a <span> every second, starting from 8AM, and which keeps repeating like a 12 hours clock (11AM, 12PM, ..., 11PM, 12AM, 1AM, ...).
The image will change at 8AM and 8PM. At 8PM, the image will change to a sleeping face, and at 8AM the image will change back to a smile face. However, that is not a problem.
The problems are:
When I set var hour = Number(document.getElementById("time").textContent); then setInterval repeats the time without any problem. However, when I set var hour = 8 instead, and keep same code, then setInterval repeats only once. Can you let me know why it is like that and how to fix it with var hour = 8?
When the hour is repeatedly increased, I cannot make it smart to change back to AM when it reaches to 12 after passing noon (12PM). For example, the code works fine from 8AM to 11PM but when it reaches to 12, the PM does not change to AM. Can you show me how to fix it?
Lastly when I change var period = "AM" instead of using DOM getElementByID like above and keep same code, then it runs to 1PM and then changes to 2AM and never changes to PM again. Can you explain to me why it happens?
If you do not know what I am talking about, you can run my code and to understand more.
Here is the HTML:
<h2>Life goes on!</h2>
<p>The current time is : <span id = "time">8</span> <span id = "period"> AM</span></p>
<img id = "emoticon" src = "smile.gif" alt = "awake">
And here is my JavaScript code:
setInterval(function () {
var hour = Number(document.getElementById("time").textContent);
hour++;
var period = document.getElementById("period").textContent;
if (hour >= 12) {
hour = hour - 12;
period = "PM";
}
if (hour == 0) {
hour = 12;
}
document.getElementById("time").textContent = hour;
document.getElementById("period").textContent = period;
if (hour == 8 && period == "PM") {
document.getElementById("emoticon").src = "sleep.gif";
document.getElementById("emoticon").alt = "sleep";
} else if (hour == 8 && period == "AM") {
document.getElementById("emoticon").src = "smile.gif";
document.getElementById("emotion").alt = "awake";
}
}, 1000);
Let me explain the problems of your code, using the same three points you made:
Your problem about var hour = 8; is that, if you set the hour inside the setInterval callback, your hour will be set to 8 at any second. Therefore, your setInterval doesn't work only once, but it gets run infinite times setting the time always to 9. To avoid this, you can move the variable outside and make it global, setting it to 8 before starting the setInterval.
To change "PM" back to "AM" when it reaches 12PM, just add an if statement (or, better, a ternary operator, like I did in the snippet below) to check if the period is either "PM" or "AM", and behave consequently. Also, be careful with the check: if you check directly on .textContent be sure that the text inside the <span> doesn't have any trailing space: use .trim() to remove extra spaces at the beginning and at the end of the string.
This is the same problem of point 1: you should make the variable period global, and then start the setInterval.
The logic of the following script is simple:
Increase the hour by 1
If the new hour equals 12, then switch to "PM" or back to "PM"
If the new hour is greater than 12, then reset it to 1
Display hour and period
Change from smile.gif to sleep.gif when it's 8PM, and vice versa when it's 8AM.
I also made some little changes to make code easier and faster to read. Here is a working code snippet:
var hour = 8,
period = "AM";
setInterval(function() {
if (++hour >= 12) {
if (hour > 12) hour = 1;
else period = (period == "PM") ? "AM" : "PM";
}
document.getElementById("time").textContent = hour;
document.getElementById("period").textContent = period;
if (hour == 8 && period == "PM") {
document.getElementById("emoticon").src = "sleep.gif";
document.getElementById("emoticon").alt = "sleep";
} else if (hour == 8 && period == "AM") {
document.getElementById("emoticon").src = "smile.gif";
document.getElementById("emotion").alt = "awake";
}
}, 1000);
<h2>Life goes on!</h2>
<p>The current time is: <span id="time">8</span> <span id="period">AM</span></p>
<img id="emoticon" src="smile.gif" alt="awake">
Here it is, it works fine now, give it a try clicking on "Run code snippet".

Javascript calculation not correct

i have a weird problem in my javascript, take a look at my code below:
dateParts = document.getElementById('date').value.split('/');
newDays = 14;
year = dateParts[2];
month = parseInt(dateParts[1]) - 1;
day = parseInt(dateParts[0]) + parseInt(newDays);
alert(dateParts[0]+" + "+newDays+" = "+day);
and assume document.getElementById('date') = 07/01/2013
the calculation will give a correct result = 07 + 14 = 21
the calculation work fine at all date, except for 08/01/2013 / 09/01/2013
which the result is 08 + 14 = 14, any idea whats wrong here?
Your numbers are treated as octals, since you haven't used radix within parseInt()s. You need to adjust your parseInt()s like this:
month = parseInt(dateParts[1], 10) - 1;
day = parseInt(dateParts[0], 10) + parseInt(newDays, 10);
The leading 0 in 08 and 09 is causing JavaScript to assume the number is octal. Since those are not valid octal values, it treats them as 0. See this question for more details.
You should always use a radix when calling parseInt to avoid this problem.
the function is The parseInt(str, redix), if the value in the parseInt start with 0, it is assumed the radix is 8 so '09', '08' is invalid and the function returns 0. You need to call the function like parseInt('08', 10) to get the correct value.

Android-Esque Time Entry Widget doesn't work when minutes section is below ten

I'm trying to make a "widget" used for controlled time entry that uses up and down arrows to increment each section in addition to allowing you to type the values in by hand. It worked fine(looping over from 12 to 1 and 59 to 1 ect.), but when I added a section that formatted the minutes properly when the value was below 10, it started acting up.
Pressing down acts normally until 10 where it displays, "10" then "09" then "0-1".
Pressing up after you get to "0-1" goes to "01" but when you get to "08", it goes back to "01".
function minFix(mins)
{
if ( mins <= 9)
{
mins = "0" + String(mins);
document.getElementById("mins").value = mins;
}
else
{
document.getElementById("mins").value = mins;
}
}
function minUp()
{
var mins = parseInt(document.getElementById("mins").value);
if (mins == 59)
{
mins = 1;
minFix(mins);
}
else
{
mins = (mins+1);
minFix(mins);
}
}
function minDown()
{
var mins = parseInt(document.getElementById("mins").value);
if (mins == 1)
{
mins = 59;
minFix(mins);
}
else
{
mins = (mins-1);
minFix(mins);
}
}
The minUp and minDown are called by the up and down arrows respectively and the textbox that displays mins has an ID of mins.
I'm sure I'm missing something simple but I can't figure it out right now so any help would be much appreciated.
this has to do with your zero that you're appending. It makes your number into a octal number, so when you read it back in, it's not being read as a decimal number. Take a look at how parseInt works and you'll understand. You need to remove the leading zero before parsing.
Scroll down for the parseInt function here: http://www.javascripter.net/faq/convert2.htm
so this line var mins = parseInt(document.getElementById("mins").value);
should be
var mins = parseInt(document.getElementById("mins").value.replace(/^0+/,""));
Oh, and welcome to Stack! please don't forget to up-vote the answers that you feel are best, and give the green checkmarks to those that you feel are the correct ones.

Categories

Resources