docs: Translate the Chinese comments in the event module into English.

This commit is contained in:
jaysunxiao committed 2026-05-07 17:03:58 +08:00
1 parent 9dd775abf2
commit d9fd135208
11 files changed
+103 -100

No files matched your search

@@ -63,13 +63,13 @@ public class SchedulerContext implements ApplicationListener<ApplicationContextE
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
// 初始化上下文
// Initialize context
SchedulerContext.instance = this;
instance.applicationContext = event.getApplicationContext();
inject();
// 启动位于SchedulerBus的static静态代码块中
// Startup is performed in the static initializer block of SchedulerBus
} else if (event instanceof ContextClosedEvent) {
// 反射获取executor,关闭掉
// Obtain the executor via reflection and shut it down
shutdown();
}
}
@@ -28,7 +28,7 @@ import java.lang.reflect.Modifier;
public abstract class EnhanceUtils {
static {
// 适配Tomcat,因为Tomcat不是用的默认的类加载器,而Javassist用的是默认的加载器
// Adapt for Tomcat: Tomcat does not use the default class loader, but Javassist relies on it
var classArray = new Class<?>[]{
IScheduler.class
};
@@ -49,29 +49,29 @@ public abstract class EnhanceUtils {
Object bean = reflectScheduler.getBean();
Method method = reflectScheduler.getMethod();
// 定义类名称
// Define the class name
CtClass enhanceClazz = classPool.makeClass(EnhanceUtils.class.getName() + StringUtils.capitalize(NamespaceHandler.SCHEDULER) + UuidUtils.getLocalIntId());
enhanceClazz.addInterface(classPool.get(IScheduler.class.getName()));
// 定义类中的一个成员
// Define a field in the class
CtField field = new CtField(classPool.get(bean.getClass().getName()), "bean", enhanceClazz);
field.setModifiers(Modifier.PRIVATE);
enhanceClazz.addField(field);
// 定义类的构造器
// Define the constructor
CtConstructor constructor = new CtConstructor(classPool.get(new String[]{bean.getClass().getName()}), enhanceClazz);
constructor.setBody("{this.bean=$1;}");
constructor.setModifiers(Modifier.PUBLIC);
enhanceClazz.addConstructor(constructor);
// 定义类实现的接口方法
// Define the interface method implementation
CtMethod invokeMethod = new CtMethod(classPool.get(void.class.getName()), "invoke", null, enhanceClazz);
invokeMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL);
String invokeMethodBody = "{this.bean." + method.getName() + "();}";
invokeMethod.setBody(invokeMethodBody);
enhanceClazz.addMethod(invokeMethod);
// 释放缓存
// Release cached class
enhanceClazz.detach();
Class<?> resultClazz = enhanceClazz.toClass(IScheduler.class);
@@ -17,7 +17,8 @@ import com.zfoo.protocol.util.ReflectionUtils;
import java.lang.reflect.Method;
/**
* 动态代理被Scheduler注解标注的方法,为了避免反射最终会用javassist字节码增强的方法去代理ReflectScheduler
* Dynamic proxy for methods annotated with @Scheduler.
* To avoid reflection overhead, Javassist bytecode enhancement is used to proxy ReflectScheduler.
*
* @author godotg
*/
@@ -23,8 +23,8 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* EN: Trigger timestamp, as long as the current timestamp is greater than this trigger event stamp, it is considered triggerable
* CN: 触发时间戳,只要当前时间戳大于这个触发事件戳都视为可以触发
* Trigger timestamp: the scheduler fires whenever the current timestamp exceeds this value.
*
* @author godotg
*/
public class SchedulerDefinition {
@@ -39,19 +39,19 @@ public abstract class SchedulerBus {
private static final List<SchedulerDefinition> schedulerDefList = new CopyOnWriteArrayList<>();
/**
* scheduler默认只有一个单线程的线程池
* Scheduler uses a single-threaded executor by default
*/
private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new SchedulerThreadFactory(1));
/**
* 上一次trigger触发时间
* Timestamp of the last trigger invocation
*/
private static long lastTriggerTimestamp = 0L;
/**
* 在scheduler中,最小的triggerTimestamp
* The minimum triggerTimestamp among all registered schedulers
*/
private static long minTriggerTimestamp = 0L;
@@ -101,7 +101,8 @@ public abstract class SchedulerBus {
}
/**
* 每一秒执行一次,如果这个任务执行时间过长超过,比如10秒,执行完成后,不会再执行10次
* Executed once per second. If this task takes longer than expected (e.g. 10 seconds),
* it will NOT catch up by running 10 times after completion.
*/
private static void triggerPerSecond() {
var currentTimeMillis = TimeUtils.currentTimeMillis();
@@ -111,7 +112,7 @@ public abstract class SchedulerBus {
}
// 有人向前调整过机器时间,重新计算scheduler里的triggerTimestamp
// Someone moved the system clock backward; recalculate triggerTimestamp for all schedulers
// var diff = timestamp - lastTriggerTimestamp;
if (currentTimeMillis < lastTriggerTimestamp) {
for (SchedulerDefinition schedulerDef : schedulerDefList) {
@@ -121,10 +122,10 @@ public abstract class SchedulerBus {
refreshMinTriggerTimestamp();
}
// diff > 0, 没有人调整时间或者有人向后调整过机器时间,可以忽略,因为向后调整时间时间戳一定会大于triggerTimestamp,所以一定会触发
// diff > 0: no clock adjustment, or clock was moved forward; can be ignored since the timestamp will exceed triggerTimestamp and will always trigger
lastTriggerTimestamp = currentTimeMillis;
// 如果minSchedulerTriggerTimestamp大于timestamp,说明没有可执行的scheduler
// If minTriggerTimestamp is greater than currentTimeMillis, no scheduler is ready to execute
if (currentTimeMillis < minTriggerTimestamp) {
return;
}
@@ -134,7 +135,7 @@ public abstract class SchedulerBus {
for (var scheduler : schedulerDefList) {
var triggerTimestamp = scheduler.getTriggerTimestamp();
if (triggerTimestamp <= currentTimeMillis) {
// 到达触发时间,则执行runnable方法
// Trigger time reached, invoke the scheduler
try {
scheduler.getScheduler().invoke();
} catch (Exception e) {
@@ -142,7 +143,7 @@ public abstract class SchedulerBus {
} catch (Throwable t) {
logger.error("scheduler invoke error", t);
}
// 重新设置下一次的触发时间戳
// Reset the next trigger timestamp
triggerTimestamp = TimeUtils.nextTimestampByCronExpression(scheduler.getCronExpression(), timestampZonedDataTime);
scheduler.setTriggerTimestamp(triggerTimestamp);
}
@@ -160,7 +161,7 @@ public abstract class SchedulerBus {
/**
* 不断执行的周期循环任务
* Periodically recurring task that runs continuously
*/
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, long period, TimeUnit unit) {
@@ -169,7 +170,7 @@ public abstract class SchedulerBus {
/**
* 固定延迟执行的任务
* Task executed with a fixed delay
*/
public static ScheduledFuture<?> schedule(Runnable runnable, long delay, TimeUnit unit) {
@@ -177,7 +178,7 @@ public abstract class SchedulerBus {
}
/**
* cron表达式执行的任务
* Task triggered by a cron expression
*/
public static void scheduleCron(Runnable runnable, String cron) {
if (SchedulerContext.isStop()) {
@@ -188,7 +189,7 @@ public abstract class SchedulerBus {
}
/**
* 立刻执行的任务
* Task executed immediately
*/
public static void execute(Runnable runnable) {
executor.execute(ThreadUtils.safeRunnable(runnable));
@@ -22,14 +22,17 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* SchedulerContext注入到Spring容器中的时机分析:
* 首先运行测试工程:com.zfoo.scheduler.ApplicationTest
* 打断点可以发现,在解析application.xml时,这种自定义的解析是在DefaultBeanDefinitionDocumentReader.java类的parseBeanDefinitions中解析xml的"scheduler元素"时
* 发现是自定义元素(是否是spring的命名空间为依据作为判断),就会调用 parseCustomElement解析自定义标签从而调用到实现BeanDefinitionParser接口的parse方法,
* 从而接着走自己的逻辑在spring容器中注入SchedulerContext这个bean对象。
* Analysis of when SchedulerContext is injected into the Spring container:
* When running the test project (com.zfoo.scheduler.ApplicationTest), breakpoints reveal that
* during the parsing of application.xml, the custom parsing is triggered inside
* DefaultBeanDefinitionDocumentReader#parseBeanDefinitions when the "scheduler" XML element is encountered.
* Since it is recognized as a custom element (determined by whether it belongs to the Spring namespace),
* parseCustomElement is called to handle the custom tag, which in turn invokes the parse() method
* of the BeanDefinitionParser implementation, registering SchedulerContext as a bean in the Spring container.
* <p>
* 从而可以得出结论:在基于zfoo的SpringBoot工程中,由于压根不解析xml对象,注册bean也是通过@Configuration配置类注册bean的,
* 因此这些实现BeanDefinitionParser接口的parse方法是都不会执行的。
* Conclusion: In a SpringBoot project based on zfoo, XML files are not parsed at all;
* beans are registered via @Configuration classes instead.
* Therefore, these BeanDefinitionParser#parse() implementations are never executed.
*
* @author godotg
*/
@@ -43,7 +46,7 @@ public class SchedulerDefinitionParser implements BeanDefinitionParser {
String name;
BeanDefinitionBuilder builder;
// 注册SchedulerContext
// Register SchedulerContext
clazz = SchedulerContext.class;
name = StringUtils.uncapitalize(clazz.getName());
builder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
@@ -27,9 +27,9 @@ public class StopWatch {
}
/**
* 从StopWatch被创建,到调用这个方法消耗的时间
* Elapsed time from when the StopWatch was created until this method is called.
*
* @return 返回消耗的时间,保留两位小数,格式xx.xx
* @return elapsed time in seconds, rounded to two decimal places (format: xx.xx)
*/
public String costSeconds() {
var cost = cost() / (float) TimeUtils.MILLIS_PER_SECOND;
@@ -30,29 +30,29 @@ import java.util.TimeZone;
*/
public abstract class TimeUtils {
// 一秒钟对应的纳秒数
// Nanoseconds per second
public static final long NANO_PER_SECOND = 1_000_000_000;
// 一秒钟对应的毫秒数
// Milliseconds per second
public static final long MILLIS_PER_SECOND = 1 * 1000;
// 一分钟对应的毫秒数
// Milliseconds per minute
public static final long MILLIS_PER_MINUTE = 1 * 60 * MILLIS_PER_SECOND;
// 一个小时对应的毫秒数
// Milliseconds per hour
public static final long MILLIS_PER_HOUR = 1 * 60 * MILLIS_PER_MINUTE;
// 一天对应的毫秒数
// Milliseconds per day
public static final long MILLIS_PER_DAY = 1 * 24 * MILLIS_PER_HOUR;
// 一周对应的毫秒数
// Milliseconds per week
public static final long MILLIS_PER_WEEK = 1 * 7 * MILLIS_PER_DAY;
// 默认的时区
// Default time zone
public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
// 默认的时区Id
// Default zone ID
public static final ZoneId DEFAULT_ZONE_ID = TimeZone.getDefault().toZoneId();
// 统一的时间格式模板
// Standard date-time format template
public static final String DATE_FORMAT_TEMPLATE = "yyyy-MM-dd HH:mm:ss";
// 简单的时间格式模板
// Simple date-time format template
public static final String SIMPLE_DATE_FORMAT_TEMPLATE = "yyyyMMddHHmmss";
// 统一的时间格式模板
// Standard date-only format template
public static final String DATE_FORMAT_TEMPLATE_FOR_DAY = "yyyy-MM-dd";
private static final FastThreadLocalAdapter<SimpleDateFormat> DATE_FORMAT = new FastThreadLocalAdapter<>(() -> new SimpleDateFormat(DATE_FORMAT_TEMPLATE));
@@ -63,7 +63,7 @@ public abstract class TimeUtils {
static {
currentTimeMillis();
// 调用一下静态方法,使SchedulerBus静态代码块初始化
// Call a static method to trigger the static initializer of SchedulerBus
SchedulerBus.refreshMinTriggerTimestamp();
}
@@ -71,7 +71,7 @@ public abstract class TimeUtils {
private static long timestamp = System.currentTimeMillis();
/**
* 获取精确的时间戳
* Get the precise current timestamp in milliseconds
*/
public static long currentTimeMillis() {
timestamp = System.currentTimeMillis();
@@ -79,20 +79,19 @@ public abstract class TimeUtils {
}
/**
* CN:获取最多只有一秒延迟的粗略时间戳,适用于对时间精度要求不高的场景,比System.currentTimeMillis()的性能高10倍
* <p>
* EN:Obtain a coarse timestamp with a delay of up to one second, which is suitable for scenarios that do not require high time accuracy
* Obtain a coarse timestamp with a delay of up to one second, which is suitable for scenarios that
* do not require high time accuracy. Its performance is 10x faster than System.currentTimeMillis().
*/
public static long now() {
return timestamp;
}
// --------------------------------------日期格式--------------------------------------
// -------------------------------------- Date Format --------------------------------------
/**
* 把日期字符串转换成Date对象,统一的日期模板要遵循DATE_FORMAT_TEMPLATE="yyyy-MM-dd HH:mm:ss"
* Convert a date string to a Date object. The standard date template follows DATE_FORMAT_TEMPLATE="yyyy-MM-dd HH:mm:ss"
*
* @param dateString 日期字符串,如:2018-02-12 10:12:50
* @param dateString date string, e.g. 2018-02-12 10:12:50
* @return <code>Date</code>
*/
public static Date stringToDate(String dateString) {
@@ -104,9 +103,9 @@ public abstract class TimeUtils {
}
/**
* 把long类型的时间戳转换成字符串格式的时间
* Convert a long timestamp to a formatted date-time string
*
* @param time 时间戳
* @param time timestamp in milliseconds
* @return yyyy-MM-dd HH:mm:ss
*/
public static String timeToString(long time) {
@@ -128,7 +127,7 @@ public abstract class TimeUtils {
throw new RuntimeException(e);
}
}
// --------------------------------------日期判断--------------------------------------
// -------------------------------------- Date Comparison --------------------------------------
/**
* <p>Checks if two date objects are on the same day ignoring time.</p>
@@ -191,10 +190,9 @@ public abstract class TimeUtils {
}
/**
* 判断两个日期是否是同一周,设置周一为一周的第一天
* <p>
* 2004-12-25”是星期六,也就是说它是2004年中第52周的星期六,那么“2004-12-26”到底是2004年的第几周哪,java中经测试取得的它的Week值是1,
* 那么也就是说它被看作2005年的第一周了,这个处理是比较好的。可以用来判断“2004-12-26”和“2005-1-1”是同一周。
* Check whether two timestamps fall in the same week, with Monday as the first day of the week.
* For example, "2004-12-25" is Saturday (week 52 of 2004), while "2004-12-26" has a WEEK_OF_YEAR
* value of 1 in Java (treated as week 1 of 2005), so "2004-12-26" and "2005-01-01" are considered the same week.
*/
public static boolean isSameWeek(long time1, long time2) {
Calendar cal1 = Calendar.getInstance();
@@ -206,13 +204,13 @@ public abstract class TimeUtils {
int yearDiff = cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR);
if (yearDiff == 0 && cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR)) {
// yearDiff==0,说明是同一年
// yearDiff==0 means they are in the same year
return cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR);
} else if (yearDiff == 1 && cal2.get(Calendar.MONTH) == 11) {
//yearDiff==1,说明cal比cal2大一年;java的一月用"0"标识,那么12月用"11"
// yearDiff==1 means cal1 is one year ahead of cal2; January is represented as "0" in Java, December as "11"
return cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR);
} else if (yearDiff == -1 && cal1.get(Calendar.MONTH) == 11) {
//yearDiff==-1,说明cal比cal2小一年
// yearDiff==-1 means cal1 is one year behind cal2
return cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR);
}
return false;
@@ -227,11 +225,11 @@ public abstract class TimeUtils {
&& cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
}
// --------------------------------------获取相关时间戳--------------------------------------
// -------------------------------------- Retrieve Related Timestamps --------------------------------------
/**
* 获取给定时间戳对应的日期的0点时间戳
* Get the timestamp of midnight (00:00:00) for the given timestamp's date
*/
public static long getZeroTimeOfDay(long time) {
Calendar cal = Calendar.getInstance();
@@ -245,7 +243,7 @@ public abstract class TimeUtils {
/**
* 获取给定时间戳对应的日期的最后时刻时间戳
* Get the timestamp of the last moment (23:59:59.999) for the given timestamp's date
*/
public static long getLastTimeOfDay(long time) {
Calendar cal = Calendar.getInstance();
@@ -257,7 +255,7 @@ public abstract class TimeUtils {
return cal.getTimeInMillis();
}
// 获取给定时间戳对应的日期的昨天这个时刻的时间戳
// Get the timestamp of yesterday at the same time as the given timestamp
public static long getYesterdayTimeOfDay(long time) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
@@ -265,35 +263,35 @@ public abstract class TimeUtils {
return cal.getTimeInMillis();
}
// 获取给定时间戳对应小时的起始时间戳
// Get the start timestamp of the hour corresponding to the given timestamp
public static long getStartTimeOfHour(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var monday = localDateTime.withMinute(0).withSecond(0).withNano(0);
return TimeUtils.localDateTimeToTimestamp(monday);
}
// 获取给定时间戳对应周的第一天的起始时间戳
// Get the start timestamp of the first day of the week corresponding to the given timestamp
public static long getStartTimeOfWeek(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var monday = localDateTime.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY)).plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
return TimeUtils.localDateTimeToTimestamp(monday);
}
// 获取给定时间戳对应月的第一天的起始时间戳
// Get the start timestamp of the first day of the month corresponding to the given timestamp
public static long getStartTimeOfMonth(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var oneOfMonth = localDateTime.with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0).withNano(0);
return TimeUtils.localDateTimeToTimestamp(oneOfMonth);
}
// 获取给定时间戳对应月的最后一天的结束时间戳
// Get the end timestamp of the last day of the month corresponding to the given timestamp
public static long getEndTimeOfMonth(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var oneOfMonth = localDateTime.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(0);
return TimeUtils.localDateTimeToTimestamp(oneOfMonth);
}
//获取上一个月月初的时间
// Get the start timestamp of the previous month
public static long getLastMonthStart(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var lastMonth = localDateTime.minusMonths(1);
@@ -301,7 +299,7 @@ public abstract class TimeUtils {
return TimeUtils.localDateTimeToTimestamp(with);
}
//获取上一个月月末的时间
// Get the end timestamp of the previous month
public static long getLastMonthEnd(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var lastMonth = localDateTime.minusMonths(1);
@@ -309,7 +307,7 @@ public abstract class TimeUtils {
return TimeUtils.localDateTimeToTimestamp(with);
}
//获取下一个月月初的时间
// Get the start timestamp of the next month
public static long getNextMonthStart(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var lastMonth = localDateTime.plusMonths(1);
@@ -317,7 +315,7 @@ public abstract class TimeUtils {
return TimeUtils.localDateTimeToTimestamp(with);
}
// 获取下一个月月末的时间
// Get the end timestamp of the next month
public static long getNextMonthEnd(long time) {
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
var lastMonth = localDateTime.plusMonths(1);
@@ -326,10 +324,10 @@ public abstract class TimeUtils {
}
/**
* LocalDateTime转毫秒时间戳
* Convert LocalDateTime to millisecond timestamp
*
* @param localDateTime LocalDateTime
* @return 时间戳
* @return timestamp in milliseconds
*/
public static Long localDateTimeToTimestamp(LocalDateTime localDateTime) {
var zoneId = ZoneId.systemDefault();
@@ -338,9 +336,9 @@ public abstract class TimeUtils {
}
/**
* 时间戳转LocalDateTime
* Convert millisecond timestamp to LocalDateTime
*
* @param timestamp 时间戳
* @param timestamp timestamp in milliseconds
* @return LocalDateTime
*/
public static LocalDateTime timestampToLocalDateTime(long timestamp) {
@@ -349,13 +347,13 @@ public abstract class TimeUtils {
return LocalDateTime.ofInstant(instant, zone);
}
// --------------------------------------时间大小判断--------------------------------------
// -------------------------------------- Time Comparison --------------------------------------
public static boolean timeBetween(long time, long from, long end) {
return from <= time && time <= end;
}
// --------------------------------------cron表达式--------------------------------------
// -------------------------------------- Cron Expression --------------------------------------
public static long nextTimestampByCronExpression(CronExpression expression, long currentTimestamp) {
var zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(currentTimestamp), DEFAULT_ZONE_ID);
return nextTimestampByCronExpression(expression, zonedDateTime);
@@ -18,8 +18,8 @@ import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* cron(译为克龙)代表100万年,是英文单词中最大的时间单位。
* google(译为古戈尔)代表10的100次方,足够穷尽宇宙万物
* 'cron' means 1 million years - the largest time unit in English.
* 'googol' represents 10 to the power of 100 - enough to enumerate all things in the universe.
*
* @author godotg
*/
@@ -28,7 +28,7 @@ public class ApplicationTest {
@Test
public void startSchedulerTest() {
// 加载配置文件,配置文件中必须引入scheduler
// Load the configuration file (must import the scheduler module)
var context = new ClassPathXmlApplicationContext("application.xml");
ThreadUtils.sleep(10000);
@@ -28,12 +28,12 @@ public class SchedulerController {
@Scheduler(cron = "0/5 * * * * ?")
public void cronScheduler1() {
logger.info("scheduler1 每5秒时间调度任务");
logger.info("scheduler1 scheduled task every 5 seconds");
}
@Scheduler(cron = "0,10,20,40 * * * * ?")
public void cronScheduler2() {
logger.info("scheduler2 每分钟的10秒,20秒,40秒调度任务");
logger.info("scheduler2 scheduled task at second 10, 20, 40 of every minute");
}
}
@@ -29,22 +29,22 @@ public class TimeUtilsTest {
@Test
public void testLocalDate() {
// 取当前日期:
// Get current date:
LocalDate today = LocalDate.now(); // -> 2014-12-24
// 根据年月日取日期:
// Construct date from year/month/day:
LocalDate crischristmas = LocalDate.of(2014, 12, 25); // -> 2014-12-25
// 根据字符串取:
LocalDate endOfFeb = LocalDate.parse("2014-02-28"); // 严格按照ISO yyyy-MM-dd验证,02写成2都不行,当然也有一个重载方法允许自己定义格式
LocalDate.parse("2014-02-26"); // 无效日期无法通过:DateTimeParseException: Invalid date
// 取本月第1天:
// Parse from string:
LocalDate endOfFeb = LocalDate.parse("2014-02-28"); // Strict ISO yyyy-MM-dd; overloaded methods allow custom formats
LocalDate.parse("2014-02-26"); // Invalid date throws DateTimeParseException
// Get first day of this month:
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 2017-03-01
// 取本月第2天:
// Get second day of this month:
LocalDate secondDayOfThisMonth = today.withDayOfMonth(2); // 2017-03-02
// 取本月最后一天,再也不用计算是28,29,30还是31:
// Get last day of this month (no manual 28/29/30/31 needed):
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth()); // 2017-12-31
// 取下一天:
LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1); // 变成了2018-01-01
// 取2017年1月第一个周一,用Calendar要死掉很多脑细胞:
// Get next day:
LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1); // results in 2018-01-01
// Get first Monday of January 2017 (much simpler than Calendar):
LocalDate firstMondayOf2015 = LocalDate.parse("2017-01-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2017-01-02
}
@@ -59,13 +59,13 @@ public class TimeUtilsTest {
LocalTime time = LocalTime.of(0, 0, 0);
LocalDateTime localDateTime = LocalDateTime.of(date, time);
System.out.println(localDateTime);
//将localDateTime转换成时间戳
// Convert LocalDateTime to timestamp
System.out.println("localDateTime:" + java.sql.Timestamp.valueOf(localDateTime).getTime());
}
/**
* 计算周五的活动下次开启的时间
* Calculate the next activation time for the Friday event
*/
@Test
public void test() {