SimpleDateFormat showing minutes, seconds and milliseconds wrong - java

I have written this sample program where I want to convert a date into another format. I don't see the expected date when using simple date format.
public class TestDate {
/**
* #param args
*/
public static void main(String[] args) {
SimpleDateFormat originalformat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss.SSSSSS");
SimpleDateFormat targetformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
try {
//Use Simple Date Format
Date date = originalformat.parse("2015-04-09-17.18.48.677862");
System.out.println("Using SDF "+targetformat.format(date));
//Use Manual Translation
String eventTime = "2015-04-09-17.18.48.677862";
StringBuffer timeBuffer = new StringBuffer();
for (int i = 0; i < eventTime.length(); i++) {
if (i == 10) {
timeBuffer.append(" ");
continue;
} else if (i == 13 || i == 16) {
timeBuffer.append(":");
continue;
} else {
timeBuffer.append(eventTime.charAt(i));
}
}
timeBuffer.append("000");
String transformedTime = timeBuffer.toString().trim();
System.out.println("Manual Translation "+transformedTime);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I see the following outputs. The first logic is using simple date format and the second one is manual translation.
Using SDF 2015-04-09 17:30:05.000862
Manual Translation 2015-04-09 17:18:48.677862000
So How to make the simple date format to output a exactly similar value like the manual one

The value 677862 is being interpreted as milliseconds, as per the SimpleDateFormat javadocs, not as microseconds. That is 677 seconds, 862 milliseconds. The seconds part is 11 minutes and 17 seconds, which is added to 17.18.48 to become 17.30.05.
To work with the S format, you will need 3 digits for the milliseconds, not 6. You will need to truncate your string to 3 digits past the last decimal point.

It would appear that you can't use 6 digits for the milliseconds. Your program is 11 minutes and 17 seconds off. 660 seconds = 11 minutes and you have 17 seconds off. So its just converting your input into minutes from seconds since it can't accept more than 3 milliseconds digits.

Related

What is the algorithm represented in Java to convert an 12 hour am/pm meridiem format hour to 24 hour format?

I needed to convert only the hour part of 12 hour time string to 24 hour format. I'm not interested in other than hour parts of a date string. SimpleDateFormatter is notoriously buggy and I in fact I'm more interested in the conversion algorithm itself. So what I want is to find the algorithm to convert the hour part. This is example of an uncleaned string containing hour part: "12:15PM". Sometimes there is spaces between minutes and meridiem sometimes not. Sometimes it has also character 160. So before conversion method I check and clean the string parts and then feed then meridiem(am or pm) and hour to the method which returns hour as 24 hour format int.
This is my naive style code for beginners to convert an hour in 12 hour format to an hour in 24 format.
public static int convert12to24(String meridiem, String hour) {
//meridiem is that am or pm,
meridiem = meridiem.toLowerCase();
int h_12 = 0;
int h_24 = 0;
try {
h_12 = Integer.parseInt(hour);
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (h_12 == 12) {
//this is the midnight hour
if (meridiem.contains("am")) {//generally before noon
h_24 = 0;
} else {//this is the hour starting at noon
h_24 = 12;
}
} else if (h_12 >= 1)//all the rest
{
if (meridiem.contains("am")) {
//hour starting after first hour at midnight to 11 facing noon
h_24 = h_12;
} else {//pm hours starting right after first hour after noon
h_24 = h_12 + 12;
}
}
return h_24;
}
java.time
I recommend using java.time, the modern Java date and time API, rather than your own calendar implementation. It can be used in all locales.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mma", Locale.ENGLISH);
String timeString12Hours = "12:15PM";
LocalTime time = LocalTime.parse(timeString12Hours, formatter);
System.out.println("Time: " + time);
int hour = time.getHour();
System.out.println("Hour of day in 24 hour format: " + hour);
Output is:
Time: 12:15
Hour of day in 24 hour format: 12
What we get for free from java.time is validation. This code will detect if meridiem is not either AM or PM, or the hour is outside the range 1 through 12 or not a number at all.
Under no circumstances use the SimpleDateFormat class mentioned in the question. It’s a notorious troublemaker of a class and long outdated.
Edit: If for compatibility with old code that I don’t know and that you haven’t got time to clean up at the moment you need the method signature from your own answer:
private static DateTimeFormatter meridiemHourFormatter
= DateTimeFormatter.ofPattern("ahh", Locale.ENGLISH);
public static int convert12to24(String meridiem, String hour) {
String meridiemHour = meridiem + hour;
return LocalTime.parse(meridiemHour, meridiemHourFormatter).getHour();
}
Trying it out:
System.out.println(convert12to24("PM", "12"));
12
Every reader can decide for himself or herself: Which method implementation takes less energy to understand if in a hurry, yours or mine?
Link: Oracle tutorial: Date Time explaining how to use java.time.

SimpleDateFormat missing time

I have a strange issue. The below code is executed in a while loop through a few times. Now, every so often, this sdf.parse returns 0s for the hours, minutes and seconds. An example of the dates look like this...
2014:3:7:8:0
2014:3:7:9:0
2014:3:7:10:0
2014:3:7:11:0
2014:3:7:12:0 * This returns 0's
2014:3:7:13:0
2014:3:7:14:0
Below is the code.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy:M:d:h:m");
sdf.setTimeZone(TimeZone.getDefault());
Date sTime = null;
try {
sTime = sdf.parse(start);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
I think 12 at hour position is getting read in 12h format, so it is the same as 0. Try H instead of h in pattern
SimpleDateFormat sdf = new SimpleDateFormat("yyyy:M:d:H:m");
'h' represents hour in 1-12 format. You should use 'H' (in upper case) instead if you want 0-23 format. Also you needn't explicitly set default time zone because by default it equals TimeZone.getDefault().

Java add dates of format dd:HH:mm:ss

I have three dates as String objects in the format: dd:HH:mm:ss
00:1:9:14
00:3:10:4
00:3:39:49
How do I add these dates in Java to get the sum (00:7:59:07)?
Sample code:
SimpleDateFormat sdf = new SimpleDateFormat("dd:HH:mm:ss");
Date d1 = sdf.parse("00:1:9:14");
Date d2 = sdf.parse("00:3:10:4");
Date d3 = sdf.parse("00:3:39:49");
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
Date d = new Date(d1.getTime() + d2.getTime() + d3.getTime());
System.out.println(d);
Output(wrong):
Wed Dec 31 01:09:14 IST 1969
Wed Dec 31 03:10:04 IST 1969
Wed Dec 31 03:39:49 IST 1969
Sun Dec 28 20:59:07 IST 1969
The dd format includes the day of the month. So your value of 00 will underflow if you use SimpleDateFormat (or Java Date because it also includes a day of the month). Instead, parse your time parts and do the math yourself.
For example, you could create a class TimePart with days, hours, minutes and seconds like
static class TimePart {
int days = 0;
int hours = 0;
int minutes = 0;
int seconds = 0;
static TimePart parse(String in) {
if (in != null) {
String[] arr = in.split(":");
TimePart tp = new TimePart();
tp.days = ((arr.length >= 1) ? Integer.parseInt(arr[0]) : 0);
tp.hours = ((arr.length >= 2) ? Integer.parseInt(arr[1]) : 0);
tp.minutes = ((arr.length >= 3) ? Integer.parseInt(arr[2]) : 0);
tp.seconds = ((arr.length >= 4) ? Integer.parseInt(arr[3]) : 0);
return tp;
}
return null;
}
public TimePart add(TimePart a) {
this.seconds += a.seconds;
int of = 0;
while (this.seconds >= 60) {
of++;
this.seconds -= 60;
}
this.minutes += a.minutes + of;
of = 0;
while (this.minutes >= 60) {
of++;
this.minutes -= 60;
}
this.hours += a.hours + of;
of = 0;
while (this.hours >= 24) {
of++;
this.hours -= 24;
}
this.days += a.days + of;
return this;
}
#Override
public String toString() {
return String.format("%02d:%02d:%02d:%02d", days, hours, minutes,
seconds);
}
}
Then your test-cases like
public static void main(String[] args) {
try {
TimePart d1 = TimePart.parse("00:1:9:14");
TimePart d2 = TimePart.parse("00:3:10:4");
TimePart d3 = TimePart.parse("00:3:39:49");
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
TimePart d4 = d1.add(d2).add(d3);
System.out.println(d4);
} catch (Exception e) {
e.printStackTrace();
}
}
And it seems to perform the addition correctly like
00:01:09:14
00:03:10:04
00:03:39:49
00:07:59:07
The above sum is arithmetic addition so you need a ref --here d0 (default epoch). Date class has a lot of problems beware...
SimpleDateFormat sdf = new SimpleDateFormat("dd:HH:mm:ss");
Date d0 = sdf.parse("00:00:00:00"); // ref
Date d1 = sdf.parse("00:01:09:14");
Date d2 = sdf.parse("00:03:10:04");
Date d3 = sdf.parse("00:03:39:49");
System.out.println(d0);
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
Date d = new Date(d1.getTime() + d2.getTime() + d3.getTime() - 2 * d0.getTime()); // impt
System.out.println(d);
Note:- Date addition is not an easy task, think twice.
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String s1 = "01:02:03";
String s2 = "10:12:13";
Date d1 = format.parse(s1);
Date d2 = format.parse(s2);
int sec = d1.getSeconds() + d2.getSeconds();
int min = d1.getMinutes() + d2.getMinutes();
int hr = d1.getHours() + d2.getHours();
Time sum = new Time(hr, min, sec);
System.out.println(sum); // Output: 11:14:16
Do your strings represent/denote amounts of time? So use the Duration class. Let’s first write an auxiliary method that parses a string into a Duration:
private static Duration parseDuration(String timeString) {
// First convert the string to ISO 8601 through a regex
String isoTimeString = timeString.replaceFirst("^(\\d+):(\\d+):(\\d+):(\\d+)$", "P$1DT$2H$3M$4S");
// Then parse into Duration
return Duration.parse(isoTimeString);
}
Duration.parse() requires ISO 8601 format, it goes like PT1H9M14S for a period of time of 1 hour 9 minutes 14 seconds. Or optionally P0DT1H9M14S. The 0D for 0 days goes before the T. So I use a regular expression (AKA a regex) to modify your string format into ISO 8601 before parsing it. The $1, $2, etc., in the replacement string refer to what was matched inside the round brackets, the so-called groups in the regular expression.
Now we can add the times up:
String[] timeStrings = { "00:1:9:14", "00:3:10:4", "00:3:39:49" };
Duration totalTime = Duration.ZERO;
for (String timeString : timeStrings) {
Duration dur = parseDuration(timeString);
totalTime = totalTime.plus(dur);
}
System.out.println(totalTime);
Output:
PT7H59M7S
7 hours 59 minutes 7 seconds. If you want, you may format it back into your format of 00:7:59:07. Search for how.
What went wrong in your code?
Your first mistake seems to have been before writing the code: thinking of the times as dates. They are not, and it would not make any sense to add dates. What is the sum of April 7 and December 25?
Mislead by this thinking, you tried to parse into Date objects. A Date is a point in time, not an amount of time, so this is wrong. Other than that the Date class is poorly designed, and the SimpleDateFormat class that you also tried to use is notoriously troublesome. Fortunately we’ve got no use for them here, and also for dates and times they are long outdated, superseded by java.time, the modern Java date and time API, of which Duration is but one of many classes.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Documentation of…
String.replaceFirst() on the use of a regular expression
Duration
Question: How to format a duration in java? (e.g format H:MM:SS); some of the answers are using the Duration class.
private static String addTimes(String time1, String time2) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTime(dateFormat.parse(time1));
c2.setTime(dateFormat.parse(time2));
c1.add(Calendar.HOUR, c2.get(Calendar.HOUR));
c1.add(Calendar.MINUTE, c2.get(Calendar.MINUTE));
c1.add(Calendar.SECOND, c2.get(Calendar.SECOND));
return dateFormat.format(c1.getTime());
}
addTimes("1:9:14", "3:10:4");
Output: 04:19:18

Issue with java DateFormat

Following is a piece of code that I am running.
#Test
public void testMyMehotd() {
String expected = "2012-09-12T20:13:47.796327Z";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'");
//df.setTimeZone(TimeZone.getTimeZone("UTC"));
Date d = null;
try {
d = df.parse(expected);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
String actual = df.format(d);
System.out.println(expected);
System.out.println(actual);
}
but the output is different than what I expect.
expected : 2012-09-12T20:13:47.796327Z
actual : 2012-09-12T20:27:03.000327Z
Can someone tell me the reason for this and what is the solution.
Thanks in advance.
Whenever you exceed 999 milliseconds, DateFormat will try to add the remaining milliseconds to your date. Consider the following simpler example:
String expected = "2012-09-12T20:13:47.1001Z";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSS'Z'");
Date d = df.parse(expected);
The resulting date will be 2012-09-12T20:13:48.0001. That is, since you have 1001 milliseconds, you get 1 extra second (1000 milliseconds), and 1 millisecond (1001 % 1000). Thus instead of 47 seconds as in the original date, you get 48 seconds.
This is also what happens if you try to parse a date with an invalid number of days in a month. For example, if you try to add an extra day to September, and parse 2012-09-31:
String expected = "2012-09-31";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date d = df.parse(expected);
System.out.println(df.format(d));
You'll actually get 2012-10-01. Again, that's because DateFormat will detect that the 31st day of September is not valid, and will try to use heuristics to transform the Date, thus adding one day, ending up with the first day of the next month.
There's an option to tell the parser not to use these heuristics, by setting lenient mode to false with:
df.setLenient(false);
However, using this mode, both above examples will throw a ParseException.
S means millisecond and you passed 796327 milliseconds. That number is equal to 13[min]:16[sec]:327[millis] so additional minutes and seconds ware added to your date.

convert a string of time to 24 hour format

I have a string holding a start time and an end time in this format 8:30AM - 9:30PM I want to be able to strip out the AM - and the PM and convert all the times to 24 hour format so 9:30PM would really be 21:30 and also have both the times stored in 2 different variables, I know how to strip the string into substrings but Im not sure about the conversion, this is what I have so far. the time variable starts out holding 8:30AM - 9:30PM.
String time = strLine.substring(85, 110).trim();
//time is "8:30AM - 9:30PM"
String startTime;
startTime = time.substring(0, 7).trim();
//startTime is "8:30AM"
String endTime;
endTime = time.substring(9).trim();
//endTime "9:30AM"
Working code (considering that you managed to split the Strings):
public class App {
public static void main(String[] args) {
try {
System.out.println(convertTo24HoursFormat("12:00AM")); // 00:00
System.out.println(convertTo24HoursFormat("12:00PM")); // 12:00
System.out.println(convertTo24HoursFormat("11:59PM")); // 23:59
System.out.println(convertTo24HoursFormat("9:30PM")); // 21:30
} catch (ParseException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Replace with KK:mma if you want 0-11 interval
private static final DateFormat TWELVE_TF = new SimpleDateFormat("hh:mma");
// Replace with kk:mm if you want 1-24 interval
private static final DateFormat TWENTY_FOUR_TF = new SimpleDateFormat("HH:mm");
public static String convertTo24HoursFormat(String twelveHourTime)
throws ParseException {
return TWENTY_FOUR_TF.format(
TWELVE_TF.parse(twelveHourTime));
}
}
Now that I think about it, SimpleDateFormat, H h K k can be confusing.
Cheers.
You need to use: SimpleDateFormat
And can refer this tutorial: Formatting hour using SimpleDateFormat
Example:
//create Date object
Date date = new Date();
//formatting hour in h (1-12 in AM/PM) format like 1, 2..12.
String strDateFormat = "h";
SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
System.out.println("hour in h format : " + sdf.format(date));
I wouldn't reinvent the wheel (unless you are doing this as a school project or some such).
Just get a date object out of your time stamp and then you can generate whatever format you want with this: SimpleDateFormat
[edited to address your specific request]
if you absolutely need to work from your own unique strings, then you'll do something like this (I don't know exactly what your strings look like... you're using offsets like 85, which means nothing out of context).
I didn't check this for bugs, but this is approximately what you want...
myStr = timestampString.toLowerCase(); //something like 8:30am
boolean add12 = (myStr.indexOf("pm") != -1)?true:false;
//convert hour to int
int hour = Integer.parseInt(myStr.split(":")[0]);
int minutes = Integer.parseInt( myStr.split(":")[1].replaceAll("\\D+","").replaceAll("^0+","") ); //get the letters out of the minute part and get a number out of that, also, strip out leading zeros
int militaryTime = hour + (add12)? 12:0;
if(!add12 && militaryTime == 12)
militaryTime = 0; //account for 12am
//dont' forget to add the leading zeros back in as you assemble your string
With Joda Time, the code looks like:
DateTimeFormatter formatter12 = DateTimeFormat.forPattern("K:mma");
DateTime begin = formatter12.parseDateTime(beginTime);
DateTime end = formatter12.parseDateTime(endTime);
DateTimeFormatter formatter24 = DateTimeFormat.forPattern("k:mma");
String begin24 = formatter24.print(begin);
String end24 = formatter24.print(end);
I should like to contribute the modern answer
DateTimeFormatter twelveHourFormatter = DateTimeFormatter.ofPattern("h:mma", Locale.ENGLISH);
String time = "8:30AM - 9:30PM";
String[] times = time.split(" - ");
LocalTime start = LocalTime.parse(times[0], twelveHourFormatter);
System.out.println(start.toString());
LocalTime end = LocalTime.parse(times[1], twelveHourFormatter);
System.out.println(end.toString());
This prints:
08:30
21:30
I am using java.time, the modern Java date and time API. The SimpleDateFormat class used in many of the other answers is long outdated and was always troublesome. java.time is so much nicer to work with than the date-time classes from the 1990’s. A LocalTime is a time of day without a date (and without time zone), so suits your need much better than an old-fashioned Date.
Link: Oracle tutorial: Date Time explaining how to use java.time.
24 hour time adds 12 to any time greater than 12pm so that 1pm is 13 and so on until 24 or 12am. Here is the sudo code:
if(hour <= 12)
{
hour = hour + 12;
}
All the below lines will works when
String str="07:05:45PM";
and when you call timeConversion(str) and want to convert to 24 hours format..
public class TimeConversion {
private static final DateFormat TWELVE_TF = new SimpleDateFormat("hh:mm:ssa");
private static final DateFormat TWENTY_FOUR_TF = new SimpleDateFormat("HH:mm:ss");
static String timeConversion(String s) {
String str = null;
try {
str= TWENTY_FOUR_TF.format(
TWELVE_TF.parse(s));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return str;
}
public static void main(String[] args) throws ParseException {
String str="07:05:45PM";
System.out.println(timeConversion(str));
}
}

Categories