如何在Java自动化机器人中正确获取上月最后一天
问题:自动化机器人切换月份后无法正确获取上月最后一天
我开发的自动化机器人需要获取上月最后一天,当前月份首次运行时结果正常,但进入下一个月后,现有代码获取的日期出现错误。
问题代码片段:
Calendar cal = Calendar.getInstance(); cal.add(Calendar.MONTH, -1); cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); dateformat2 = new SimpleDateFormat((cal.get(Calendar.MONTH)+1)+"/"+cal.get(Calendar.DAY_OF_MONTH)+"/yyyy");
完整的Teco_Bot类代码:
import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; public class Teco_Bot extends Thread{ WebDriver driver; GUI gui; String user[]= {"NorthVillage432","VPM1263"}; String pass[]= {"user123","user123"}; String accId[]= {"0","1"}; SimpleDateFormat dateformat1,dateformatDay,dateformat2,dateformat3; Statement st; ResultSet rs; public Teco_Bot() { dateformat1 = new SimpleDateFormat("yyyy-MM-dd"); dateformatDay = new SimpleDateFormat("dd"); dateformat3 = new SimpleDateFormat("MM/dd/yyyy"); Calendar cal = Calendar.getInstance(); cal.add(Calendar.MONTH, -1); cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); dateformat2 = new SimpleDateFormat((cal.get(Calendar.MONTH)+1)+"/"+cal.get(Calendar.DAY_OF_MONTH)+"/yyyy"); gui = new GUI(); } public void run() { String currentdate=""; EmailNotification notif1; EmailNotification notif2; while(true) { try { Thread.sleep(2000); }catch(Exception ee) {ee.printStackTrace();} System.out.println("Waiting"); if((dateformatDay.format(new Date()).equalsIgnoreCase("05")||dateformatDay.format(new Date()).equalsIgnoreCase("15"))&&!currentdate.equalsIgnoreCase(dateformat1.format(new Date()))) { notif1 = new EmailNotification("junrybuenavista@yahoo.com",30,"Teco Bot"); notif1.start(); notif2 = new EmailNotification("michaelvinocur@htgrp.net",30,"Teco Bot"); notif2.start(); setDataBaseConnection(); while(true) { for(int i=0;i<user.length;i++) { setBrowser(); gui.textAppend("Processing "+user[i]+"\n"); Login(user[i],pass[i]); currentdate = dateformat1.format(new Date()); try { st.execute("DELETE FROM `teco_data`"); }catch(Exception ee) {ee.printStackTrace();} getData(); if(driver.findElements(By.xpath("//*[@id=\"selectionForm\"]/div[3]/div[2]/div[2]/ul/li[2]/a")).size() != 0) { driver.findElement(By.xpath("//*[@id=\"selectionForm\"]/div[3]/div[2]/div[2]/ul/li[2]/a")).click(); System.out.println(); getData(); } driver.get("http://localhost/googleapi/teco-query.php?day="+dateformatDay.format(new Date())+"&acc="+accId[i]+"&date="+dateformat2.format(new Date())+"&date2="+dateformat3.format(new Date())); System.out.println("http://localhost/googleapi/teco-query.php?day="+dateformatDay.format(new Date())+"&acc="+accId[i]+"&date="+dateformat2.format(new Date())+"&date2="+dateformat3.format(new Date())); try { Thread.sleep(4000); }catch(Exception ee) {ee.printStackTrace();} driver.quit(); try { Thread.sleep(2000); }catch(Exception ee) {ee.printStackTrace();} gui.textAppend("Processing complete\n\n"); } try { st.close(); rs.close(); }catch(Exception ee) {ee.printStackTrace();} break; } notif1.setIsRunning(true); notif2.setIsRunning(true); } } } public void getData() { try { WebElement table = driver.findElement(By.xpath("//*[@id=\"selectionForm\"]/div[2]/table")); List<WebElement> acc = table.findElements(By.xpath(".//tr/td[2]")); List<WebElement> add = table.findElements(By.xpath(".//tr/td[3]")); List<WebElement> amount_due = table.findElements(By.xpath(".//tr/td[4]")); List<WebElement> due_date = table.findElements(By.xpath(".//tr/td[5]")); for(int i=0;i<acc.size();i++) { System.out.print(acc.get(i).getText()+" "); System.out.print(add.get(i).getText()+" "); System.out.print(amount_due.get(i).getText()+" "); System.out.println(due_date.get(i).getText()+" "); st.execute("INSERT INTO teco_data (acc, address, amount_due, due_date)\r\n" + "VALUES ('"+acc.get(i).getText()+"', '"+add.get(i).getText()+"', '"+amount_due.get(i).getText()+"', '"+due_date.get(i).getText()+"');"); } }catch(Exception ee) {ee.printStackTrace();} } public void setDataBaseConnection() { while(true) { System.out.println("Database connecting"); gui.textAppend("Database connecting\n"); try{ Thread.sleep(1500); Class.forName("com.mysql.jdbc.Driver"); Connection con=DriverManager.getConnection( "jdbc:mysql://localhost:3306/teco_db","root",""); st=con.createStatement(); break; }catch(Exception e){} } } public void setBrowser() { System.setProperty("webdriver.chrome.driver", "C:\\Jars\\chromedriver.exe"); HashMap<String,Object> chromePrefs = new HashMap<String, Object>(); chromePrefs.put("plugins.always_open_pdf_externally", true); chromePrefs.put("download.default_directory", "C:"+File.separator+"Square_download"); chromePrefs.put("excludeSwitches", "enable-popup-blocking"); ChromeOptions options = new ChromeOptions(); options.setExperimentalOption("prefs", chromePrefs); driver = new ChromeDriver(options); driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); } public void Login(String user,String pass) { gui.textAppend("Starting Login\n"); driver.get("https://account.tecoenergy.com/"); try { Thread.sleep(2000); }catch(Exception ee) {ee.printStackTrace();} driver.findElement(By.id("UserName")).sendKeys(user); driver.findElement(By.id("Credentials_Password")).sendKeys(pass); driver.findElement(By.id("login-submit")).click(); gui.textAppend("Login complete\n"); try { Thread.sleep(4000); }catch(Exception ee) {ee.printStackTrace();} } class GUI { JTextArea area; GUI(){ area=new JTextArea(); area.setEditable(false); JScrollPane scrollableTextArea = new JScrollPane(area); scrollableTextArea.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { e.getAdjustable().setValue(e.getAdjustable().getMaximum()); } }); JFrame frame=new JFrame("Teco bot Running"); frame.add(scrollableTextArea); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(500,300); frame.setLocationRelativeTo(null); frame.setVisible(true); } public void textAppend(String stringIn) { area.append(stringIn); } public void textClear() { area.setText(""); } } }
问题分析
你的代码存在两个核心错误:
- 日期逻辑初始化时机错误:上月最后一天的计算和
dateformat2的初始化写在构造函数里,只会在机器人启动时执行一次。月份切换后,不会重新计算,始终使用启动时的旧日期值。 SimpleDateFormat用法错误:你把计算出的上月最后一天的具体月份、日期硬编码到了格式化字符串中,这完全违背了日期格式化的逻辑——SimpleDateFormat应该用固定格式(如MM/dd/yyyy)来格式化动态的日期对象,而非把日期值写进格式里。
解决方案
步骤1:重构构造函数,初始化固定格式的格式化器
把dateformat2改为固定格式,不再硬编码日期值:
public Teco_Bot() { dateformat1 = new SimpleDateFormat("yyyy-MM-dd"); dateformatDay = new SimpleDateFormat("dd"); dateformat2 = new SimpleDateFormat("MM/dd/yyyy"); // 固定格式,复用即可 dateformat3 = new SimpleDateFormat("MM/dd/yyyy"); gui = new GUI(); }
步骤2:在任务触发时实时计算上月最后一天
在run方法的任务逻辑中,每次需要使用上月最后一天时,实时计算并格式化:
// 替换原有URL拼接代码 // 实时计算上月最后一天 Calendar lastMonthCal = Calendar.getInstance(); lastMonthCal.add(Calendar.MONTH, -1); lastMonthCal.set(Calendar.DAY_OF_MONTH, lastMonthCal.getActualMaximum(Calendar.DAY_OF_MONTH)); Date lastDayOfLastMonth = lastMonthCal.getTime(); // 格式化日期 String lastDayStr = dateformat2.format(lastDayOfLastMonth); String currentDateStr = dateformat3.format(new Date()); String dayStr = dateformatDay.format(new Date()); // 拼接并访问URL String url = "http://localhost/googleapi/teco-query.php?day=" + dayStr + "&acc=" + accId[i] + "&date=" + lastDayStr + "&date2=" + currentDateStr; driver.get(url); System.out.println(url);
更优方案:使用Java 8+的java.time API(推荐)
Calendar和SimpleDateFormat是旧API,存在线程不安全、设计繁琐等问题。推荐使用Java 8引入的java.time包,代码更简洁安全:
// 导入必要类 import java.time.LocalDate; import java.time.format.DateTimeFormatter; // 实时计算上月最后一天 LocalDate today = LocalDate.now(); LocalDate lastDayOfLastMonth = today.minusMonths(1).withDayOfMonth(today.minusMonths(1).lengthOfMonth()); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy"); String lastDayStr = lastDayOfLastMonth.format(formatter);
额外建议
- 避免在循环中重复创建日期格式化器实例,可作为类成员变量复用。
- Selenium中尽量用显式等待替代
Thread.sleep(),提升代码稳定性。 - 数据库操作存在SQL注入风险,建议使用
PreparedStatement替代直接拼接SQL字符串。
内容的提问来源于stack exchange,提问作者Junry Buenavista
相关产品推荐
相关产品推荐

