Regex master needed!
I have an variable timestamp coming from the server and I need to find which format is used everytime. I've tried implementing a regex formats but they don't work. I'm fairly new to regex patterns but still I've tried working them up my self or else look for a specific example but couldn't find so I'm asking you.
The formats from the server can look like this:
"2015-02-23 15:27:31 UTC"
or
"2015-01-22T19:38:40Z"
here is the code to find the formats:
private static String getFormat(String time) {
String firstRegEx = "^\\d{4}-\\d{2}-\\d{2}\'T+\'\\d{2}:\\d{2}:\\d{2}\'Z\'$";
String secondRegEx = "^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\w{3}$";
if (time.toLowerCase().matches(firstRegEx)) {
return firstRegEx;
} else if (time.toLowerCase().matches(secondRegEx)) {
return secondRegEx;
}
return null;
}
Can you look at my regex patterns and tell me what am I doing wrong?
First you have to remove the single quotes arround the char Tand Zand second you call toLowercase() wich will canvert T to t and Z to z. remove it:
private static String getFormat(String time) {
String firstRegEx = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$";
String secondRegEx = "^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\w{3}$";
if (time.matches(firstRegEx)) {
return firstRegEx;
} else if (time.toLowerCase().matches(secondRegEx)) {
return secondRegEx;
}
return null;
}
^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$
Your first regex should be simply this.This will match 2015-01-22T19:38:40Z.See demo.
https://regex101.com/r/aI4rA5/4
Your second regex works fine.
I believe this is the alternative solution suggested in the comments...
public static void main(String[] args) {
System.out.println(getFormat("2015-02-23 15:27:31 UTC"));
System.out.println(getFormat("2015-01-22T19:38:40Z"));
}
private static DateFormat getFormat(String time) {
DateFormat format1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
DateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
if (isFormat(format1, time)) {
return format1;
} else if (isFormat(format2, time)) {
return format2;
} else {
return null;
}
}
private static boolean isFormat(DateFormat format, String candidate) {
return format.parse(candidate, new ParsePosition(0)) != null;
}
If you were using the regex to decide how to parse later on you could bundle this into a single method capable of consuming multiple formats...
public static void main(String[] args) {
System.out.println(getDate("2015-02-23 15:27:31 UTC"));
System.out.println(getDate("2015-01-22T19:38:40Z"));
}
private static Date getDate(String time) {
DateFormat[] formats = { new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z") };
Date date = null;
for (DateFormat format : formats) {
if ((date = format.parse(time, new ParsePosition(0))) != null) {
break;
}
}
return date;
}
If these are the only possible formats then it is enough to test if date.charAt(10) == 'T'
Related
I have specific text file looking like this:
name: meeting_name1
description:
04/18/2012 00:00:00
05/18/2012 00:00:00
... (more dates)
07/18/2012 00:00:00
name: meeting_name2
description: some_desc
04/18/2012 00:00:00
05/18/2012 00:00:00
... (more dates)
07/18/2012 00:00:00
(etc)
I have java object looking like this:
class Meeting {
String name;
String description;
List<Date> dates;
}
My point is to read the file, parse values, create objects and save them to database.
I can read the file line by line and convert it to List<String>, ie. all data together.
`I can make and fill one java object with values and save it to database.
My issue here is how to find out that I'm at the end of dates and lines (name: meeting_name2) of new object begin.
So I could make something like List<List<String>> where List<String> would be equal to one object, ie. List<Meeting>?
Not sure if its understandable, sorry for formatting.
Assumption that you could read the file data to List variable. (See above answer)
List<String> lines = Files.readAllLines(Paths.get("FILE_NAME"));
Now, you can see below code as a demo. It is the simple loop and if else statament.
Hope it will help you.
public static void main(String[] args) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
List<String> data = new ArrayList<>();
data.add("name: meeting_name1");
data.add("description: some_desc");
data.add("07/18/2012 00:00:00");
data.add("07/18/2012 00:00:00");
data.add("name: meeting_name2");
data.add("description: some_desc");
data.add("07/18/2012 00:00:00");
List<Meeting> result = new ArrayList<>();
Meeting temp = null;
for (String line : data) {
if (line.startsWith("name:")) {
temp = new Meeting(line.split(":")[1].trim());
result.add(temp);
} else if (line.startsWith("description:")) {
temp.setDescription(line.split(":")[1].trim());
} else {
temp.getDates().add(simpleDateFormat.parse(line)); // Use date for
}
}
System.out.println(result.get(0).getName() + ": " + result.get(0).getDates().size()); // meeting_name1: 2
System.out.println(result.get(1).getName() + ": " + result.get(1).getDates().size()); // meeting_name2: 1
}
static class Meeting {
String name;
String description;
List<Date> dates;
public String getName() {
return name;
}
public List<Date> getDates() {
return dates;
}
Meeting(String name) {
this.name = name;
this.dates = new ArrayList<>();
}
public void setDescription(String description) {
this.description = description;
}
}
One possibility would be to read all lines first. You would not need to worry about the end of lines with:
List<String> lines = Files.readAllLines(Paths.get("FILE_NAME"));
then iterarate through the array,
if a line starts with "name:" you make a new object and add the data like that:
List<Meeting> meetings = new ArrayList();
Meeting currentMeeting;
for (String line : lines) {
if(line.startsWith("name:"))
{
currentMeeting = new Meeting();
meetings.add(currentMeeting);
//...add data (name)
}
//...add more data (description and dates)
}
I have the below java program in which you specify the parameter in which format you want to convert date that is whether you want to convert it in US or UK format.
Now the problem is that when i want date to be formatted as per US format it does not do that , request you to please advise what is missing in this format ,below is program and output which is there when i try to convert it in US format
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.apache.commons.lang.time.DateUtils;
public class DateFormattingTest {
public static void main(String[] args) throws ParseException {
System.out.println ("01-Mar-2016-->" + extractDate("01-Mar-2016", "US") );
}
public static java.util.Date extractDate(String dateStr, String dateType) {
java.util.Date date = null;
if (!dateStr.matches("\\d{4}-\\d{2}-\\d{2}")){
String[] datePatternsOfUk = { "d-M-yy", "d-M-yyyy", "d/M/yy", "d/M/yyyy", "yyyy-MM-dd","dd-MM-yy", "dd-MMM-yy","dd-MMM-yyyy","dd-MM-yyyy",
"dd/MM/yy","dd/MMM/yy","dd/MMM/yyyy"};
String[] datePatternsOfUs = { "M-d-yy","MM-dd-yy","M/d/yy","MM/dd/yy", "MM/dd/yy", "MMM-dd-yy",
"MMM/dd/yy", "MMM-dd-yyyy", "MM-dd-yyyy", "MMM/dd/yyyy",
"MM/dd/yyyy" };
String[] datePatterns = datePatternsOfUk;
try {
if (dateType.equals("US")) {
datePatterns = datePatternsOfUs;
} else if (dateType.equals("UK")) {
datePatterns = datePatternsOfUk;
}
int p = dateStr.lastIndexOf("/");
if (p == -1) {
p = dateStr.lastIndexOf("-");
}
String firstSubstring = dateStr.substring(0, p + 1);
String secondSubstring = dateStr.substring(p + 1);
if (p != -1 && secondSubstring.length() <= 2) {
secondSubstring = Integer.toString(2000 + Integer.parseInt(secondSubstring));
dateStr = firstSubstring + secondSubstring;
}
date = DateUtils.parseDate(dateStr, datePatterns);
} catch (ParseException e) {
}
return date;
}
if (dateStr.matches("\\d{4}-\\d{2}-\\d{2}")){
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String convertedCurrentDate =sdf.format(sdf.parse(dateStr));
date=sdf.parse(convertedCurrentDate);
} catch (ParseException e) {
}
return date;
}
return null;
}
}
The output that i am getting on US format is shown below..
01-Mar-2016-->null
Actually in US format i am expecting the date to be display as
01-Mar-2016--> 3-Jan-2016
folks please advise for this is my expected result is not correct one as per US format
01-Mar-2016 isn't in a format you have defined for US. Add dd-MMM-yyyy to datePatternsOfUs like
String[] datePatternsOfUs = { "M-d-yy","MM-dd-yy","M/d/yy","MM/dd/yy",
"MM/dd/yy", "MMM-dd-yy", "MMM/dd/yy", "MMM-dd-yyyy",
"MM-dd-yyyy", "MMM/dd/yyyy", "MM/dd/yyyy", "dd-MMM-yyyy" };
I am parsing JSON from server in my Android application by using Jackson JSON library. However, parsing requests fail whenever I receive DateTime since it's in this format:
"/Date(1277931782420)/"
I know I should do something like:
ObjectMapper om = new ObjectMapper();
om.setDateFormat(new TicksSinceFormat());
But I have no idea if I can use SimpleDateFormat at all (and what format string would I use?) or I need to write my own DateFormat parser. So, I would seriously appreciate if somebody could help with code example.
EDIT:
OK, see my answer for complete code.
This proved to be tougher then I expected:
public class TicksSinceFormat extends DateFormat {
#Override
public StringBuffer format(Date date, StringBuffer buffer, FieldPosition field) {
long millis = date.getTime();
return new StringBuffer("/Date(" + millis + ")/");
}
#Override
public Date parse(String string, ParsePosition position) {
int start = string.indexOf("(") + 1;
int end = string.indexOf(")");
String ms = string.substring(start, end);
Date date = new Date(Long.parseLong(ms));
position.setIndex(string.length() - 1); // MUST SET THIS
return date;
}
#Override
public Object clone() {
return new TicksSinceFormat(); // MUST SET THIS
}
}
Using class is then extremely simple, just do:
ObjectMapper om = new ObjectMapper();
om.setDateFormat(new TicksSinceFormat())
I presume that this can be coded better + that I'll need to deal with differences when it comes to .NET Ticks VS Java ticks - but for now this'll do. If somebody has better solution or more insight into mentioned problems I'll deal with later - feel free to post and I'll mark your answer as correct one if it's better.
EDIT: As I've explained in this question & answer I've switched to ServiceStack.Text library on the server and it returns different, ISO8601 format. For that format I'm using slightly different parsing (since Jackson has trouble parsing ISO8601 that contains milliseconds). Of course, as with other code I'm posting - let me know if you have better version (just please post code / edit this post, rather than resorting to philosophical rhetoric on how it should be done):
#SuppressLint("SimpleDateFormat")
public class JacksonSimpleDateFormat extends SimpleDateFormat {
public JacksonSimpleDateFormat() {
if (mParser == null) {
mParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
mParser.setTimeZone(TimeZone.getTimeZone("UTC"));
}
}
#Override
public StringBuffer format(Date date, StringBuffer buffer, FieldPosition field) {
return mParser.format(date, buffer, field);
}
private static SimpleDateFormat mParser;
#Override
public Date parse(String string, ParsePosition position) {
String str = string.split("\\.")[0];
Date date = null;
try {
date = mParser.parse(str);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
position.setIndex(string.length() - 1);
return date;
}
#Override
public Object clone() {
return new JacksonSimpleDateFormat();
}
}
I may be wrong on this, as I haven't gotten very far into Android development, but the format you presented:
"/Date(1277931782420)/"
Appears to be Unix epoch time.
If that is the case, you would not want/need to use SimpleDateFormat. Instead, try creating a Long from it and passing to the Date constructor, accounting for whether it is seconds or milliseconds-based epoch value.
Here is a StackOverflow post that provides the code for doing so: https://stackoverflow.com/a/535017/463196
I want to convert a string into a date, this is simple. But what I'd like to do it without knowing the date format.
Here is a situation: say I have 100 dates and all are in the same format but I'd like to write a Java program to find out this format for me. The result of this program should give me a list of all the possible formats.
For example:
06-06-2006
06-06-2009
...
06-13-2001 <- 99th record
the result of this will give me date format can be mm-dd-yyyy
If the 99th record also was 06-06-2006 the result should be mm-dd-yyyy and dd-mm-yyyy.
Can someone please help me with an example?
Seems sensible to create a set of formats you know about (DATE_FORMATS) and then test each line to see which formats understand every line. You should end up with a set of possibilities.
public class DateFormatDetector {
private static final Set<String> DATE_FORMATS = new HashSet<String>();
static {
DATE_FORMATS.add("yyyy-MM-dd");
DATE_FORMATS.add("dd-MM-yyyy");
DATE_FORMATS.add("MM-dd-yyyy");
}
public static Set<String> getPossibleDateFormats(List<String> dates) {
Set<SimpleDateFormat> candidates = new HashSet<SimpleDateFormat>();
for (String df : DATE_FORMATS) {
SimpleDateFormat candidate = new SimpleDateFormat(df);
candidate.setLenient(false);
candidates.add(candidate);
}
for (String date : dates) {
Iterator<SimpleDateFormat> it = candidates.iterator();
while (it.hasNext()) {
SimpleDateFormat candidate = it.next();
try {
// try to parse the string as a date
candidate.parse(date);
}
catch (ParseException e) {
// failed to parse, so this format is not suitable
it.remove();
}
}
}
Set<String> results = new HashSet<String>();
for (SimpleDateFormat candidate : candidates)
results.add(candidate.toPattern());
return results;
}
}
Try to use SimpleDateFormat prepare all possible formats and calculate parsed result.
The solution could be functional Java as described for example in the stack overflow
java program to accept any format of date as input and print the month,
Is it possible
I tried the following,any other alternative ways/ideas??
import java.text.*;
import java.util.*;
public class PrintMonth3{
public static void main(String args[])throws Exception{
String patterns[]={"dd.MM.yyyy","dd.MM.yy","dd.MMM.yyyy","dd.MMM.yy","d.MM.yyyy"};
String input="4.06.2011";
for(int i=0;i<patterns.length;i++)
doPrintMonth(patterns[i],input);
System.out.println("\nNot a valid date format..");
}
public static void doPrintMonth( String pattern,String input ) {
try{
SimpleDateFormat sdf=new SimpleDateFormat(pattern);
Date output=sdf.parse(input);
String mon[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
int m=output.getMonth();
System.out.println("\n\t" + mon[m] );
System.exit(0);
}
catch(Exception e){}
}
}
No, it's not. How would it distinguish 01/02/2011 (dd/MM/yyyy) and 01/02/2011 (MM/dd/yyyy)?
Within reason, yes. Here's a working example that accepts a variety of formats.
I'm assuming a German / European format like this:
DD. MM. YYYY HH:MM:SS:MMMM
(which means that I can't match any date format where the month comes first)
Here's the class:
public class VariableDateParser {
private static final Pattern DATE_PATTERN = Pattern
.compile("((?:(?:\\d+(?:[./]\\s*)?)+)?)\\s*((?:(?:\\d+[:]?)+)?)");
public Date getDate(final String dateString) {
final Calendar calendar = Calendar.getInstance();
final Matcher matcher = DATE_PATTERN.matcher(dateString);
if (matcher.matches()) {
final String dateGroup = matcher.group(1).trim();
if (!"".equals(dateGroup)) {
final Iterator<Integer> fields = Arrays.asList(
Calendar.DATE, Calendar.MONTH, Calendar.YEAR).iterator();
final String[] items = dateGroup.split("\\D+");
for (final String item : items) {
if ("".equals(item))
break;
else if (fields.hasNext()) {
final Integer field = fields.next();
calendar.set(field, Integer.parseInt(item) -
// months are 0-based, grrrr!!!
(field.equals(Calendar.MONTH) ? 1 : 0));
} else {
throw new IllegalArgumentException(
"Bad date part: " + dateGroup);
}
}
}
final String timeGroup = matcher.group(2).trim();
if (!"".equals(timeGroup)) {
final Iterator<Integer> fields = Arrays.asList(
Calendar.HOUR, Calendar.MINUTE, Calendar.SECOND,
Calendar.MILLISECOND).iterator();
final String[] items = timeGroup.split("\\D+");
for (final String item : items) {
if ("".equals(item))
break;
else if (fields.hasNext()) {
final Integer field = fields.next();
calendar.set(field, Integer.parseInt(item));
} else {
throw new IllegalArgumentException(
"Bad time part: " + timeGroup);
}
}
}
} else
throw new IllegalArgumentException(
"Bad date string: " + dateString);
return calendar.getTime();
}
}
Test Code:
public static void main(final String[] args) {
VariableDateParser parser = new VariableDateParser();
DateFormat df = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.LONG, Locale.GERMAN);
System.out.println(df.format(parser.getDate("11")));
System.out.println(df.format(parser.getDate("11. 10.")));
System.out.println(df.format(parser.getDate("11. 10. 4")));
System.out.println(df.format(parser.getDate("11. 10. 2004")));
System.out.println(df.format(parser.getDate("11. 10. 2004 11")));
System.out.println(df.format(parser.getDate("11. 10. 2004 11:35")));
System.out.println(df.format(parser.getDate("11. 10. 2004 11:35:18")));
System.out.println(df.format(parser.getDate("11. 10. 2004 11:35:18:123")));
System.out.println(df.format(parser.getDate("11:35")));
System.out.println(df.format(parser.getDate("11:35:18")));
System.out.println(df.format(parser.getDate("11:35:18:123")));
}
Output:
11.05.2011 15:57:24 MESZ
11.10.2011 15:57:24 MESZ
11.10.0004 15:57:24 MEZ
11.10.2004 15:57:24 MESZ
11.10.2004 23:57:24 MESZ
11.10.2004 23:35:24 MESZ
11.10.2004 23:35:18 MESZ
11.10.2004 23:35:18 MESZ
01.05.2011 13:35:24 MESZ
01.05.2011 13:35:18 MESZ
01.05.2011 13:35:18 MESZ
Note:
This is a quick proof of concept, not a serious attempt of writing such a class. This will match many invalid formats and ignore many valid ones.
For a wide range of formats, yes it is possible. For any format, no it is not. Consider the simple problem of British vs American dates e.g is 03/04/10 the third of april or the fourth of march?
No, it is not possible.
Proof by counter example: 10/11/12. This is a 'valid' format... but what is the month?
It's possible only if you also tell it what the format is, for instance with the Locale.
Technically its not but what you can do is provide some options to get the user to choose their format. If you are writing this in a GUI then you might want to use radio buttons and put them in a radio group. Otherwise if this is just for use within the compiler (such as a school program) then just use a switch statement like so:
Scanner kbReader = new Scanner(System.in);
String format = kbReader.next();//they might enter mm/dd/yy or any format you want.
switch(format)
{
case "mm/dd/yy": //do coding here
break;
case "dd/mm/yy": //do coding here
break;
}
just like that or you could just use a series of if-else statements because that is basically what a switch statement is.