What is the best way to parse time that a user typed in a text field in GWT? Default time formats require users to enter time exactly as the time format for locale specifies it.
I want to be more flexible as there are many different ways users can enter time. For example, entries like "8", "8p", "8pm", "8.15pm", "13:15", "1315", "13.15" should be valid.
I ended up with my own method that I want to share. This method returns time in milliseconds, which can be displayed using any data formats for the selected locale.
Any suggestions to improve it are highly appreciated.
EDIT: Improved following suggestions in comments.
public static Long parseTime(String value) {
// ";" is a common typo - we are not punishing users for it
value = value.trim().toLowerCase().replace(";", ":");
RegExp time12 = RegExp.compile("^(1[012]|[1-9])([:.][0-5][0-9])?(\\s)?(a|p|am|pm)?$");
RegExp time24 = RegExp.compile("^(([01]?[0-9]|2[0-3])[:.]?([0-5][0-9])?)$");
if (time12.test(value) || time24.test(value)) {
String hours = "0", minutes = "0";
if (value.contains(":") || value.contains(".")) {
String[] values = value.split("[:.]");
hours = values[0];
minutes = values[1].substring(0, 2);
} else {
// Process strings like "8", "8p", "8pm", "2300"
if (value.contains("a")) {
hours = value.substring(0, value.indexOf("a")).trim();
} else if (value.contains("p")) {
hours = value.substring(0, value.indexOf("p")).trim();
} else if (value.length() < 3) {
hours = value;
} else {
hours = value.substring(0, value.length() - 2);
minutes = value.substring(value.length() - 2);
}
}
if (value.contains("a") && hours.equals("12")) {
// 12am is actually zero hours
hours = "0";
}
Long time = (Long.valueOf(hours) * 60 + Long.valueOf(minutes)) * 60 * 1000;
if (value.contains("p") && !hours.equals("12")) {
// "pm" adds 12 hours to the total, except for 12pm
time += 12 * 60 * 60 * 1000;
}
return time;
}
return null;
}
Related
I have one question for my first development project in Java9.
I need to insert an Object with some info and a Date type, inside an ArrayList
only if the time is not present in the ArrayList and if it respects the condition that every time in the ArrayList must be stored every 10 minutes.
So, for example the idea is something like that:
correct: [21:10, 21:20, 21:50, 22:00],
incorrect: [21:05, 21:10, 21:20, 21:55, 22:00]
I try something like this:
private static ArrayList<Order> orders = new ArrayList<Order>();
//ordinazione = orderTime,name,surname,adderess,pizze;
public Pizzeria() {
Order ord = new Order(name, surname, address, num_pizze);
isValid(ord);
}
private void isValid(Order ord) {
boolean valid = false;
long prew,current;
long add = ord.getOrderTime().getTime();
int min10 = 10*60*1000; //10 minutes
if(orders.size() == 0) {
addOrder(ord);
}else if(orders.size() == 1){
current = orders.get(0).getOrderTime().getTime() / 1000;
if(add > current) {
if(add - current >= min10) {
valid = true;
addOrder(order);
}
}else{
if(current - add >= min10) {
valid = true;
addOrder(ord);
}
}
}else{
int i = 0;
while (orders.size() > i) {
prec = i > 1 ? orders.get(i-1).getOrderTime().getTime() / 1000 : 0;
current = orders.get(i).getOrderTime().getTime() / 1000;
if (current - add >= min10 && add - prec >= min10) {
valid = true;
addOrder(ord);
break;
}
i++;
}
}
if(!valid) {
System.out.println("this time is not allowed!");
}
}
public void addOrder(Orderd ord) {
orders.add(ord);
}
any advice?
It may not be the most beautiful solution, but it’s pretty simple: allocate an array with a slot for an order every 10 minutes of the day.
First we need the array of orders:
/** Accept an order every 10 minutes */
private static final int SLOT_SIZE_MINUTES = 10;
private Order[] orders
= new Order[Math.toIntExact(Duration.ofDays(1).toMinutes()) / SLOT_SIZE_MINUTES];
Now the method to fit a new order into the orders could be like:
/**
* If the order fits into a vacant slot that agrees with the time +/- 10 minutes,
* adds it there, adjusts the order time and returns true;
* otherwise returns false
*/
private boolean addOrderIfValid(Order newOrder) {
LocalTime orderTime = newOrder.getOrderTime();
int slotIndex = orderTime.get(ChronoField.MINUTE_OF_DAY) / SLOT_SIZE_MINUTES;
if (orders[slotIndex] != null) { // slot is already taken
slotIndex++;
if (slotIndex == orders.length) { // end of array
// start over from 00:00
slotIndex = 0;
}
}
if (orders[slotIndex] == null) { // vacant
newOrder.setOrderTime(LocalTime.MIN.with(ChronoField.MINUTE_OF_DAY, slotIndex * SLOT_SIZE_MINUTES));
orders[slotIndex] = newOrder;
return true;
} else { // did not find a vacant slot
return false;
}
}
What my method does: It tries to fit the order into the right 10 minutes slot based on the order time already in the order, truncating the time to a 10 minutes slot. If the slot is already taken, it looks into the following slot, using cyclic overflow. If either of the two slots tried is vacant, it sets the order time to the appropriate time for the slot found. This is also what ensures that the time will be at a whole 10 minutes of the hour: 21:00, 21:10, etc., not 21:05.
Link
Oracle tutorial: Date Time explaining how to use java.time.
So I have this assignment that is asking us to take in a String format of time in the order of HH:MM:SSAM or HH:SS:MMPM. The constraint is that it cannot run if it is in wrong format, let it be missing any form of the AM or PM, missing a number, or if it is in 24 Hour Format.
I have the whole idea down, however for my statements, it is giving me the error of:
bad operand types for binary operator '>'
incomparable types: String and int
Did I convert them improperly or am I doing something else wrong?
public static void main(String args[]) {
//Test Methods
String fullTime1 = "03:21:36AM";
secondsAfterMidnight(fullTime1);
}
public static int secondsAfterMidnight(String time) {
String[] units = time.split(":");
int hours = Integer.parseInt(units[0]);
int minutes = Integer.parseInt(units[1]);
int seconds = Integer.parseInt(units[2]);
int totalSeconds = 0;
if (units[0] > 12 || units[1] > 59 || units[2] > 59) { //1st Error applies to these three, units[0] > 12 units[1] > 59 units[2] > 59
return -1;
} else if (time.equalsIgnoreCase("AM") || time.equalsIgnoreCase("PM")) {
totalSeconds = (hours * 3600) + (minutes * 60) + (seconds);
} else if (time.equalsIgnoreCase("AM") && units[0] == 12) { //2nd Error applies to this units[0] == 12
totalSeconds = (minutes * 60) + (seconds);
} else {
return -1;
}
return totalSeconds;
}
You have already parsed the String values and saved them in the variables hours , minutes, seconds. Then you can use those for the check in the if.
Also the presence of AM?PM in the Integer.parseInt() will cause NumberFormatException to avoid it remove the String part from the number by using regex.
Also for checking the presence of AM/PM you can use String.contains.
Please check the reformatted code below:
public static int secondsAfterMidnight(String time) {
String[] units = time.split(":");
int hours = Integer.parseInt(units[0]);
int minutes = Integer.parseInt(units[1]);
int seconds = Integer.parseInt(units[2].replaceAll("[^0-9]", ""));
int totalSeconds = 0;
if (hours > 12 || minutes > 59 || seconds > 59) {
return -1;
} else if (time.contains("AM") || time.contains("PM")) {
totalSeconds = (hours * 3600) + (minutes * 60) + (seconds);
} else if (time.contains("AM") && hours == 12) {
totalSeconds = (minutes * 60) + (seconds);
} else {
return -1;
}
return totalSeconds;
}
Please note that even though you have converted the String to int, you are still comparing String with int. There would also be a RuntimeException when you do this:
int seconds = Integer.parseInt(units[2]);
As units[2] will contain 36AM. So you should be using substring() to remove the "AM/PM" part.
units is of type String and you are trying to compare it with an int hence the compile time error.
You need to convert the String to an int and then compare it, as shown below :
Integer.parseInt(units[0]) > 12
so on and so forth.
Also rather than re-inventing the wheel, you can make use of the already existing java-8's LocalTime to find the number of seconds for a particular time:
public static int secondsAfterMidnight(String time) {
LocalTime localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("hh:mm:ss a"));
return localTime.toSecondOfDay();
}
I haven't verified your logic to calculate the seconds, but this code has corrections:
public static int secondsAfterMidnight(String time) {
String[] units = time.split(":");
int hours = Integer.parseInt(units[0]);
int minutes = Integer.parseInt(units[1]);
int seconds = 0;
String amPm = "";
if ( units[2].contains("AM") || units[2].contains("PM") ||
units[2].contains("am") || units[2].contains("pm") ) {
seconds = Integer.parseInt(units[2].substring(0, 2));
amPm = units[2].substring(2);
}
else {
seconds = Integer.parseInt(units[2]);
}
int totalSeconds = 0;
if (hours > 12 || minutes > 59 || seconds > 59) {
return -1;
} else if (amPm.equalsIgnoreCase("AM") || amPm.equalsIgnoreCase("PM")) {
totalSeconds = (hours * 3600) + (minutes * 60) + (seconds);
} else if (amPm.equalsIgnoreCase("AM") && hours == 12) {
totalSeconds = (minutes * 60) + (seconds);
} else {
return -1;
}
return totalSeconds;
}
java.time
static DateTimeFormatter timeFormatter
= DateTimeFormatter.ofPattern("HH:mm:ssa", Locale.ENGLISH);
public static int secondsAfterMidnight(String time) {
try {
return LocalTime.parse(time, timeFormatter).get(ChronoField.SECOND_OF_DAY);
} catch (DateTimeParseException dtpe) {
return -1;
}
}
Let’s try it out using the test code from your question:
String fullTime1 = "03:21:36AM";
System.out.println(secondsAfterMidnight(fullTime1));
12096
This is the recommended way for production code.
Only if you are doing an exercise training string manipulation, you should use one of the other answers.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I am currently trying to write a method that takes a string like "1pm" and converts it to military time --> 13
Right now I have the following and it is not working correctly. Any tips would be greatly appreciated.
/**
* Set the hour of this appointment, using a more human-friendly
* string.
* #param newHour The new hour for this appointment, using an
* am/pm designation such as "9am" or "5pm".
*/
public void setTime(String newHour)
{
String day = newHour.substring(newHour.length() - 2);
String dig = newHour.substring(2, newHour.length() - 2);
if (dig.equals("12"))
{
dig = "0";
}
if (day.equals("am"))
{
hour = Integer.parseInt(dig);
}
else
{
hour = Integer.parseInt(dig) + 12;
}
}
Your "dig" string is wrong. Its startindex should be 0.
String dig = newHour.substring(0, newHour.length() - 2);
I want to calculate the difference in minutes between two dates, if this difference is > 0 and < 50 minutes, I show a textview with the next text: "X until limit time" (it's on time, but is close to the limit)
If the difference is < 0 (it's late) the textview should say "X minutes late"
Right now everything work for the first condition, but for the second one, the number sometimes show 740 minutes late or bigger numbers
Date fechaactual = new Date();
long diff = trackingfecha.getTime() - fechaactual.getTime();
long diferenciaminutos = diff / (60 * 1000);
RunneableUpdateUI run = new RunneableUpdateUI();
if(diferenciaminutos < 50) {
if(diferenciaminutos >= 0) {
//Show the minutes close to some specific time
StringBuffer buffer = new StringBuffer();
buffer.append("te quedan ");
String minutes = String.format("%02d", diferenciaminutos);
buffer.append(minutes);
buffer.append(" min");
run.toUpdateValue = buffer.toString();
} else {
//Here is the problem
StringBuffer buffer = new StringBuffer();
String minutes = String.format("%02d", Math.abs(diferenciaminutos));
buffer.append(minutes);
buffer.append(" min tarde");
run.toUpdateValue = buffer.toString();
}
timerHandler.post(run);
}
if (0<=x && x < 50)
{
// x time to reach
}
else
{
if (0> x)
{
// late
}
}
}
where x is your Time Diffrent
how can I customize the date format in an android development to be like that of twitter and instagram. What i have below my current code, but I don't like the format it produces like "11 minutes ago" or "34 minutes ago". I prefer the twitter format like "11m" or "34m". Please anyone know how i can format my date like that?
Date createdAt = message.getCreatedAt();//get the date the message was created from parse backend
long now = new Date().getTime();//get current date
String convertedDate = DateUtils.getRelativeTimeSpanString(
createdAt.getTime(), now, DateUtils.SECOND_IN_MILLIS).toString();
mPostMessageTimeLabel.setText(convertedDate); //sets the converted date into the message_item.xml view
Had the same problem. Instead of using a library I figured I could probably write my own version and have it be a little more understandable as to what is happening (and be able to tweak it a bit if needed).
Here is the utility method I made (helpful Log statements for Android users to test it out included):
public static String convertLongDateToAgoString (Long createdDate, Long timeNow){
Long timeElapsed = timeNow - createdDate;
// For logging in Android for testing purposes
/*
Date dateCreatedFriendly = new Date(createdDate);
Log.d("MicroR", "dateCreatedFriendly: " + dateCreatedFriendly.toString());
Log.d("MicroR", "timeNow: " + timeNow.toString());
Log.d("MicroR", "timeElapsed: " + timeElapsed.toString());*/
// Lengths of respective time durations in Long format.
Long oneMin = 60000L;
Long oneHour = 3600000L;
Long oneDay = 86400000L;
Long oneWeek = 604800000L;
String finalString = "0sec";
String unit;
if (timeElapsed < oneMin){
// Convert milliseconds to seconds.
double seconds = (double) ((timeElapsed / 1000));
// Round up
seconds = Math.round(seconds);
// Generate the friendly unit of the ago time
if (seconds == 1) {
unit = "sec";
} else {
unit = "secs";
}
finalString = String.format("%.0f", seconds) + unit;
} else if (timeElapsed < oneHour) {
double minutes = (double) ((timeElapsed / 1000) / 60);
minutes = Math.round(minutes);
if (minutes == 1) {
unit = "min";
} else {
unit = "mins";
}
finalString = String.format("%.0f", minutes) + unit;
} else if (timeElapsed < oneDay) {
double hours = (double) ((timeElapsed / 1000) / 60 / 60);
hours = Math.round(hours);
if (hours == 1) {
unit = "hr";
} else {
unit = "hrs";
}
finalString = String.format("%.0f", hours) + unit;
} else if (timeElapsed < oneWeek) {
double days = (double) ((timeElapsed / 1000) / 60 / 60 / 24);
days = Math.round(days);
if (days == 1) {
unit = "day";
} else {
unit = "days";
}
finalString = String.format("%.0f", days) + unit;
} else if (timeElapsed > oneWeek) {
double weeks = (double) ((timeElapsed / 1000) / 60 / 60 / 24 / 7);
weeks = Math.round(weeks);
if (weeks == 1) {
unit = "week";
} else {
unit = "weeks";
}
finalString = String.format("%.0f", weeks) + unit;
}
return finalString;
}
Usage:
Long createdDate = 1453394736888L; // Your Long
Long timeNow = new Date().getTime();
Log.d("MicroR", convertLongDateToAgoString(createdDate, timeNow));
// Outputs:
// 1min
// 3weeks
// 5hrs
// etc.
Feel free to test this out and let me know if you find any issues!
I might be a bit late, but i write it down for somebody who is looking for a solution.
Using PrettyTime you can obtain formatted dates like "2 months ago" and so on.
To fit your needs you have to feed it with a custom TimeFormat object, there is no need to create a new TimeUnit object since we are formatting normal time units.
To do this just create your TimeFormat object for minutes for example:
public class CustomMinuteTimeFormat implements TimeFormat {
#Override
public String format(Duration duration) {
return Math.abs(duration.getQuantity()) + "m";
}
#Override
public String formatUnrounded(Duration duration) {
return format(duration);
}
#Override
public String decorate(Duration duration, String time) {
return time;
}
#Override
public String decorateUnrounded(Duration duration, String time) {
return time;
}
}
Then Instantiate a new PrettyTime instance and set your formatter.
PrettyTime pretty = new PrettyTime();
//This line of code is very important
pretty.registerUnit(new Minute(), new CustomMinuteTimeFormat());
//Use your PrettyTime object as usual
pretty.format(yourDateObject);
This will output "2m" if time elapsed is 2 minutes.