日期和时间

时间线

Java的Date和TimeAPI规范要求Java使用的时间尺度为:

  • 每天86400秒
  • 每天正午与官方时间精确匹配
  • 在其他时间点上,以精确定义的方式与官方时间接近匹配

这赋予了Java很大的灵活性,使其可以进行调整,以适应官方时间未来的变化。

在Java中,Instant表示时间线上的某个点。被称为"新纪元"的时间线原点被设置为穿过伦敦格林尼治皇家天文台的本初子午线所处时区的1970年1月1日的午夜。这与UNIX/POSIX时间中使用的惯例相同。从该原点开始,时间按照每天86400秒向前或向回度量,精确到纳秒。Instant的值往回追溯10亿年**(Instant.MIN)。最大的值Instant.MAX**是公元 1 000 000 000年的12月31日.

静态方法调用**Instant.now()**会给出当前的时刻。可以用equals和compareTo方法来比较两个Instant对象,因此可以将Instant对象用作时间戳。

为了得到两个时刻之间的时间差,可以使用静态方法Duration.between,例如下面的代码展示了如何度量算法的运行时间:

Instant start=Instant.now();
runAlgorithm();
Instant end=Instant.now();
Duration timeElapsed=Duration.between(start,end);
long mills = timeElapsed.toMillis();

Duration是两个时刻之间的时间量。可以通过调用toNanos、toMillis、getSeconds、toMinutes、toHours、toDays来获得Duration按照传统单位量度的时间长度。

在Java8中,必须调用getSeconds而不是toSeconds

想要检查某个算法是否至少比另一个算法快10倍,可执行如下的计算:

Duration timeElapsed2=Duration.betwwen(start2,end2);

boolean overTenTimesFaster=timeElapsed.toNanos()*10 < timeElapsed.toNanos();

Instant和Duration类都是不可修改的类,所以诸如multipliedBy和minus这样的方法都会返回一个新的实例

示例程序

package javabase.time;import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;public class TimeLine {public static void main(String[] args) {Instant start=Instant.now();runAlgorithm();Instant end=Instant.now();Duration timeElapsed=Duration.between(start,end);long millis = timeElapsed.toMillis();System.out.printf("%d millisecond\n",millis);Instant start2 = Instant.now();runAlgorithm2();Instant end2 = Instant.now();Duration timeElapsed2 = Duration.between(start2, end2);long millis2 = timeElapsed2.toMillis();System.out.printf("%d millisecond\n",millis2);boolean overTenTimeFaster = timeElapsed.multipliedBy(10).minus(timeElapsed2).isNegative();System.out.printf("The first algorithm is %smore than times faster",overTenTimeFaster?"":"not ");}public static void runAlgorithm(){int size=100;List<Integer> list = new Random().ints().map(i -> i % 100).limit(size).boxed().collect(Collectors.toList());Collections.sort(list);System.out.println(list);}/*** 猴子排序*/public static void runAlgorithm2(){int size=10;List<Integer> list = new Random().ints().map(i -> i % 100).limit(size).boxed().collect(Collectors.toList());while (!IntStream.range(1,list.size()).allMatch(i->list.get(i-1).compareTo(list.get(i))<=0)){Collections.shuffle(list);}System.out.println(list);}}

相关接口

  • java.time.Instant

    • static Instant now()

      从最佳的可用系统时钟中获取当前的时刻。

    • Instant **plus(**TemporalAmount amountToAdd)

      Instant minus(TemporalAmount amountToSubtract)

      产生一个时刻,该时刻与当前时刻距离给定的时间量。Duration和Period实现了TemporalAmount接口。

    • Instant [plus|minus] [Nanos|Millis|Seconds] (long number)

      产生一个时刻,该时刻与当前时刻距离给定数量的纳秒、微秒或秒。

  • java.time.Duration

    • static Duration of[Nanos|Millis|Seconds|Minutes|Hours|Days] (long number)

      产生一个给定数量的指定时间单位的时间间隔

    • static Duration between(Temporal startInclusive, Temporal endExclusive)

      产生一个在给定时间点之间的Duration对象。Instant、LocalDate/LocalDateTime/LocalTime类实现了Temporal接口

    • long to[Nanos|Millis|Seconds|Minutes|Hours|Seconds|Days] ()

      获取当前时长按照方法名中的时间单位度量的数量。

    • int to[Nanos|Millis|Seconds|Minutes|Hours]Part()

      long to[Days|Hours|Minutes|Seconds|Millis|Nanos]Part ()

      当前时长中给定时间单位的部分。例如在100秒的时间间隔中,分钟的部分是1,秒的部分是40

    • Instant plus(TemporalAmount amountToAdd)

      Instant minus(TemporalAmount amountToSubtract)

      产生一个时刻,该时刻与当前时刻距离给定的时间量。Duration和Period实现了TemporalAmount接口。

    • Duration multipliedBy(long multiplicand)

      Duration dividedBy(long divisor)

      Duration negated()

      产生一个时长,该时长是通过当前时刻乘以或除以给定的量或-1得到的。

    • boolean isZero()

      boolean isNegative()

      如果当前的Duration对象是0或负数,则返回true。

    • Duration [plus|minus] [Nanos|Millis|Seconds|Minutes|Hours|Days] (long number)

      产生一个时长,该时长是通过当前时刻加上或减去给定的数量的指定时间单位而得到的

本地日期

现在让我们从绝对时间转向人类时间。在Java API中有两种人类时间,本地日期/时间和时区时间。本地日期/时间包含日期和当天的时间,但是与时区信息没有任何关联。例如1903年6月14日就是一个本地日期。因为这个日期既没有当天的信息,也没有时区信息,因此它并不对应精确的时刻。与之相反的是,1969年7月16日 09:32:00 EDT是一个时区日期/时间,表示的是时间线上的一个精确的时刻。

有许多计算并不需要时区,在某些情况下时区甚至是一种障碍。例如:你安排每周10:00开一次会。如果你加7天到最后一次会议的时区时间上,那么你可能碰巧跨越了夏令时的时间调整边界,这次会议可能会早一小时或晚一小时。

因此API的设计者们推荐程序员不要使用时区时间,除非确实想要表达绝对时间的实例。生日、假日、计划时间等都最好表示成本地时间。

LocalDate是带有年、月、日的日期。为了构建LocalDate对象,可以使用now或of静态方法:

LocalDate today=LocalDate.now();
LocalDate alnozosBirthday=LocalDate.of(1903,6,14);
alnozosBirthday=LocalDate.of(1903,Month.JUNE,14);

与UNIX和java.util.Date中使用的月从0开始计数而年从1900开始计算的不规则的惯用法不同,你需要提供通常使用的月份的数字,或者使用Month枚举。

例如,程序员日是每年的第256天。下面展示了如何计算它:

LocalDate programmersDay=LocalDate.of(2022,1,1).plusDays(255);

两个Instant之间的时长是Duration,而用于本地日期的等价物是Period,它表示的是流逝的年、月、日的数量。可以调用birthday.plus(Period.ofYears(1))来获取下一年的生日。

当然也可以直接调用birthday.plusYear(1)。但是birthday.plus(Duration.ofDays(365))在闰年是不会产生正确结果的。

until方法会产生两个本地日期之间的时长。例如

independenceDay.until(christmas)

会产生5个月21天的一段时长。这实际上并不是很有用,因为每个月的天数不尽相同。为了确定到底有多少天,可以使用:

independenceDay.until(christmas,ChronoUnit.DAYS)

警告:LocalDate API中的有些方法可能会创建出不存在的日期。例如在1月31日上加上1个月不应该产生2月31日。这些方法并不会抛出异常,而是会返回该月有效的最后一天。例如:

LacalDate.of(2016,1,31).plusMonths(1)

LacalDate.of(2016,3,31).minusMonths(1)

都将产生2016年2月29日

getDayOfWeek会产生星期日期,即DayOfWeek枚举的某个值。DayOfWeek.MONDAY的枚举值为1,而DayOfWeek.SUNDAY的枚举值为7.例如

LocalDate.of(1990,1,1).getDayOfWeek().getValue()

会产生1。DayOfWeek枚举具有便捷方法plus和minus,以7为模计算星期日期。例如,DayOfWeek.SATURDAY.plus(3)会产生DayOfWeek.TUESDAY

注释:java.util.Calendar中,星期日为1,星期六为7

java9添加了datesUntil方法,它们会产生LocalDate对象流

除了LocalDate之外,还有MonthDay、YearMonth和Year类可以描述部分日期。

示例程序

package javabase.time;import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class LocalDates {public static void main(String[] args) {LocalDate today = LocalDate.now();System.out.println("today: "+today);LocalDate alonzosBirthday = LocalDate.of(1903, 6, 14);alonzosBirthday=LocalDate.of(1903, Month.JUNE,14);System.out.println("alonzosBirthday: "+alonzosBirthday);LocalDate programmersDay = LocalDate.of(2018, 1, 1).plusDays(255);System.out.println("programmersDay: "+programmersDay);LocalDate independenceDay = LocalDate.of(2018, Month.JULY, 4);LocalDate christmas = LocalDate.of(2018, Month.DECEMBER, 25);System.out.println("Until christmas: "+independenceDay.until(christmas));System.out.println("Until christmas: "+independenceDay.until(christmas, ChronoUnit.DAYS));System.out.println(LocalDate.of(2016,1,31).plusMonths(1));System.out.println(LocalDate.of(2016,3,31).minusMonths(1));DayOfWeek startOfLastMillennium = LocalDate.of(1900, 1, 1).getDayOfWeek();System.out.println("startOfLastMillennium: "+startOfLastMillennium);System.out.println(startOfLastMillennium.getValue());System.out.println(DayOfWeek.SATURDAY.plus(3));LocalDate start = LocalDate.of(2000, 1, 1);LocalDate endExclusive = LocalDate.now();Stream<LocalDate> firstDaysInMonth = start.datesUntil(endExclusive, Period.ofMonths(1));System.out.println("firstDaysInMonth: "+firstDaysInMonth.collect(Collectors.toList()));}
}

相关接口

  • java.time.LocalDate

    • static LocalDate now()

      获取当前的LocalDate

    • static LocalDate of(int year,int month,int dayOfMonth)

      static LocalDate of(int year,Month month,int dayOfMonth)

      用给定的年、月(1-12之间的整数或Month枚举的值)和日(1-31)产生一个本地日期

    • LocalDate [plus|minus] [Days|Weeks|Months|Years] (long number)

      产生一个LocalDate,该对象是通过在当前对象上加上或减去给定数量的时间单位获得的。

    • LocalDate **plus(**TemporalAmount amountToAdd)

      LocalDate minus(TemporalAmount amountToSubtract)

      产生一个时刻,该时刻与当前时刻距离给定的时间量。Duration和Period类实现了TemporalAmount接口

    • LocalDate withDayOfMonth(int dayOfMonth)

      LocalDate withDayOfYear(int dayOfYear)

      LocalDate withMonth(int month)

      LocalDate withyear(int year)

      返回一个新的LocalDate,将月份日期、年日期、月或年修改为给定值

    • Month getMonth()

      int getMonthValue()

      获取用Month枚举表示的月份,或者用1-12之间的数字表示的月份。

    • int getYear()

      获取年份,在-999,999,999到999,999,999之间

    • Period until(ChronoLocalDate endDateExclusive)

      获取直到给定终止日期的period。LocalDate和Date类针对非公历实现了ChronoLocalDate接口

    • boolean isBefore(ChronoLocalDate other)

      boolean isAfter(ChronoLocalDate other)

      如果该日期在给定日期之前或之后,则返回true

    • boolean isLeapYear()

      如果当前是闰年,则返回ture。

    • Stream< LocalDate > datesUntil(LocalDate endExclusive)

      Stream< LocalDate > datesUntil(LocalDate endExclusive,Period step)

      产生一个日期流,从当前的LocalDate对象直至参数endExclusive指定的日期,其中步长尺寸为1,或是给定的period

  • java.time.Period

    • static Period of(int years,int months,int days)

      Period of[Days|Weeks|Months|Years] (int number)

      用给定数量的时间单位产生一个Period对象

    • int get[Days|Months|Years] ()

      获取当前Period对象的日、月、年。

    • Period [plus|minus] [Days|Months|Years] (long number)

      产生一个Period ,该对象是通过在当前对象上加上或减去给定数量的时间单位获得的。

    • Period plus(TemporalAmount amountToAdd)

      Period minus(TemporalAmount amountToSubtract)

      产生一个时刻,该时刻与当前时刻距离给定的时间量。Duration和Period类实现了TemporalAmount接口

    • Period with[Days|Months|Years] (int number)

      返回一个新的Period,将日、月、年修改为给定值

日期调整器

TemporalAdjusters类提供了大量用于常见调整的静态方法。可以将调整方法的结果传递给with方法。例如,某个月的第一个星期二可以像下面这样计算:

LocalDate firstTuesday=LocaDate.of(year,month,1).with(TemporalAdjusters.nextOrSame(DayOfWeek.TUEDDAY));

with会返回一个新的LocalDate对象,而不会修改原来的对象。

还可以通过实现TemporalAdjusters接口来创建自己的调整器。下面是用于计算下一个工作日的调整期器:

TemporalAdjusters NEXT_WORKDAY=w->
{var result=(LocalDate)w;do{result=result.plusDays(1);}while(result.getDayOfWeek()>=6);return result;
}
LocalDate backToWork = today.with(NEXT_WORKDAY)

注意:lambda表达式的参数类型为Temporal,它必须被强制转型为LocalDate。可以使用ofDateAdjuster方法来避免这种强制转型,该方法期望得到的参数是类型为UnaryOperator< LocalDate >的lambda表达式

TemporalAdjusters NEXT_WORKDAY=TemporalAdjusters.ofDateAdjuster(w->
{LocalDate result=w;//no castdo{result=result.plusDays(1);}while(result.getDayOfWeek()>=6);return result;
});

相关接口

  • java.time.LocalDate

    • LocalDate with(TemporalAdjusters adjuster)

      返回该日期通过给定的调整器调整后的结果

  • java.time.temporal.TemporalAdjusters

    • static TemporalAdjusters next(DayOfWeek dayOfWeek)

      static TemporalAdjusters nextOrSame(DayOfWeek dayOfWeek)

      static TemporalAdjusters previous(DayOfWeek dayOfWeek)

      static TemporalAdjusters previousOrSame(DayOfWeek dayOfWeek)

      返回一个调整器,用于将日期调整为给定的星期日期

    • static TemporalAdjusters dayOfWeekInMonth(int n,DayOfWeek dayOfWeek)

      static TemporalAdjusters lastInMonth(DayOfWeek dayOfWeek)

      返回一个调整器,用于将日期调整为月份中第n个或最后一个给定的星期日期

    • static TemporalAdjusters firstDayOfMonth

      static TemporalAdjusters firstDayOfNextMonth

      static TemporalAdjusters firstDayOfYear

      static TemporalAdjusters firstDayOfNextYear()

      static TemporalAdjusters lastDayOfMonth()

      static TemporalAdjusters lastDayOfYear()

      返回一个调整器,用于将日期调整为月份或年份中给定的日期

本地时间

LocalTime表示当日时刻,例如15:00:00。可以用now或of方法创建其实例:

LocalTime rightNow = LocalTime.now();
LocalTIme bedTime = LocalTime.of(22,30);//or LocalTime.of(22,30,0)

API说明展示了最常见的对本地时间的操作。plus和minus操作时按照一天24小时循环操作的,例如:

LocalTime wakeup=bedtime.plusHours(8)//wakeup is 6:00:00

注释:LocalTime自身并不关心AM/PM。这种愚蠢的设计将问题抛给格式器去解决。

还有一个表示日期和时间的LocalDateTime类。这个类适合存储固定时区的时间点,例如用于排课或排程。但是如果你的计算需要跨越夏令时,或者需要处理不同时区的用户,那么就应该使用接下来要讨论的ZonedDateTime类

相关接口

  • java.time.LocalTime

    • static LocalTime now()

      获取当前的LocalTime。.

    • static LocalTime of(int hour,int minute)

      static LocalTime of(int hour,int minute,int second)

      static LocalTime of(int hour,int minute,int nanoOfSecond)

      产生一个LocalTIme,它具有给定的小时(0-23)、分钟、秒(0-59)和纳秒(0-999,999,999).

    • LocalTime [plus|minus] [Days|Weeks|Months|Years] (long number)

      产生一个LocalTime ,该对象是通过在当前对象上加上或减去给定数量的时间单位获得的。

    • LocalTime **plus(**TemporalAmount amountToAdd)

      LocalTime minus(TemporalAmount amountToSubtract)

      产生一个时刻,该时刻与当前时刻距离给定的时间量。Duration和Period类实现了TemporalAmount接口.

    • int getHour()

      获取小时(0-23)

    • int getMinute()

      int getSecond()

      获取分钟或秒(0-59).

    • int getNano()

      获取纳秒(0-999,999,999).

    • int toSecondOfDay()

      long toNanoOfDay()

      产生自午夜到当前LocalTime的秒或纳秒数.

    • boolean isBefore()

      boolean isAfter()

      如果当前日期在给定日期之前或之后,则返回true.

时区时间

在理性的世界里,我们都会遵守格林尼治时间。但是实际中时区显得并不规则,并且还有国际日期变更线和夏令时。

**互联网编码分配管理机构(Internet Assigned Numbers Authority,IANA)**保存着一个数据库,里面存储着世界上所有已知的时区,它每年都会更新数次,而批量更新会处理夏令时的变更规则。Java使用了IANA数据库。

每个时区都有一个ID,例如America/New_York和Europe/Berlin。可以调用ZoneId.getAvailableZoneIds查找所有可用的id。

给定一个时区ID,静态方法ZoneId.of(id)可以产生一个ZoneId对象。可以调用local.atZone(ZoneId)用这个对象将LocalDateTime对象转换成ZoneDateTime对象,或者可以调用静态方法ZonedDateTime.of(year,month,day,hour,minute,second,nano,zoneId)来构造一个ZonedDateTime对象。例如:

ZonedDateTime apollo11launch = ZonedDateTime.of(1969,7,16,9,32,0,0,ZoneId.of("America/NewYork"));

这是一个具体的时刻,调用apollo11launch.toInstant可以获得对应得Instant对象。反过来,如果你有一个时刻对象,调用instant.atZone(ZoneId.of(“UTC”))可以获得格林尼治皇家天文台得ZonedDateTime对象,或者使用其他的ZoneId获得地球上其他地方的ZoneId

UTC代表"协调世界时",这是英文"Coordinated Universal Time"和法文"Temps Universal Coordine"首字母缩写的折中。UTC是不考虑夏令时的格林尼治皇家天文台时间。

当夏令时开始时,时钟要向前拨快一小时。例如,在2013年,中欧地区在3月31日2:00切换到夏令时,如果你试图构建的时间时不存在的3月31日2:30,那么你实际上得到的是3:30:

ZonedDateTime skipped=ZonedDateTime.of(LocalDate.of(2013,3,31),LocalTime.of(2,30),ZoneId.of("Europe/Berlin"));//Constructs March 3:30

反过来,当夏令时结束时,时钟要向回拨慢一小时,这样同一个本地时间就会有出现两次。当你构建位于这个时间段内的时间对象时,就会得到这两个时刻中较早的一个:

ZonedDateTime ambiguous=ZonedDateTime.of(LocalDate.of(2013,10,27),LocalTime.of(2,30),ZoneId.of("Europe/Berlin"));//2013-10-27T02:30+02:00[Europe/Berlin]ZonedDateTime anHourLater=ambigous.plusHours(1);//2013-10-27T02:30+01:00[Europe/Berlin]

一个小时后的时间会具有相同的小时和分钟,但是时区的偏移量会发生变化。

你还需要在调整跨越夏令时边界的日期时特别注意。例如,如果你将会议设置在下个星期,不要直接加上一个7天的Duration,而是应该使用Period类。

示例程序

package javabase.time;import java.time.*;public class ZonedTimes {public static void main(String[] args) {System.out.println(ZoneId.getAvailableZoneIds());ZonedDateTime apollo11launch = ZonedDateTime.of(1969, 7, 16, 9, 32, 0, 0, ZoneId.of("America/New_York"));System.out.println("apollo11launch: "+apollo11launch);Instant instant = apollo11launch.toInstant();System.out.println("instant: "+instant);ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("UTC"));System.out.println("zonedDateTime: "+zonedDateTime);ZonedDateTime skipped = ZonedDateTime.of(LocalDate.of(2013, 3, 21), LocalTime.of(2, 30), ZoneId.of("Europe/Berlin"));System.out.println("skipped: "+skipped);ZonedDateTime ambiguous = ZonedDateTime.of(LocalDate.of(2013, 10, 27),LocalTime.of(2, 30), ZoneId.of("Europe/Berlin"));ZonedDateTime anHourLater = ambiguous.plusHours(1);System.out.println("ambiguous: "+ambiguous);System.out.println("anHourLater: "+anHourLater);ZonedDateTime meeting = ZonedDateTime.of(LocalDate.of(2013, 10, 31),LocalTime.of(14, 30), ZoneId.of("America/Los_Angeles"));System.out.println("meeting: "+meeting);ZonedDateTime nextMeeting = meeting.plus(Duration.ofDays(7));System.out.println("nextMeeting: "+nextMeeting);nextMeeting=meeting.plus(Period.ofDays(7));System.out.println("nextMeeting: "+nextMeeting);}
}

相关接口

  • java.time.ZonedDateTime

    • static ZonedDateTime now()

      获取当前的ZonedDateTime.

    • static ZonedDateTime of(int year,int month,int datOfMonth,int hour,int minute,int second,int nanoOfSecond,ZoneId zone)

      static ZonedDateTime of(LocalDate date,LocalTime time,ZoneId id)

      static ZonedDateTime of(LocalDateTime localDateTime,ZoneId id)

      static ZonedDateTime ofInstant(Instant instant,ZoneId zone)

      用给定的参数和时区产生一个ZonedDateTime.

    • ZonedDateTime [plus|minus] [Days|Weeks|Months|Years|Hours|Minutes|Seconds|Nanos] (long number)

      产生一个ZonedDateTime,该对象是通过在当前对象上加上或减去给定数量的时间单位获得的。

    • ZonedDateTime **plus(**TemporalAmount amountToAdd)

      ZonedDateTime minus(TemporalAmount amountToSubtract)

      产生一个时刻,该时刻与当前时刻距离给定的时间量。Duration和Period类实现了TemporalAmount接口.

    • ZonedDateTime with[DayOfMonth|DayOfYear|Month|Year|Hour|Minute|Second|Nano] (int value)

      返回一个新的ZonedDateTime ,用给定的值替换给定的时间单位

    • ZonedDateTime withZoneSameInstant(ZoneId zone)

      ZonedDateTime withZoneSameLocal(ZoneId zone)

      返回一个新的ZonedDateTime ,位于给定的时区,它与当前对象要么表示相同的时刻,要么表示相同的本地时间。

    • int getDayOfMonth()

      获取月份日期(1-31)

    • int getDayOfYear()

      获取年份日期(1-366)

    • DayOfWeek getDayOfWeek()

      获取星期日期,返回DayOfWeek枚举的值

    • Month getMonth()

      int getMonthValue()

      获取用Month枚举值代表的月份,或者1-12之间的数字表示的月份。

    • int getYear()

      获取年份,在-999,999,999到999,999,999之间

    • int getHour()

      获取小时(0-23)

    • int getMinute()

      int getSecond()

      获取分钟或秒(0-59).

    • int getNano()

      获取纳秒(0-999,999,999)

    • public ZoneOffset getOffset()

      获取与UTC的时间差距。差距可在-12:00 - +14:00变化。有些时区还有小数时间差。时间差会随着夏令时变化

    • LocalDate toLocalDate()

      LocalTime toLocalTime()

      LocalDateTime toLocalDateTime()

      Instant toInstant()

      生成当地日期、时间、或日期/时间,或相应的瞬间

    • boolean isBefore(ChronoZonedDateTime other)

      boolean isAfter(ChronoZonedDateTime other)

      如果当前时区日期/时间在给定时区日期/时间之前或之后,则返回true.

格式化和解析

DateTimeFormatter类提供了三种用于打印日期/时间值的格式器

预定义的格式器

格式器 描述 示例
BASIC_ISO_DATE 年、月、日、时区偏移量,中间无分隔符 19690716-0500
ISO_LOCAL_DATE
ISO_LOCAL_TIME
ISO_LOCAL_DATE_TIME
分隔符为 - 、: 、T 1969-07-16,09:32:00
1969-07-16T09:32:00
ISO_OFFSET_DATE
ISO_OFFSET_TIME
ISO_OFFSET_DATE_TIME
类似ISO_LOCAL_XXX,但是有时区偏移量 1969-07-16-05:00
09:32:00-05:00
1969-07-16T09:32:00-05:00:00
ISO_ZONED_DATE_TIME 有时区偏移量和时区ID 1969-07-16T09:32:00-05:00[America/New_York]
ISO_INSTANT 在UTC中,用z时区ID来表示 1969-07-16T14:32:00Z
ISO_DATE
ISO_TIME
ISO_DATE_TIME
类似ISO_OFFSET_XXX,但是时区信息是可选的 1969-07-16-05:00
09:32:00-05:00
1969-07-16T09:32:00-05:00[America/New_York]
ISO_ORDINAL_DATE LocalDate的年和年日期 1969-197
ISO_WEEK_DATE LocalDate的年、星期和星期日期 1969-W29-3
RFC_1123_DATE_TIME 用于邮件时间戳的标准,编纂与RFC822,并在RFC1123中将年份更新为4位 Wed,16 Jul 1969 09:32:00 -0500

要使用标准的格式器,可以直接调用其format方法:

String formatted = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(apollo11launch);

locale相关的格式器

标准格式器主要是为了机器可读的时间戳而设计的。为了向人类读者表示日期和时间。可以使用locale相关的格式器。对于日期和时间而言,有4种与locale相关的格式化风格,即SHORT、MEDIUM、LONG、FULL。

风格 日期 时间
SHORT 7/16/69 9:32 AM
MEDIUM Jul 16,1969 9:32:00 AM
LONG July 16,1969 9:32:00 AM EDT
FULL Wednesday,July 16,1969 9:32:00 AM EDT

静态方法ofLocalizedDate、ofLocalizedTime和ofLocalizedDateTime可以创建这种格式器:

DateTimeFormatter formatter=DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
String formatted = formater.format(apollo11launch);

这些方法使用了默认的locale。为了切换到不同的locale,可以直接使用withLocale方法

formatted=fomatter.withLocale(Locale.FRENCH).format(apollo11lauhch)

DayOfWeek和Month枚举都有getDisplayName方法,可以按照不同的locale和格式给出星期日期和月份的名字:

for(DayOfWeek w:DayOfWeek.values())System.out.print(w.getDisplayName(TextStyle.SHORT,Locale.ENGILSH)+" ");

带有定制模式的格式器

可以通过指定模式来定制自己的日期格式,例如:

formatter = DateTimeFormatter.ofPattern("E yyyy-MM-dd HH:mm");

会将日期格式化为Wed 1969-07-16 09:32的形式。每个字符都表示一个不同的时间域,而字母重复的次数对应于所选择的特定格式。下面是最有用的模式元素:

时间域或目的 示例
ERA G : AD, GGGG: Anno Domini,GGGGG:A
YEAR_OF_ERA yy:69,yyyy:1969
MONTH_OF_YEAR M:7 , MM:07 , MMM:Jul , MMMM: July , MMMMM:J
DAY_OF_MOUTH d:6 , dd:06
DAY_OF_WEED e:3 , E:Wed , EEEE:Wednesday , EEEEE:W
HOUR_OF_DAY H:9 , HH:09
CLOCK_HOUR_OF_AM_PM k:9 , kk:09
AMPM_OF_DAY a:AM
MINUTE_OF_HOUR mm:02
SECOND_OF_MINUTE ss:00
NANO_OF_SECOND nnnnnn:000000
时区ID VV:America/New_York
时区名 z:EDT , zzzz:Eastern Daylight Time V :ET, VVVV:Eastern time
时区偏移量 x:-04 , xx:-0400 , xxx:-04:00 , XXX:同xxx,但是Z表示0
本地化的时区偏移量 O:GMT_4 , OOOO:GMT-04:00
修改后的儒略日 g:58243

为了解析字符串中的日期/时间值,可以使用众多的静态parse方法之一。例如:

LocalDate churchsBirthday=LocalDate.parse("1903-06-14");
ZonedDateTime apollo11launch=ZonedDateTime.parse("1969-07-16 03:32:00-0400",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxx"));

第一个调用使用了标准的ISO_LOCAL_DATE格式器,而第二个调用使用的是一个定制的格式器。

相关接口

  • java.time.format.DateTimeFormatter

    • String format(TemporaAccessor Temporal)

      格式化给定值。Instant、LocalDate、LocalTime、LocalDateTime和ZonedDateTime,以及许多的其他类,都实现了TemporaAccessor接口。

    • static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle)

    • static DateTimeFormatter ofLocalizedTime(FormatStyle timeStyle)

    • static DateTimeFormatter ofLocalizedDateTIme(FormatStyle dateTimeStyle)

    • static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateStyle,FormatStyle timeStyle)

      产生一个用于给定风格的格式器。FormatStyle枚举的值包括SHORT、 MEDIUM、 LONG、 FULL。

    • DateTimeFormatter withLocale(Locale locale)

      用给定的地点产生一个等价于当前格式器的格式器。

    • static DateTimeFormatter ofPattern(String Pattern)

      static DateTimeFormatter ofPattern(String Pattern,Locale locale)

      用给定的模式和地点产生一个格式器

  • java.time.LocalDate

    • static LocalDate parse(CharSequence text)

      static LocalDate parse(CharSequence text,DateTimeFormatter formatter)

      用默认的格式器或给定的格式器产生一个LocalDate

  • java.time.ZonedDateTime

    • static ZonedDateTime parse(CahrSequence text)

      static ZonedDateTime parse(CahrSequence text,DateTimeFormatter formatter)

      用默认的格式器或给定的格式器产生一个ZonedDateTime

与遗留代码的互操作

转换到遗留类 转换自遗留类
Instant<->java.util.Date Date,from(instant) date.toInstant()
ZonedDateTime<->java.utl.GregorianCalendar GregorianCalendar.from(zonedDateTime) cal.toZonedDateTime()
Instant<->java.sql.Timestamp TimeStamp.from(instant) timeStamp.toInstant()
LocalDateTime<->java.sql.Timestamp TimeStamp.valueOf(localDateTime) timeStamp.toLocalDateTime()
LocalDate<->java.sql.Date Date.valueOf(localDate) date.toLacalDate()
LocalTime<->java.sql.Time Time.valueOf(localTime) time.toLocalTIme()
LocalTimeFormatter<->java.text.DateFormat formatter.toFormat()
java.util.TimeZone<->ZoneId Timezone.getTimeZone(id) timeZone.toZoneId()
java.nio.file.attribute.FileTime<->Instant FileTime.from(instant) fileTime.toInstant()

Java日期和时间详解相关推荐

  1. JS中的日期和时间详解

    JS中的日期和时间详解 关于Date()构造函数 简单实例 用Date()构造函数创建时钟 关于Date()构造函数 Date()构造函数是javascript的核心语言部分,用来创建表示时间和日期的 ...

  2. java日期时间详解

    一.简介 java中的日期处理一直是个问题,没有很好的方式去处理,所以才有第三方框架的位置比如joda. 文章主要对java日期处理的详解,用1.8可以不用joda. 1. 相关概念 首先我们对一些基 ...

  3. 详解 Java 日期与时间

    文章目录 一.时区 二.夏令时 三.旧 API 3.1. Date 3.2. SimpleDateFormat 3.3. Calendar 四.新 API 4.1. LocalDateTime 4.2 ...

  4. JavaScript日期时间详解

    JavaScript日期时间详解 日期与时间 获取时间对象 var date=new Date() 时间的获取方法 date.toString() date.toLocaleString() date ...

  5. java 8 新功能详解_Java 8和Java 14之间的新功能

    java 8 新功能详解 从版本9开始,Java每6个月就有一次新功能,因此很难跟踪这些新更改. 互联网上的大多数信息都描述了最近2个Java版本之间的变化. 但是,如果您的情况与我相似,则说明您使用 ...

  6. java 8 新功能详解_Java 8的8个新功能

    java 8 新功能详解 注意:确保还检查了我们的详细教程Java 8 Features – ULTIMATE Guide . Jdk 1.8(又名Java 8)今天发布,这意味着它的通用发布版本已经 ...

  7. java控制excel_利用Java控制EXCEL实例详解

    利用Java控制EXCEL实例详解发布者:本站     时间:2020-05-06 15:05:43 使用Windows操作系统的朋友对Excel(电子表格)一定不会陌生,但是要使用Java语言来操纵 ...

  8. java -jar 和 -cp详解

    java -jar 和 -cp详解 命令行执行程序 假如我们有一个程序,把它打包成Test.jar,如何运行才能成功输出Hello World package com.test; public cla ...

  9. Java编程配置思路详解

    Java编程配置思路详解 SpringBoot虽然提供了很多优秀的starter帮助我们快速开发,可实际生产环境的特殊性,我们依然需要对默认整合配置做自定义操作,提高程序的可控性,虽然你配的不一定比官 ...

最新文章

  1. 50年前,Hello World发明者第一次提交的Go代码长这样……
  2. CASS 7.1 和 AutoCAD 2006的安装使用
  3. selenium--字符串/整型问题Can't convert 'int' object to str implicitly提示解决方法
  4. linux怎么安装java环境变量_linux怎么配置java环境变量
  5. opengl加载显示3D模型3MF类型文件
  6. 使用CArchive类进行序列化
  7. rabbitmq订单模块_RabbitMQ播放模块! 构架
  8. numpy 中shape的用法
  9. AngularJS + Java---前台网页与后台数据库传递数据 基本结构
  10. 生宣、熟宣、半生半熟宣纸各有什么特点?初学书法用哪种宣纸好?
  11. 机器学习2-Logistic回归
  12. 转载:最舒服的色彩搭配RGB值配色 (赞、实用)
  13. unity mmd不支持android,MMD模型导入Unity的解决方案
  14. 人工神经网络概念梳理与实例演示
  15. 解决No version of NDK matched the requested version编译报错的问题
  16. npm包本地离线安装
  17. LATEX中公式中插入图片,文字段落中插入图片
  18. 计算机函数sun怎么用,手把手演示sumif函数怎么用【处置步骤】
  19. Flask cookie、session与闪现
  20. jquery设置disabled属性的方法

热门文章

  1. Daniel带你深层解析象锅锅比赛
  2. 由c语言转到c++,我们需要做什么?
  3. grafana使用配置
  4. 修改hosts提升访问速度
  5. 【引用】音频文件格式全介绍_chrome os
  6. elementUI tree回显值
  7. 他在你心里,变的廉价了么:伤感日志
  8. ReactNative中的样式
  9. linux系统更改ip无法保存,解决win10修改IP无法保存并提示“出现了一个意外情况”的方法...
  10. 区块链非对称加密技术过程简易图解