Why is 2010/08/15 matching to M/d/yy? - java

I have an array of valid date formats I want to detect in a some text a user enters:
public static final DateFormat[] DATE_FORMATS = {
new SimpleDateFormat("M/d/yy"),
new SimpleDateFormat("M.d.yy"),
new SimpleDateFormat("M-d-yy"),
new SimpleDateFormat("M/d/yyyy"),
new SimpleDateFormat("M.d.yyyy"),
new SimpleDateFormat("M-d-yyyy"),
new SimpleDateFormat("M/dd/yy"),
new SimpleDateFormat("M.dd.yy"),
new SimpleDateFormat("M-dd-yy"),
new SimpleDateFormat("M/dd/yyyy"),
new SimpleDateFormat("M.dd.yyyy"),
new SimpleDateFormat("M-dd-yyyy"),
new SimpleDateFormat("MM/d/yy"),
new SimpleDateFormat("MM.d.yy"),
new SimpleDateFormat("MM-d-yy"),
new SimpleDateFormat("MM/d/yyyy"),
new SimpleDateFormat("MM.d.yyyy"),
new SimpleDateFormat("MM-d-yyyy"),
new SimpleDateFormat("MM/dd/yy"),
new SimpleDateFormat("MM.dd.yy"),
new SimpleDateFormat("MM-dd-yy"),
new SimpleDateFormat("MM/dd/yyyy"),
new SimpleDateFormat("MM.dd.yyyy"),
new SimpleDateFormat("MM-dd-yyyy"),
new SimpleDateFormat("yyyy/MM/dd"),
new SimpleDateFormat("yyyy.MM.dd"),
new SimpleDateFormat("yyyy-MM-dd")
};
Dates are detected through the code below. this.searchTokens is an array of each search term from the user's entered text.
List<Date> datesFound = new ArrayList<Date>();
for (String token : this.searchTokens) {
Date date;
for (DateFormat dateFormat : DateHelper.DATE_FORMATS) {
try {
// Attempt to parse this token as a date.
date = (Date) dateFormat.parse(token);
datesFound.add(date);
break;
} catch (ParseException e) {
continue;
}
}
}
This code validates and adds the correct dates to my List object for any date except dates formatted like so:
yyyy/MM/dd
yyyy.MM.dd
yyyy-MM-dd
In one of my unit tests, the dates 2010/08/15 and 2011/08/15 match to M/d/yy the first time through the loop and become Date objects with the values Jun 8, 2182 and Jul 8, 2182, respectively. Why would the first SimpleDateFormat in DATE_FORMATS accept a match like this? The number of digits don't even match up... Is there a better way I should go about detecting these dates?

Call .setLenient(false) on the SimpleDateFormat object you created.
I think M and MM will still both match 1 or 2 digits though. I think you would have to check that yourself (with a regex) if that's not what you want.

This worked for me. Using .setLenient(false) by itself did not fix the problem.
for(int i=0; i < PropDateFormats.length; i++)
{
try
{
ParsePosition p = new ParsePosition(0);
PropDateFormats[i].setLenient(false);
PropagationDate = PropDateFormats[i].parse(_date,p);
if(p.getIndex() < _date.length())
{
log.trace("setPropagationDate.parse("+_date+") failed. Index=["+i+"[ as"+PropagationDate);
throw new ParseException(_date, p.getIndex());
}
log.trace("setPropagationDate.parse("+_date+") passed. Index=["+i+"[ as"+PropagationDate);
break;
}

Related

Java - Do not delete files that match any of the array values

I have a list of files(approximately 500 or more files) where the filename contains a date.
file_20180810
file_19950101
file_20180809
etc.
What I want to do is delete files which exceed the storage period.
I've come up with the following logic so far
~Get dates of valid storage period (ie. if storage period is 5 days and date today is 20180810, store date values 20180810, 20180809, 20180808, 20180807, 20180806, 20180805 in an array.
~Check every file in a directory if it contains any of the following dates. If it contains date, don't delete, else delete.
My problem here is, if the file name does contain one single date and I use a loop to delete a file, it might delete other files with valid dates as well. To show what I want to do in code form, it goes somehow like this:
if (!fileName.contains(stringDate1) &&
!fileName.contains(stringDate2) &&
!fileName.contains(stringDate3)) //...until storage period
{//delete file}
Is there a better way to express this? Any suggestions for a workaround?
Please and thank you.
Parse dates from your filename. Here's an example:
import java.time.*;
import java.util.regex.*;
public class MyClass {
public static void main(String args[]) {
LocalDate today = LocalDate.now();
long storagePeriod = 5L;
String fileName = "file_20180804";
int year = 0;
int month = 0;
int day = 0;
String pattern = "file_(\\d{4})(\\d{2})(\\d{2})";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(fileName);
if (m.find()) {
year = Integer.parseInt(m.group(1));
month = Integer.parseInt(m.group(2));
day = Integer.parseInt(m.group(3));
}
LocalDate fileDate = LocalDate.of(year, month, day);
if (fileDate.isBefore(today.minusDays(storagePeriod))) {
System.out.println("Delete this file");
}
}
}
You can try using Regex to extract the actual date of each file and check for the inclusion in a validity period.
Pattern p = Pattern.compile("file_(?<date>\d{6})");
foreach(File f : filelist){
Matcher m = p.matcher(f.filename());
if(m.find()){
Date fileDate = new Date(m.group("date"));
if(fileDate.before(periodStartDate)){
file.delete();
}
}
}
The code is not precise and should not compile, check about Date object creation and comparison, but the main idea is pretty much here.
You can only delete Files that are not in the Array like (tested, working):
String path = ""; // <- Folder we want to clean.
DateFormat df = new SimpleDateFormat("yyyyMMdd"); // <- DateFormat to convert the Calendar dates into our format.
Calendar cal = Calendar.getInstance(); // <- Using Calendar to get the days backwards.
ArrayList<String> dr = new ArrayList<String>(); // <- Save the dates we want to remove. dr = don't remove
dr.add(df.format(cal.getTime())); // <- add the actual date to List
for(int i = 0; i < 5; i++) { // <- Loop 5 Times to get the 5 Last Days
cal.add(Calendar.DATE, -1); // <- remove 1 day from actual Calendar date
dr.add(df.format(cal.getTime())); // <- add the day before to List
}
for(File file : new File(path).listFiles()) { // <- loop through all the files in the folder
String filename = file.getName().substring(0, file.getName().lastIndexOf(".")); // <- name of the file without extension
boolean remove = true; // <- Set removing to "yes"
for(String s : dr) { // <- loop through all the allowed dates
if(filename.contains(s)) { // <- when the file contains the allowed date
remove = false; // <- Set removing to "no"
break; // <- Break the loop for better performance
}
}
if(remove) { // <- If remove is "yes"
file.delete(); // <- Delete the file because it's too old for us!
}
}
but this is not the best way! A much better method would be to calculate how old the files are. Because of the _ you can pretty easily get the dates from the filenames. Like (not tested):
String path = ""; // <- Folder we want to clean.
Date today = new Date();
DateFormat df = new SimpleDateFormat("yyyyMMdd"); // <- Dateformat you used in the files
long maxage = 5 * 24 * 60 * 60 * 1000; // <- Calculate how many milliseconds ago we want to delete
for(File file : new File(path).listFiles()) { // <- loop through all the files in the folder
String fds = file.getName().split("_")[1]; // <- Date from the filename as string
try {
Date date = df.parse(fds); // Convert the string to a date
if(date.getTime() - today.getTime() <= maxage) { // <- when the file is older as 5 days
file.delete(); // <- Delete the file
}
} catch (ParseException e) {
e.printStackTrace();
}
}
Here is some example code which demonstrates how a list of input files (file name strings, e.g., "file_20180810") can be verified against a supplied set of date strings (e.g., "20180810") and perform an operation (like delete the file) on them.
import java.util.*;
import java.io.*;
public class FilesTesting {
private static final int DATE_STRING_LENGTH = 8; // length of 20180809
public static void main(String [] args) {
List<String> filter = Arrays.asList("20180810", "20180808", "20180809", "20180807", "20180806", "20180805");
List<File> files = Arrays.asList(new File("file_20180810"), new File("file_19950101"), new File("file_20180809"));
for (File file : files) {
String fileDateStr = getDateStringFromFileName(file.getName());
if (filter.contains(fileDateStr)) {
// Do something with it
// Delete file - if it exists
System.out.println(file.toString());
}
}
}
private static String getDateStringFromFileName(String fileName) {
int fileLen = fileName.length();
int dateStrPos = fileLen - DATE_STRING_LENGTH;
return fileName.substring(dateStrPos);
}
}
If you’re using ES6 you can use array includes and return a true or false to validate.
['a', 'b', 'c'].includes('b')

Counter Invoked in Two ActionListeners

I have a counter x that I want to invoke in two separate ActionListeners. When I try to make x into final, I can't increment using x++;. I tried to make x within the nest, but then I can't use the same value in the other ActionListener. Code is as follows:
buttonIn.addActionListener(new ActionListener() {
String reportDate = "";
int x = 0;
public void actionPerformed(ActionEvent e)
{
DateFormat df = new SimpleDateFormat("MM/dd/yyyy hh:mm aa");
Date time = new GregorianCalendar().getTime();
reportDate = df.format(time);
String confirm = name.getText() + " has checked in at " + reportDate;
timeLabel.setText(confirm);
timeLabel.setVisible(true);
String action = "Time In";
reportData[x][0] = name.getText();
reportData[x][1] = "Time In";
reportData[x][2] = reportDate;
x++;
System.out.println(x);
}
});
buttonOut.addActionListener(new ActionListener() {
String reportDate = "";
public void actionPerformed(ActionEvent e)
{
DateFormat df = new SimpleDateFormat("MM/dd/yyyy hh:mm aa");
Date time = new GregorianCalendar().getTime();
reportDate = df.format(time);
String confirm = name.getText() + " has checked out at " + reportDate;
timeLabel.setText(confirm);
timeLabel.setVisible(true);
reportData[x][0] = name.getText();
reportData[x][1] = "Time Out";
reportData[x][2] = reportDate;
x++;
}
});
One simple option is to use AtomicInteger instead - then the variable can be final, but you can still increment the wrapped value. So:
final AtomicInteger counter = new AtomicInteger(0);
buttonIn.addActionListener(new ActionListener() {
// Within here, you can use counter.get and counter.incrementAndGet
});
buttonOut.addActionListener(new ActionListener() {
// Within here, you can use counter.get and counter.incrementAndGet
});
I'd also strongly consider extracting that code into a separate class though - almost all the code is the same, so you should be able to remove the duplication by parameterizing the differences. So you'd end up with something like:
AtomicInteger counter = new AtomicInteger(0);
buttonIn.addActionListener(new ReportListener(
counter, reportData, "%s has checked in at %s", "Time In"));
buttonOut.addActionListener(new ReportListener(
counter, reportData, "%s has checked out at %s", "Time Out"));
(Where ReportListener is the new class implementing ActionListener.)
Additionally:
I strongly suspect you want to use HH rather than hh in your SimpleDateFormat
Consider which time zone and locale you want to use in your SimpleDateFormat, and specify them explicitly for clarity
To get the current time, just call new Date() rather than creating a calendar and extracting the date from it
There's no obvious reason for having reportDate as an instance variable
For testability, I'd encourage you to use some sort of Clock interface, with an implementation provided by dependency injection, so you can fake time appropriately
Consider using Joda Time for all date/time work; it's much cleaner than the built-in date/time API

How do I search for a range of values in between two Strings in Java

First I know this is bad way to compare dates but I don't understand how to evaluate a strings to include the data between 2 values. For example in this code how would I list not only transactions that occurred on tdate 1 and 2 but also the transactions that fell between them.
tdate information is set up like 11/07/2013
System.out.println("Start Date: ");
tdate = in.next();
System.out.println("End Date: ");
tdate2 = in.next();
transactionSet trr = new transactionSet();
for(int idx = 0; idx < ts.getNumTrans(); idx++){
t = ts.retrieve(idx);
if (t.getTdate().equals(tdate) || t.getTdate().equals(tdate2)){
trr.insert(t);
}
}
SimpleDateFormat format = new SimpleDateFormat("d/M/yyyy"); // If this is the right format
Date first = format.parse(tdate);
Date second = format.parse(tdate2);
Date toCheck = format.parse(someDate);
if (toCheck.after(first) && toCheck.before(second)){
// ...
}
You might want to do this in a try/catch block, in case the dates that are input are not formatted correctly.

Trying to get the arraylist value inside hashmap key

I'm probably being stupid here...but I need help with this one! Basically i need to do a .contains("message") to determine if the key already contains the incoming message.
Thanks in advance!
EDIT: Just as a note, i do not want it to do anything if it already exists! Currently its not adding it to the list.
EDIT2: the date will not matter for the incoming message because the incoming message does not have the date portion.
private Map<Integer,List<String>> map = new HashMap<Integer,List<String>>();
public synchronized void addToProblemList(String incomingMessage, int storeNumber){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy h:mm:ss a");
String formattedDate = sdf.format(date);
if(map.get(storeNumber)==null){
map.put(storeNumber, new ArrayList<String>());
}
for(String lookForText : map.get(storeNumber)){
if(lookForText.contains(incomingMessage)){
}else if(!lookForText.contains(incomingMessage)){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}
}
}
It used to look like this, but it always added it:
public synchronized void addToProblemList(String incomingMessage, int storeNumber){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy h:mm:ss a");
String formattedDate = sdf.format(date);
if(map.get(storeNumber)==null){
map.put(storeNumber, new ArrayList<String>());
}
if(map.get(storeNumber).contains(incomingMessage)==true){
//Do nothing
}
if (map.get(storeNumber).contains(incomingMessage)==false){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}
What you are adding to the list is a key of the store number and an empty array list,
So the first message for the store you add to the list is empty, therefore your for loop will not execute as it has no elements to iterate.
So add this
if(map.get(storeNumber)==null){
ArrayList<String> aList = new ArrayList<String>();
aList.add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
map.put(storeNumber, aList);
}
Note map.get(storeNumber).contains(incomingMessage)==true you dont need to boolean comparison here as contains() returns a boolean.
The reason this original approach of yours wouldn't have worked is doing a List.contains() means you are doing an check to see if the list contains an exact matching string which it would not have since when you have added the String it also contained "\nTime of incident: "+formattedDate+"\n... which I suspect would not have matched just incomingMessage
You have this:
for(String lookForText : map.get(storeNumber)){
if(lookForText.contains(incomingMessage)){
}else if(!lookForText.contains(incomingMessage)){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}
}
Try this instead:
List<String> messages = map.get(storeNumber);
if(!messages.contains(incomingMessage)){
map.get(storeNumber).add(incomingMessage+"\nTime of incident: "+formattedDate+"\n--------------------------------------------------------");
}

Check date format before parsing

I am parsing several documments with the field Duration. But in the differents files, it is in differnt formats, ex:
"Duration": "00:43"
"Duration": "113.046"
"Duration": "21.55 s"
I want to parse all of them to the format "Duration": "113.046", how could I check before any parsing in wich format it is??
Some conditions before this piece of code, because this is not right for all of them:
Long duration;
DateFormat sdf = new SimpleDateFormat("hh:mm:ss");
try {
Date durationD = sdf.parse(totalDuration);
Date zeroSec = sdf.parse("00:00:00");
duration = durationD.getTime() - zeroSec.getTime();
} catch (Exception e) {
duration = Long.parseLong(totalDuration);
}
Thanks in advance
You could match the pattern with help of regex and then format accordingly. Here's a kickoff example:
Map<Pattern, DateFormat> dateFormatPatterns = new HashMap<Pattern, DateFormat>();
dateFormatPatterns.put(Pattern.compile("\\d{1,2}:\\d{2}"), new SimpleDateFormat("H:m"));
dateFormatPatterns.put(Pattern.compile("\\d{1,3}\\.\\d{3}"), new SimpleDateFormat("s.S"));
dateFormatPatterns.put(Pattern.compile("\\d{1,2}\\.\\d{2} s"), new SimpleDateFormat("s.S 's'"));
String[] strings = { "00:43", "113.046", "21.55 s" };
DateFormat finalFormat = new SimpleDateFormat("HH:mm:ss");
for (String string : strings) {
for (Pattern pattern : dateFormatPatterns.keySet()) {
if (pattern.matcher(string).matches()) {
Date date = dateFormatPatterns.get(pattern).parse(string);
String formattedTime = finalFormat.format(date);
System.out.println(formattedTime);
break;
}
}
}
This yields here
00:43:00
00:01:53
00:00:21
If these are all your known input formats, then convert your input to your expected date format.
Just string-replace all : with . and remove s.
Do not forget to strip the spaces, too. By the way, "113.046" seems a bit odd date format to me - if I were in your shoes, I would have used some of the standard date time formats and convert the irregular ones.
My solution, not smart at all:
long DurationFixer(String duration){
long durationLong = 0;
if(duration.contains(":")){
DateFormat sdf = new SimpleDateFormat("mm:ss");
try {
Date durationD = sdf.parse(duration);
Date zeroSec = sdf.parse("00:00:00");
durationLong = durationD.getTime() - zeroSec.getTime();
} catch (Exception e) {
durationLong = (Long.parseLong(duration))/1000;
}
}
else{
String r = "";
if(duration.contains("s")){
for (int i = 0; i < duration.length()-2; i ++) {
if ((duration.charAt(i) == '.'))
break;
else
r += duration.charAt(i);
}
}
durationLong = Long.valueOf(r);
}
return durationLong;
}
If someone could find a better solution, please, tell me.
Thanks everybody!

Categories