Converting from String to DateTime - java

I want to convert a String of different forms to the Date format. Here is my current code:
SimpleDateFormat sdf = new SimpleDateFormat("hhmma");
Date time = sdf.parse(string);
Testing it out, it will parse inputs such as 1030pm correctly. However, it does not work on inputs like 830pm or any other single digit hours. Is there any way around this? Or do I have to have a different DateFormat ("hmma") for different String lengths?

java.time
You apparently have a time-of-day without any date. The java.util.Date class, despite its poorly chosen name, represents both a date and a time-of-day.
In Java 8 and later, the built-in java.time framework (Tutorial) offers a time-of-day class called LocalTime. Just what you need for a time-only value.
If the hour may not have a padded leading zero, but the minute will, then I suggest simply prepending a zero when the length of input string is shorter.
String input = "834AM";
String s = null;
switch ( input.length () ) {
case 6:
s = input;
break;
case 5:
// Prepend a padded leading zero.
s = "0" + input;
break;
default:
// FIXME: Handle error condition. Unexpected input.
break;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "hhmma" );
LocalTime localTime = formatter.parse ( s , LocalTime :: from );
System.out.println ( "Input: " + input + " → s: " + s + " → localTime: " + localTime );
When run.
Input: 834AM → s: 0834AM → localTime: 08:34
If, on the other hand, a minute number below 10 will be a single digit without a padded leading zero, I have no solution. Seems to me that would be impossibly ambiguous. For example, does 123AM mean 01:23AM or 12:03AM?
Tip: Stick with ISO 8601 formats, specifically 24-hour clock and leading zeros.

Related

Java: DateTimeFormatter and parsing sub-seconds [duplicate]

With the first release of Java 8 (b132) on Mac OS X (Mavericks), this code using the new java.time package works:
String input = "20111203123456";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Rendering:
2011-12-03T12:34:56
But when I add "SS" for fraction-of-second (and "55" as input), as specified in the DateTimeFormatter class doc, an exception is thrown:
java.time.format.DateTimeParseException: Text '2011120312345655' could not be parsed at index 0
The doc says Strict mode is used by default and requires the same number of format characters as input digits. So I'm confused why this code fails:
String input = "2011120312345655";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Another example using example from documentation ("978") (fails):
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
This example works, adding a decimal point (but I find no such requirement in the doc):
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Renders:
localDateTime: 2011-12-03T12:34:56.978
Omitting the period character from either the input string or the format cause a fail.
Fails:
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Fails:
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Bug – Fixed in Java 9
This issue was already reported in JDK-bug-log. Stephen Colebourne mentions as work-around following solution:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
Note: This workaround does not cover your use-case of only two pattern symbols SS. An adjustment might only be to use other fields like MICRO_OF_SECOND (6 times SSSSSS) or NANO_OF_SECOND (9 times SSSSSSSSS). For two fraction digits see my update below.
#PeterLawrey About the meaning of pattern symbol "S" see this documentation:
Fraction: Outputs the nano-of-second field as a fraction-of-second.
The nano-of-second value has nine digits, thus the count of pattern
letters is from 1 to 9. If it is less than 9, then the nano-of-second
value is truncated, with only the most significant digits being
output. When parsing in strict mode, the number of parsed digits must
match the count of pattern letters. When parsing in lenient mode, the
number of parsed digits must be at least the count of pattern letters,
up to 9 digits.
So we see that S stands for any fraction of second (including nanosecond), not just milliseconds. Furthermore, the fractional part does at the moment not take well in adjacent value parsing, unfortunately.
EDIT:
As background here some remarks about adjacent value parsing. As long as fields are separated by literals like a decimal point or time part separators (colon), the interpretation of fields in a text to be parsed is not difficult because the parser then knows easily when to stop i.e. when the field part is ended and when the next field starts. Therefore the JSR-310 parser can process the text sequence if you specify a decimal point.
But if you have a sequence of adjacent digits spanning over multiple fields then some implementation difficulties arise. In order to let the parser know when a field stops in text it is necessary to instruct the parser in advance that a given field is represented by a fixed-width of digit chars. This works with all appendValue(...)-methods which assume numerical representations.
Unfortunately JSR-310 has not managed well to do this also with the fractional part (appendFraction(...)). If you look for the keyword "adjacent" in the javadoc of class DateTimeFormatterBuilder then you find that this feature is ONLY realized by appendValue(...)-methods. Note that the spec for pattern letter S is slightly different but internally delegates to appendFraction()-method. I assume we will at least have to waint until Java 9 (as reported in JDK-bug-log, or later???) until fraction parts can manage adjacent value parsing as well.
Update from 2015-11-25:
The following code using two fraction digits only does not work and misinterpretes the millisecond part:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 2)
.toFormatter();
String input = "2011120312345655";
LocalDateTime ldt = LocalDateTime.parse(input, dtf);
System.out.println(ldt); // 2011-12-03T12:34:56.055
The workaround
String input = "2011120312345655";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
Date d = sdf.parse(input);
System.out.println(d.toInstant()); // 2011-12-03T12:34:56.055Z
does not work because SimpleDateFormat interpretes the fraction in a wrong way, too, similar to the modern example (see output, 55 ms instead of 550 ms).
What is left as solution is either waiting an undertermined long time until Java 9 (or later?) or writing your own hack or using 3rd-party libraries as solution.
Solution based on a dirty hack:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
int len = input.length();
LocalDateTime ldt = LocalDateTime.parse(input.substring(0, len - 2), dtf);
int millis = Integer.parseInt(input.substring(len - 2)) * 10;
ldt = ldt.plus(millis, ChronoUnit.MILLIS);
System.out.println(ldt); // 2011-12-03T12:34:56.550
Solution using Joda-Time:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
Solution using my library Time4J:
String input = "2011120312345655";
ChronoFormatter<PlainTimestamp> f =
ChronoFormatter.ofTimestampPattern("yyyyMMddHHmmssSS", PatternType.CLDR, Locale.ROOT);
System.out.println(f.parse(input)); // 2011-12-03T12:34:56.550
Update from 2016-04-29:
As people can see via the JDK-issue mentioned above, it is now marked as resolved - for Java 9.
DateTimeFormatterBuilder#appendFraction(ChronoField.MILLI_OF_SECOND, 0, 3, true)
Something like this helped me
Here's an algorithm which adjusts the order of the trailing zeros that are conventionally returned from the formatted date String.
/**
* Takes a Date and provides the format whilst compensating for the mistaken representation of sub-second values.
* i.e. 2017-04-03-22:46:19.000991 -> 2017-04-03-22:46:19.991000
* #param pDate Defines the Date object to format.
* #param pPrecision Defines number of valid subsecond characters contained in the system's response.
* */
private static final String subFormat(final Date pDate, final SimpleDateFormat pSimpleDateFormat, final int pPrecision) throws ParseException {
// Format as usual.
final String lString = pSimpleDateFormat.format(pDate);
// Count the number of characters.
final String lPattern = pSimpleDateFormat.toLocalizedPattern();
// Find where the SubSeconds are.
final int lStart = lPattern.indexOf('S');
final int lEnd = lPattern.lastIndexOf('S');
// Ensure they're in the expected format.
for(int i = lStart; i <= lEnd; i++) { if(lPattern.charAt(i) != 'S') {
// Throw an Exception; the date has been provided in the wrong format.
throw new ParseException("Unable to process subseconds in the provided form. (" + lPattern + ").", i);
} }
// Calculate the number of Subseconds. (Account for zero indexing.)
final int lNumSubSeconds = (lEnd - lStart) + 1;
// Fetch the original quantity.
String lReplaceString = lString.substring(lStart + (lNumSubSeconds - pPrecision), lStart + lNumSubSeconds);
// Append trailing zeros.
for(int i = 0; i < lNumSubSeconds - pPrecision; i++) { lReplaceString += "0"; }
// Return the String.
return lString.substring(0, lStart) + lReplaceString;
}

How to parse HH:mm into amount of minutes considering locale time separator?

I have an input string of 'HH:mm' format where time separator is according to locale settings (e.g. '10:45' for US or '10.45' for Italy). I need to convert it into amount of minutes.
Here is what I came up with:
String timeSeparator = getTimeSeparator(locale);
String[] duration = durationString.split(Pattern.quote(timeSeparator));
int minutes = Integer.valueOf(duration[0])*60 + Integer.valueOf(duration[1]);
getTimeSeparator method taken from https://stackoverflow.com/a/7612009/7753225
Is there any simpler way? For example using `java.time'.
tl;dr
Duration.parse(
"PT" +
"10:45".replace( ":" , "M" )
.replace( "-" , "M" )
.replace( "." , "M" )
.replace( "," , "M" )
+ "S"
).toMinutes()
"10:45" → PT10M45S → 10
ISO 8601 format
The ISO 8601 standard defines a format for such spans of time not attached to the time line: PnYnMnDTnHnMnS
The P marks the beginning. The T separates any years-months-days from the hours-minutes-seconds. So an hour and a half is PT1H30M. Your example of ten minutes and forty-five seconds is PT10M45S.
Perform simple string manipulations to convert your input to this format.
String input = "10:45" ;
String inputStandardized = "PT" +
input.replace( ":" , "M" )
.replace( "-" , "M" )
.replace( "." , "M" )
.replace( "," , "M" )
+ "S" ;
PT10M45S
Duration
Parse as a Duration. The java.time classes use ISO 8601 formats by default when parsing and generating strings. So no need to specify a formatting pattern.
Duration duration = Duration.parse( inputStandardized );
duration.toString(): PT10M45S
You can use the Duration object to do date-time math, passing to plus and minus. So you may not need the number of minutes as an integer number. But you can ask for total number of minutes in the duration.
long minutes = duration.toMinutes();
10
See this code run live at IdeOne.com.
Tip: Using a clock-time format for a span of time is asking for trouble. The ambiguity leads to confusion and errors. I suggest using the standard ISO 8601 formats for durations as they are designed to be unambiguous, easy to read, and easy to parse.
In Java 8 you could use DateTimeFormatter.ofLocalizedTime to obtain a locale-specific date-time formatter. Then you could use it to parse the string into a LocalTime and extract the minute field.
public static int getMinute(String timeString, Locale locale) {
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(locale);
LocalTime time = LocalTime.parse(timeString, formatter);
return time.getHour()*60 + time.getMinute();
}
Example:
System.out.println(getMinute("10.47", new Locale("fi"))); // 647
System.out.println(getMinute("11:23", Locale.ROOT)); // 683
If you don't care about the time separator, we could use a fixed pattern instead.
public static int getMinute(String timeString, Locale locale) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH[:][.]mm", locale);
LocalTime time = LocalTime.parse(timeString, formatter);
return time.getHour()*60 + time.getMinute();
}
(The […] means optional, so it will match HH:mm, HH.mm, HHmm and HH:.mm)
Note very elegant, but it would do the job
String pattern = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(locale).format(LocalTime.MIN)
.replaceFirst("\\d*", "H").replaceFirst("\\d.*", "mm");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, locale);
int minutes = formatter.parse(string).get(ChronoField.MINUTE_OF_DAY);
First parse the time to a LocalTime instance, afterwards calculate the difference to the start of the day using Duration:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendLocalized(null, FormatStyle.SHORT)
.toFormatter(Locale.ITALY);
LocalTime time = LocalTime.from(formatter.parse("10.45"));
long minutes = Duration.between(LocalTime.MIN, time).toMinutes();
above results to 645
You can use getLocalizedDateTimePattern method to get a pattern with which to initialise a DateTimeFormatter, to parse time with given locale. The first null is for the date that you don't need in your case.
String timePattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
null, FormatStyle.SHORT, IsoChronology.INSTANCE, locale);
You then use it to create a formatter (note that you have to specify locale again):
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(timePattern, locale);
If instead of the SHORT time format for the locale you expect "HHcmm" where 'c' is a non numeric char that is the time separator for that locale, then either:
extract numbers from text
String[] duration = durationString.split("\\D");
int minutes = parseInt(duration[0])*60 + parseInt(duration[1]);
make a DateTimeFormatter with the time separator
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(
String.format("HH%smm", timeSeparator), locale);
LocalTime time = LocalTime.parse(text, dtf);
int minutes = time.toSecondOfDay() / 60;
or if you must
int minutes = ChronoUnit.MINUTES.between(LocalTime.MIN, time);

Timestamp converter

I tried to create an timestamp to Date and Date to timestamp converter in java. the converter logic works perfectly and I displayed it on a console window. But this works not correctly.
If I type this timestamp into my console window:
1449946800000 (I get this timestamp from my calendar web plugin)
Date : 13.10.2016 20:00:00 (Timezone "Europe/Berlin ) = 1476381600000
First time I call a mehtod which required the timestamp format like above.
LongStamp = LongStamp/1000;
logic.convertedTime(LongStamp);
} else {
.....
}
If match, the method logic.convertedTime(xx) is calling. ( See class convertLogic)
Here is my code:
public class convertLogic {
/** TimeZone-Support**/
private static final String TIMEZONEBERLIN = "Europe/Berlin";
/**
* convertedTime: <br>
* input timestamp to convert into date <br>
* <strong>...</strong>
* #param timestamp
*/
public void convertedTime(long timestamp) {
TimeZone timeZoneBerlin = TimeZone.getTimeZone(TIMEZONEBERLIN);
// System.out.println(timeZoneBerlin.getDisplayName());
// System.out.println(timeZoneBerlin.getID());
// System.out.println(timeZoneBerlin.getOffset(System.currentTimeMillis()));
Calendar myDate = GregorianCalendar.getInstance();
myDate.setTimeZone(timeZoneBerlin);
myDate.setTimeInMillis(timestamp * 1000);
int month = myDate.get(Calendar.MONTH) + 1;
int second = Integer.parseInt(leadingZero(myDate.get(Calendar.SECOND)));
System.out.println("Datum: " + myDate.get(Calendar.DAY_OF_MONTH) + "." + month + "." + myDate.get(Calendar.YEAR)
+ " " + myDate.get(Calendar.HOUR_OF_DAY) + ":" + myDate.get(Calendar.MINUTE) + ":"
+ second);
}
/**
* leadingZero for 2 digit values
*
* #param value
* #return String
*/
private static String leadingZero(int value) {
int testValue = value;
if (testValue < 10) {
return "0" + value;
} else {
return value + "";
}
}
And I get following output:
Datum: 13.10.2016 20:0:0
But I want or I need all zeros from the Hour,Minutes and seconds like this :
13.10.2016 20:00:00
Does anybody know an solution?
Thanks in advance!
Fix: Call your leadingZero method
After adding a leading zero you effectively remove it.
After padding with a leading zero, you call Integer.parseInt. That call generates a number, an int. The default format used when converting that to a String has no leading zero.
Change the data type of second to String.
Likewise, pass the result of myDate.get(Calendar.HOUR_OF_DAY) to your leadingZero method.
Strange results
I do not understand your input and results. If 1449946800000 is a count of milliseconds from the epoch of first moment of 1970 UTC, that is a date-time in 2015 not 2016:
2015-12-12T19:00:00Z
2015-12-12T20:00+01:00[Europe/Berlin]
java.time
You are working too hard. Use a date-time framework for this work.
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
Example code
First translate your count-from-epoch into an Instant object representing a moment on the timeline in UTC with a resolution up to nanoseconds.
long input = 1_449_946_800_000L;
Instant instant = Instant.ofEpochMilli ( input );
Apply a time zone, Europe/Berlin. A ZoneId represents the time zone. We use that to generate a ZonedDateTime object.
ZoneId zoneId = ZoneId.of ( "Europe/Berlin" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Create a String to represent the value of our ZonedDateTime object.
No need to define a formatting pattern. The DateTimeFormatter class can automatically localize a date-time representation to a human language and cultural norms defined by a Locale. Your desired format is already known to be the medium-length format commonly used in Germany.
Understand that a time zone and a Locale are distinct and separate. One adjusts the meaning of the date-time to a wall-clock time of some geographic area on the globe. The other translates names of day or month, and determines comma versus period, and other such issues.
Locale locale = Locale.GERMANY;
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.MEDIUM ).withLocale ( locale );
String output = zdt.format ( formatter );
Dump to console.
System.out.println ( "input: " + input + " | instant: " + instant + " | zdt: " + zdt + " | output: " + output );
input: 1449946800000 | instant: 2015-12-12T19:00:00Z | zdt: 2015-12-12T20:00+01:00[Europe/Berlin] | output: 12.12.2015 20:00:00
DateFormat might be your friend to simplify the code (or DateTimeFormatter in case of Java 8).

DateTimeFormatter fails on milliseconds [duplicate]

With the first release of Java 8 (b132) on Mac OS X (Mavericks), this code using the new java.time package works:
String input = "20111203123456";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Rendering:
2011-12-03T12:34:56
But when I add "SS" for fraction-of-second (and "55" as input), as specified in the DateTimeFormatter class doc, an exception is thrown:
java.time.format.DateTimeParseException: Text '2011120312345655' could not be parsed at index 0
The doc says Strict mode is used by default and requires the same number of format characters as input digits. So I'm confused why this code fails:
String input = "2011120312345655";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Another example using example from documentation ("978") (fails):
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
This example works, adding a decimal point (but I find no such requirement in the doc):
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Renders:
localDateTime: 2011-12-03T12:34:56.978
Omitting the period character from either the input string or the format cause a fail.
Fails:
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Fails:
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Bug – Fixed in Java 9
This issue was already reported in JDK-bug-log. Stephen Colebourne mentions as work-around following solution:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
Note: This workaround does not cover your use-case of only two pattern symbols SS. An adjustment might only be to use other fields like MICRO_OF_SECOND (6 times SSSSSS) or NANO_OF_SECOND (9 times SSSSSSSSS). For two fraction digits see my update below.
#PeterLawrey About the meaning of pattern symbol "S" see this documentation:
Fraction: Outputs the nano-of-second field as a fraction-of-second.
The nano-of-second value has nine digits, thus the count of pattern
letters is from 1 to 9. If it is less than 9, then the nano-of-second
value is truncated, with only the most significant digits being
output. When parsing in strict mode, the number of parsed digits must
match the count of pattern letters. When parsing in lenient mode, the
number of parsed digits must be at least the count of pattern letters,
up to 9 digits.
So we see that S stands for any fraction of second (including nanosecond), not just milliseconds. Furthermore, the fractional part does at the moment not take well in adjacent value parsing, unfortunately.
EDIT:
As background here some remarks about adjacent value parsing. As long as fields are separated by literals like a decimal point or time part separators (colon), the interpretation of fields in a text to be parsed is not difficult because the parser then knows easily when to stop i.e. when the field part is ended and when the next field starts. Therefore the JSR-310 parser can process the text sequence if you specify a decimal point.
But if you have a sequence of adjacent digits spanning over multiple fields then some implementation difficulties arise. In order to let the parser know when a field stops in text it is necessary to instruct the parser in advance that a given field is represented by a fixed-width of digit chars. This works with all appendValue(...)-methods which assume numerical representations.
Unfortunately JSR-310 has not managed well to do this also with the fractional part (appendFraction(...)). If you look for the keyword "adjacent" in the javadoc of class DateTimeFormatterBuilder then you find that this feature is ONLY realized by appendValue(...)-methods. Note that the spec for pattern letter S is slightly different but internally delegates to appendFraction()-method. I assume we will at least have to waint until Java 9 (as reported in JDK-bug-log, or later???) until fraction parts can manage adjacent value parsing as well.
Update from 2015-11-25:
The following code using two fraction digits only does not work and misinterpretes the millisecond part:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 2)
.toFormatter();
String input = "2011120312345655";
LocalDateTime ldt = LocalDateTime.parse(input, dtf);
System.out.println(ldt); // 2011-12-03T12:34:56.055
The workaround
String input = "2011120312345655";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
Date d = sdf.parse(input);
System.out.println(d.toInstant()); // 2011-12-03T12:34:56.055Z
does not work because SimpleDateFormat interpretes the fraction in a wrong way, too, similar to the modern example (see output, 55 ms instead of 550 ms).
What is left as solution is either waiting an undertermined long time until Java 9 (or later?) or writing your own hack or using 3rd-party libraries as solution.
Solution based on a dirty hack:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
int len = input.length();
LocalDateTime ldt = LocalDateTime.parse(input.substring(0, len - 2), dtf);
int millis = Integer.parseInt(input.substring(len - 2)) * 10;
ldt = ldt.plus(millis, ChronoUnit.MILLIS);
System.out.println(ldt); // 2011-12-03T12:34:56.550
Solution using Joda-Time:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
Solution using my library Time4J:
String input = "2011120312345655";
ChronoFormatter<PlainTimestamp> f =
ChronoFormatter.ofTimestampPattern("yyyyMMddHHmmssSS", PatternType.CLDR, Locale.ROOT);
System.out.println(f.parse(input)); // 2011-12-03T12:34:56.550
Update from 2016-04-29:
As people can see via the JDK-issue mentioned above, it is now marked as resolved - for Java 9.
DateTimeFormatterBuilder#appendFraction(ChronoField.MILLI_OF_SECOND, 0, 3, true)
Something like this helped me
Here's an algorithm which adjusts the order of the trailing zeros that are conventionally returned from the formatted date String.
/**
* Takes a Date and provides the format whilst compensating for the mistaken representation of sub-second values.
* i.e. 2017-04-03-22:46:19.000991 -> 2017-04-03-22:46:19.991000
* #param pDate Defines the Date object to format.
* #param pPrecision Defines number of valid subsecond characters contained in the system's response.
* */
private static final String subFormat(final Date pDate, final SimpleDateFormat pSimpleDateFormat, final int pPrecision) throws ParseException {
// Format as usual.
final String lString = pSimpleDateFormat.format(pDate);
// Count the number of characters.
final String lPattern = pSimpleDateFormat.toLocalizedPattern();
// Find where the SubSeconds are.
final int lStart = lPattern.indexOf('S');
final int lEnd = lPattern.lastIndexOf('S');
// Ensure they're in the expected format.
for(int i = lStart; i <= lEnd; i++) { if(lPattern.charAt(i) != 'S') {
// Throw an Exception; the date has been provided in the wrong format.
throw new ParseException("Unable to process subseconds in the provided form. (" + lPattern + ").", i);
} }
// Calculate the number of Subseconds. (Account for zero indexing.)
final int lNumSubSeconds = (lEnd - lStart) + 1;
// Fetch the original quantity.
String lReplaceString = lString.substring(lStart + (lNumSubSeconds - pPrecision), lStart + lNumSubSeconds);
// Append trailing zeros.
for(int i = 0; i < lNumSubSeconds - pPrecision; i++) { lReplaceString += "0"; }
// Return the String.
return lString.substring(0, lStart) + lReplaceString;
}

Date localization in java using DateFormat.getDateInstance

I have a date which must be localized.
Below code returns 5/1/12 19:06:34 but the result i want is 05/01/12 19:06:34
Could you please tell me how to manage this.
private String localizeDate(String date){ //Format is 2012-05-01 19:30:49
Locale loc = DataContextHolder.getDataContext().getLocale();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", loc);
Date parsed=null;
try {
parsed = formatter.parse(date);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, loc);
String localizedDate = df.format(parsed) + " " + date.substring(11, 13) + ":"
+ date.substring(14, 16) + ":"
+ date.substring(17, 19);
return localizedDate;
}
You can avoid leading zeros by reducing the number of consecutive pattern letters for that particular element. Multiple pattern letters in a row tell the date formatter that, at minimum, you want that many characters to express that value.
In your example, the following should resolve your problem.
new SimpleDateFormat("y-M-d H:m:s", loc);
Find more in the SimpleDateFormat documentation.
For clarity, see the following example.
SimpleDateFormat a = new SimpleDateFormat("yyyyy-MMMM-dddd HHH:mmmm:sssss");
SimpleDateFormat b = new SimpleDateFormat("y-M-d H:m:s");
System.out.println(a.format(new Date()));
// Prints 02012-June-0005 012:0027:00026
System.out.println(b.format(new Date()));
// Prints 12-6-5 12:27:26
Problems
You have multiple problems happening with your code.
Time-Zone
One problem is that you are localizing without handling time zone. The parsing will apply the JVM's default time zone. That means running on different computers with different time zone settings yields different results. Generally best to specify a time zone rather than rely on default.
Formatting, Not Localizing
Another problem is that if you are truly localizing, you would not be specifying the details of the format. You would let the locale drive the formatting, whatever is appropriate to that locale and to the user's own localization settings.
Avoid java.util.Date
Yet another problem is that you are using the java.util.Date class bundled with Java. That class, and its sibling java.util.Calendar, are notoriously troublesome. Avoid them. Use either the Joda-Time framework or the new java.time.* package in Java 8.
ISO 8601
Your input string is very close to the standard ISO 8601 format, like this: 2014-02-07T07:06:41+03:00. Your text is merely missing a T in the middle and a time zone offset.
The Joda-Time framework uses ISO 8601 by default. So replacing that space with a "T" lets you pass the string directly to a DateTime constructor. No need for a formatter and parsing, as the DateTime constructor will do that work for you automatically.
Note that including or omitting the time zone offset from the end of the string input changes the behavior of constructing a DateTime. Without an offset, the string is parsed as if it occurred at the specified time within the passed time zone (the 2nd argument, see code example below). On the other hand, if you do include an offset in your input, the string is parsed as if it occurred at the specified time in the offset's locality, and then adjusts the time to the passed time zone argument.
Joda-Time Example
Here is some example code in Joda-Time 2.3.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Istanbul" );
String inputOriginal = "2012-05-01 19:30:49";
String input = inputOriginal.replace( " ", "T" );
DateTime dateTime = new DateTime( input, timeZone );
// Or if your input is UTC/GMT (no time zone offset), pass a predefined DateTimeZone constant.
//DateTime dateTime = new DateTime( input, DateTimeZone.UTC );
We have our date-time value in hand. Now we proceed to generate formatted strings from that date-time. We do so by using Joda-Time’s Locale-sensitive formatting facility. Note that not only the format is localized, so is the text of the month and day names localized to appropriate language.
// Create a formatter from a two-character style pattern.
// The first character is the date style, and the second character is the time style.
// Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full.
// A date or time may be ommitted by specifying a style character '-'.
java.util.Locale locale = new java.util.Locale( "tr", "TR" ); // Turkey chosen as an example.
String output_ShortShort = DateTimeFormat.forStyle( "SS" ).withLocale( locale ).print( dateTime );
String output_LongShort = DateTimeFormat.forStyle( "LS" ).withLocale( locale ).print( dateTime );
Dump to console…
System.out.println( "input: " + input );
System.out.println( "dateTime: " + dateTime );
System.out.println( "dateTime in UTC/GMT: " + dateTime.toDateTime( DateTimeZone.UTC ) );
System.out.println( "output_ShortShort: " + output_ShortShort );
System.out.println( "output_LongShort: " + output_LongShort );
When run…
input: 2012-05-01T19:30:49
dateTime: 2012-05-01T19:30:49.000+03:00
dateTime in UTC/GMT: 2012-05-01T16:30:49.000Z
output_ShortShort: 01.05.2012 19:30
output_LongShort: 01 Mayıs 2012 Salı 19:30
You need to choose the appropriate date formatter. Please read the documentation here:
http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html

Categories

Resources