Parse seconds-of-day as part of a date string - java

I need to parse a date string which has the following format:
yyyy-MM-dd TTTTT. All pattern letters are the standard DateTimeFormatter letters, except for the TTTTT part, which is a seconds-of-day field.
As there is no pattern defined for such a field, I needed to come up with something else. My first thought was to try and parse the field as milli-of-day (A), but by using 5 A's my field is treated as the least significant characters which is... yeah, not what I want.
Is there any out-of-the box solution I could use or do I have to resort to some custom made solution (like ignoring the TTTTT field, parsing it manually and using LocalDateTime.plusSeconds() on the resulting date)?
In other words, how can I make the following test case pass?
public class DateTimeParserTest {
private static final String PATTERN = "yyyy-MM-dd TTTTT";
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern(PATTERN);
#Test
public void testParseSecondsOfDay() throws Exception {
String input = "2016-01-01 86399";
LocalDateTime localDateTime = LocalDateTime.parse(input, FORMATTER);
assertEquals("2016-01-01T23:59:59", localDateTime.toString());
}
}

Based on your correction (leaving out the potentially ambivalent part "HH:mm") the Java-8-solution would look like:
private static final DateTimeFormatter FORMATTER =
new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd ")
.appendValue(ChronoField.SECOND_OF_DAY, 5)
.toFormatter();

well it may not be what what you are looking, but i hope it helps
private static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; //MODIFICATION HERE
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern(PATTERN);
#Test
public void testParseSecondsOfDay() throws Exception {
String input = "2016-01-01 00:00:86399";
int lastIndexOf = input.lastIndexOf(':');
CharSequence parsable = input.subSequence(0,lastIndexOf)+":00";
CharSequence TTTTPart = input.subSequence(lastIndexOf+1, input.length());
LocalDateTime localDateTime = LocalDateTime.parse(parsable, FORMATTER);
Calendar calendar = Calendar.getInstance(); // gets a calendar using the default time zone and locale.
calendar.set(
localDateTime.getYear(),
localDateTime.getMonth().ordinal(),
localDateTime.getDayOfMonth(),
localDateTime.getHour(),
localDateTime.getMinute(),
0);
calendar.add(Calendar.SECOND, Integer.parseInt(TTTTPart.toString()));
StringBuilder toParse = new StringBuilder();
toParse.append(calendar.get(Calendar.YEAR) /* It gives 2016 but you what 2016, why */-1);
toParse.append("-").append(calendar.get(Calendar.MONTH) + 1 < 10 ? "0" : "")
.append(calendar.get(Calendar.MONTH)+1);
toParse.append("-").append(calendar.get(Calendar.DAY_OF_MONTH) < 10 ? "0" : "")
.append(calendar.get(Calendar.DAY_OF_MONTH));
toParse.append(" ").append(calendar.get(Calendar.HOUR_OF_DAY) < 10 ? "0" : "")
.append(calendar.get(Calendar.HOUR_OF_DAY));
toParse.append(":").append(calendar.get(Calendar.MINUTE) < 10 ? "0" : "")
.append(calendar.get(Calendar.MINUTE));
toParse.append(":").append(calendar.get(Calendar.SECOND) < 10 ? "0" : "")
.append(calendar.get(Calendar.SECOND));
localDateTime = LocalDateTime.parse(toParse, FORMATTER);
//Why 2015 and not 2016?
assertEquals("2015-01-01T23:59:59", localDateTime.toString());
}
it bugs me with is 2015 and not 2016, be aware that the solution is hammered to give you 2015 instead of 2016.

I'm still using Joda and no java8, but there it would be
Chronology lenient = LenientChronology.getInstance(ISOChronology.getInstance());
DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:sssss").withChronology(lenient);
DateTime dateTime = DateTime.parse("2016-01-01 00:00:86399", FORMATTER);
assertEquals("2016-01-01T23:59:59", dateTime.toLocalDateTime().toString());
Maybe you can obtain the same behavior using:
DateTimeFormatter formatter =
DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT);

Related

How to convert 2022-08-16T06:25:00.000 to HH:mm

I'm trying to convert date from API "2022-08-16T06:25:00.000" to HH:mm (6:25) but getting DateTimeParseException.
My code: ZonedDateTime.parse(it.time[0], DateTimeFormatter.ofPattern("HH:mm"))
"time": [
"2022-08-16T06:25:00.000",
"2022-08-16T07:40:00.000"
],
String dateTimeStr = "2022-08-16T06:25:00.000";
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm");
String time = dateTime.format(fmt);
System.out.println(time);
or, if you want to use the time as an instance of LocalTime, you can get it by dateTime.toLocalTime()
You don't need to define any DateTimeFormatter in this situation.
use a LocalDateTime because the input String does not hold any information about the zone
don't use a DateTimeFormatter for parsing that only parses hour of day and minutes of hour, the String to be parsed just contains more information
Here's an example without any DateTimeFormatter explicitly defined (but it will use default ones for parsing, at least):
public static void main(String[] args) {
// example input
String fromApi = "2022-08-16T06:25:00.000";
// parse it to a LocalDateTime because there's no zone in the String
LocalDateTime localDateTime = LocalDateTime.parse(fromApi);
// extract the time-of-day part
LocalTime localTime = localDateTime.toLocalTime();
// and print its toString() implicitly
System.out.println(localTime);
}
Output: 06:25
The above code will produce output of the pattern HH:mm, which will have leading zeros at hours of day to always have a two-digit representation.
If you insist on single-digit hours of day, you will have to prepare a DateTimeFormatter, like in this alternative example:
public static void main(String[] args) {
// example input
String fromApi = "2022-08-16T06:25:00.000";
// parse it to a LocalDateTime because there's no zone in the String
LocalDateTime localDateTime = LocalDateTime.parse(fromApi);
// extract the time-of-day part
LocalTime localTime = localDateTime.toLocalTime();
// prepare a DateTimeFormatter that formats single-digit hours of day
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("H:mm");
// print the LocalTime formatted by that DateTimeFormatter
System.out.println(localTime.format(dtf));
}
Output this time: 6:25
The other answers use Java. Since you've added a [kotlin] tag, here is a Kotlin-based answer for the sake of completeness. In order to make it different to the Java answers, I'm using kotlinx.datetime, which is still at the experimental stage at version 0.4.0.
import kotlinx.datetime.LocalDateTime
fun main() {
println(LocalDateTime.parse("2022-08-16T06:25:00.000").time) // prints "06:25"
// If you want "6:25" you can format it yourself:
println(with(LocalDateTime.parse("2022-08-16T06:25:00.000")) {
"$hour:$minute"
})
}
How about different approach
String dateTimeStr = "2022-08-16T06:25:00.000";
Matcher m=Pattern.of("T(\\d{2}:\\d{2}):").matcher(dateTimeStr);
m.find();
System.out.println(m.group(1);; //should print 06:25
And yet another "alternative" answer. It relies on the fact that in an ISO-compliant date-time format, the time starts in the 11th position.
private static final int ISO_TIME_POS = 11;
....
String dateTimeStr = "2022-08-16T06:25:00.000";
String timeStr = dateTimeStr.substring(ISO_TIME_POS, ISO_TIME_POS + 5);
System.out.println(timeStr); // prints "06:25"

How to convert string into valid date format if I provide month and days as optional

I want to convert my string into valid date format even months and days are optional
For Example, I have the following string like below
String date1 = "20-12-2021"
String date2 = "12-2021" i.e without day
String date3 = "2021" i.e without day & month
String date4 = "21" i.e without day, month & just last two digits of the year
I want to convert all the above strings format into the valid date with 'yyyy-mm-dd' format as below,
For date1, Date should be "2021-12-20"
For date2, Date should be "2021-12-01"
For date3, Date should be "2021-01-01"
For date4, Date should be "2021-01-01"
Could you please help me with this? Any info will be really helpful. I can't define single string formatted for parser since the string format is unpredictable in my scenario.
You can use DateTimeFormatterBuilder with multiple patterns and default values of missing parts (month and day):
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("[dd-MM-uuuu]")
.appendPattern("[MM-uuuu]")
.appendPattern("[uuuu]")
.appendPattern("[uu]")
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
.toFormatter();
Outputs
2021-12-20
2021-12-01
2021-01-01
2021-01-01
A whacky yet working solution in your special case would be the following:
public class DateParser {
private static String defaultDate = "01-01-2000";
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
public static LocalDate parseDate(String date) {
String fullDate = defaultDate.substring(0, defaultDate.length() - date.length()) + date;
return LocalDate.parse(fullDate, formatter);
}
}
Basically, use a default full date, e.g. 01-01-2000, and then combine it with the input date. Then, use the formatter dd-MM-yyyy to parse the date. One of the advantages is that it reuses the same formatter, hence is very efficient. However, you may need to add some checks and exception handling.
To test it:
public static void main(String[] args) {
List.of(
"20-12-2021",
"12-2021",
"2021",
"21"
).forEach(date -> System.out.println(parseDate(date)));
}
Output:
2021-12-20
2021-12-01
2021-01-01
2021-01-01
(I must admit #YCF_L solution is more elegant though)

Exception when trying to parse a LocalDateTime

I am using the following timestamp format:
yyyyMMddHHmmssSSS
The following method works fine:
public static String formatTimestamp(final Timestamp timestamp, final String format) {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
return timestamp.toLocalDateTime().format(formatter);
}
And, when I pass in a Timestamp with that format string, it returns, for example:
20170925142051591
I then require to map from that string to a Timestamp again, essentially the reverse operation. I know that I can use a SimpleDateFormat and its parse() method, but I'd prefer to stick to java.time style formatting, if possible.
I wrote this (rather hacky) bit of code, which works with some formats, but not with this particular one:
public static Timestamp getTimestamp(final String text, final String format, final boolean includeTime) {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
final TemporalAccessor temporalAccessor = formatter.parse(text);
if (includeTime) {
final LocalDateTime localDateTime = LocalDateTime.from(temporalAccessor);
return DateTimeUtil.getTimestamp(localDateTime);
} else {
final LocalDate localDate = LocalDate.from(temporalAccessor);
return DateTimeUtil.getTimestamp(localDate);
}
}
It fails on the second line, at the formatter.parse(text); part.
Stack Trace:
java.time.format.DateTimeParseException: Text '20170925142051591' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at java.time.LocalDateTime.parse(LocalDateTime.java:477)
at com.csa.core.DateTimeUtil.main(DateTimeUtil.java:169)
Is there a simpler way to achieve what I want without utilising SimpleDateFormat?
It's a bug: https://bugs.openjdk.java.net/browse/JDK-8031085
The link above also provides the workaround: using a java.time.format.DateTimeFormatterBuilder with a java.time.temporal.ChronoField for the milliseconds field:
String text = "20170925142051591";
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// date/time
.appendPattern("yyyyMMddHHmmss")
// milliseconds
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
// create formatter
.toFormatter();
// now it works
formatter.parse(text);
Unfortunately, there seems to be no way to parse this using only DateTimeFormatter.ofPattern(String).

trying to parse/formate date with timezone using joda-time/java

I am trying to parse the date to look like 03-23-2015 21:16:00 GMT+05:00 using joda-time but i am not able to achieve it, however it is working fine with SimpleDateFormat but for some reason i want to use Joda-Time (see my question on SO.)
Please note that i don't want to hardcode timezone to GMT+05:00 but i want to set the user's default timezone.
I am trying it as:
public class Consts{
public static final DateTimeFormatter DATE_FORMATTER_2 = DateTimeFormat
.forPattern("MM-dd-yyyy HH:mm:ss z");
public static final DateTimeFormatter DATE_FORMATTER_TEMP_1 = DateTimeFormat
.forPattern("MM-dd-yyyy HH:mm:ss Z");
}
And then i am using these formatters as:
cDate = new LocalDateTime(DateTimeZone.getDefault());
sDate = new LocalDateTime(DateTimeZone.getDefault());
eDate = new LocalDateTime(DateTimeZone.getDefault());
if (mStartTimeTV.getText().toString().equals("Now")) {
sDate = cDate;
} else {
sDate = Consts.DATE_FORMATTER_WITHOUT_TIME_ZONE
.parseLocalDateTime(mStartTimeTV.getText().toString());
}
if (!mEndTimeTV.getText().toString().equals("")) {
eDate = Consts.DATE_FORMATTER_WITHOUT_TIME_ZONE
.parseLocalDateTime(mEndTimeTV.getText().toString());
} else {
eDate = sDate;
}
And while sending the dates to the server i am formatting them as:
String s0 = Consts.DATE_FORMATTER_2.print(sDate);
String s = Consts.DATE_FORMATTER_2.withZone(
DateTimeZone.getDefault()).print(sDate);
String s1 = Consts.DATE_FORMATTER_TEMP_1.print(sDate);
But the output is always: 03-24-2015 16:07:23
I have also tried with ZZZZ but no luck.
LocalDateTime doesn't have a time zone, so there is nothing to format. You should use DateTime instead, which you can obtain using LocalDateTime.toDateTime(timeZone).

Displaying AM and PM in lower case after date formatting

After formatting a datetime, the time displays AM or PM in upper case, but I want it in lower case like am or pm.
This is my code:
public class Timeis {
public static void main(String s[]) {
long ts = 1022895271767L;
String st = null;
st = new SimpleDateFormat(" MMM d 'at' hh:mm a").format(ts);
System.out.println("time is " + ts);
}
}
This works
public class Timeis {
public static void main(String s[]) {
long ts = 1022895271767L;
SimpleDateFormat sdf = new SimpleDateFormat(" MMM d 'at' hh:mm a");
// CREATE DateFormatSymbols WITH ALL SYMBOLS FROM (DEFAULT) Locale
DateFormatSymbols symbols = new DateFormatSymbols(Locale.getDefault());
// OVERRIDE SOME symbols WHILE RETAINING OTHERS
symbols.setAmPmStrings(new String[] { "am", "pm" });
sdf.setDateFormatSymbols(symbols);
String st = sdf.format(ts);
System.out.println("time is " + st);
}
}
Unfortunately the standard formatting methods don't let you do that. Nor does Joda. I think you're going to have to process your formatted date by a simple post-format replace.
String str = oldstr.replace("AM", "am").replace("PM","pm");
You could use the replaceAll() method that uses regepxs, but I think the above is perhaps sufficient. I'm not doing a blanket toLowerCase() since that could screw up formatting if you change the format string in the future to contain (say) month names or similar.
EDIT: James Jithin's solution looks a lot better, and the proper way to do this (as noted in the comments)
If you don't want to do string substitution, and are using Java 8 javax.time:
Map<Long, String> ampm = new HashMap<>();
ampm.put(0l, "am");
ampm.put(1l, "pm");
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.appendPattern("E M/d h:mm")
.appendText(ChronoField.AMPM_OF_DAY, ampm)
.toFormatter()
.withZone(ZoneId.of("America/Los_Angeles"));
It's necessary to manually build a DateTimeFormatter (specifying individual pieces), as there is no pattern symbol for lowercase am/pm. You can use appendPattern before and after.
I believe there is no way to substitute the default am/pm symbols, making this is the only way short of doing the string replace on the final string.
Calendar c = Calendar.getInstance();
System.out.println("Current time => " + c.getTime());
SimpleDateFormat df = new SimpleDateFormat("HH:mm a");
String formattedDate = df.format(c.getTime());
formattedDate = formattedDate.replace("a.m.", "AM").replace("p.m.","PM");
TextView textView = findViewById(R.id.textView);
textView.setText(formattedDate);
Try this:
System.out.println("time is " + ts.toLowerCase());
Although you may be able to create a custom format as detailed here and here
Unfortunately out of the box the AM and PM do not seem to be customisable in the standard SimpleDateFormat class
James's answer is great if you want different style other than default am, pm. But I'm afraid you need mapping between Locale and Locale specific AM/PM set to adopting the override. Now you simply use java built-in java.util.Formatter class. So an easy example looks like this:
System.out.println(String.format(Locale.UK, "%1$tl%1$tp", LocalTime.now()));
It gives:
9pm
To note that if you want upper case, just replace "%1$tp" with "%1$Tp". You can find more details at http://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#dt.
String today = now.format(new DateTimeFormatterBuilder()
.appendPattern("MM/dd/yyyy ")
.appendText(ChronoField.AMPM_OF_DAY)
.appendLiteral(" (PST)")
.toFormatter(Locale.UK));
// output => 06/18/2019 am (PST)
Locale.UK => am or pm;
Locale.US => AM or PM; try different locale for your needs (defaul, etc.)
new SimpleDateFormat("MMM d 'at' hh:mm a", new Locale("en", "IN"));
change to a locale that uses am/pm instead of AM/PM.
just add toLowarCase() like this
public class Timeis {
public static void main(String s[]) {
long ts = 1022895271767L;
String st = null;
st = new SimpleDateFormat(" MMM d 'at' hh:mm a").format(ts).toLowerCase();
System.out.println("time is " + ts);
}
}
and toUpperCase() if you want upper case
This is how we perform in android studio's java code pass unix timestamp as parameter
private String convertTimeToTimeStamp(Long timeStamp){
SimpleDateFormat sdf=new SimpleDateFormat("hh:mm aaa", Locale.getDefault());
return sdf.format(timeStamp);
}
function returns time of format 09:30 PM

Categories

Resources