时间术语:
Greenwich:格林威治/格林尼治,是位于伦敦市中心东南部的一个区,1675国王查理二世在此建立了皇家格林尼治天文台,1851年御用天文学家艾里在天文台设置了中星仪并确定了格林威治子午线,1884年在美国华盛顿特区举行的国际本初子午线大会上正式将此线定之为经度的起点。
GMT(Greenwich Mean Time):格林尼治标准时间/格林威治标准时间, 格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界(UTC)。
UTC(Coordinated Universal Time):协调世界时/世界协调时间/世界标准时间,UTC基于国际原子时,并通过不规则的加入闰秒来抵消地球自转变慢的影响,是比GMT时间更精确,也就是说一天不一定是86400秒,可能会根据情况加入一个闰秒。闰秒在必要的时候会被插入到UTC中,以保证协调世界时UTC与世界时UT1相差不超过0.9秒。UTC表示时间偏移量时,书写格式为±[hh]:[mm]或±[hh][mm]或±[hh]。UTC的0时区表示会在时间后面加一个”Z”,如“01:30Z”或“0130Z”表示0时区的1时30分,而北京时间为“09:30 UTC+08:00”或“09:30 UTC+0800”,会比标准UTC时间早8个小时。
Epoch(Unix epoch):操作系统的纪元时间,为“1970-01-01T00:00:00Z”,即格林威治当地时间1970年1月1日0时0分0秒,至于为什么会选择这个时间说法不一,最好的解释是设计Unix/类Unix操作系统时为了方便决定采用的时间,并不是Unix/类Unix系统的诞生时间。
Unix time:Unix timestamp/POSIX time/Epoch time,定义为从1970-01-01 00:00:00 UTC开始所经过的秒数。由于不考虑闰秒,所以它不是线性的时间表示,也不是真正的UTC时间表示。
Java中的时间处理:
java.util.Date
:
用来表示一个特定时刻,精确到毫秒(1/1000秒)。所表示的时间总是UTC时间,不考虑系统的时区。由于该类“历史悠久”,很多方法已经被废弃了。如果想使用展示日期相关的方法,建议使用java.text.DateFormat
类相关方法。如果想使用计算或划分日期的方法,建议使用java.util.Calendar
类相关方法。
public Date()
该构造器会根据系统当前时间(System.currentTimeMillis())构造时间。
public Date(long milliseconds)
该构造器会使用从1970-01-01T00:00:00Z所经过的毫秒数构造时间,即Unix时间*1000。
其他构造器不建议使用,如public Date(int year, int month, int day)
@deprecated 该构造器会使用GregorianCalendar(格里高利历/公历)默认时区来构造日期,参数中的year为从1900年后多少年,如果year为0则表示1900+0=1900年。month为0 - 11,day为1 - 31。其他类似构造器:public Date(int year, int month, int day, int hour, int minute)
hour为0 - 23,minute为0 - 59。public Date(int year, int month, int day, int hour, int minute, int second)
second为0 - 59。public Date(String string)
虽然可以解析很多格式的时间表示字符串,但是也已不建议使用了。
Date类可以很方便的进行时间比较,如public boolean after(Date date)
可以判断该时间是否在给定时间之后,public boolean before(Date date)
可以判断该时间是否在给定时间之前,同时也为比较实现了compareTo
和equals
方法。可以通过public long getTime()
方法拿到从1970-01-01T00:00:00Z所经过的毫秒数。
其它所有方法都已经废弃了,不建议使用。
java.util.Calendar
:
用来让Date对象和一系列整形字段(如YEAR,MONTH)很方便的转换。Calendar
是一个抽象类,其子类可以根据情况将Date
解释成不同的日期形式,已知的也是我们最常用到的子类就是GregorianCalendar(格里高利历/公历)。
像其它的本地化敏感类一样,Calendar
会提供getInstance
一系列方法,返回基于系统设置的GregorianCalendar
实例并将时间和日期初始化为当前的时间日期。
如果Calendar
是宽松模式的,它可以接受比它所生成的日历字段范围更大范围内的值,如一个宽松的GregorianCalendar
会把MONTH==JANUARY,DAY_OF_MONTH==32解释为2月1日,而非宽松的GregorianCalendar
则会抛出字段越界异常。
Calendar
使用两个参数定义了指定情况下的7 天制星期:firstDayOfWeek(每周的第一天是周几/周几作为每周的开始,通常为SUNDAY)和minimalDaysInFirstWeek(新年的第一个周所包含的最小天数,从1-7,如GregorianCalendar
日历中该值为1,那么新年的第一个周就必须包含1月1日,而如果该值为7,则只有当1月1号那天的星期等于firstDayOfWeek时,1月1日所在的周才会最为新年的第一周,否则将作为上一年的最后一周,因此firstDayOfWeek和minimalDaysInFirstWeek共同决定了1月1日所在的那一周会不会成为新年的第一周)。
Calendar
的字段操作:
set(f, value)
方法可以更改f字段的值为value,而且是马上更改内部字段的值,但是calendar的milliseconds值要在下次调用get()
、getTime()
、getTimeInMillis()
方法时才会重新计算,因此多次调用set()
方法不会触发多次不必要的计算。由于set()
方法因为修改了字段的值,其他日历字段也可能会发生更改,这取决于日历字段、日历字段的值和日历系统。此外get(f)
没有必要返回计算后的字段值,因为这些细节会由具体的Calendar决定,如GregorianCalendar最初被设置为1999年8月31日。调用set(Calendar.MONTH, Calendar.SEPTEMBER)
将该日期设置为1999年9月31日。如果随后调用 getTime(),将解析为1999年10月1日(因为9月没有31日)的一个暂时内部表示。但是,在调用 getTime() 之前调用 set(Calendar.DAY_OF_MONTH, 30) 会将该日期设置为 1999年9月30日,因为在调用 set() 之后没有发生马上重新计算。add(f, delta)
方法可以给f字段的值设置delta偏移,相当于set(f, get(f) + delta)
。为了防止add(f, delta)
方法产生相应字段值地溢出,约定两个规则:
规则一:如果设置了delta后f字段的值发生了溢出,则将该字段的值取模以调整回取值范围内,并且下一个更大的字段的值会相应的增加或减少。
规则二:如果你想调整某个字段的值并且不希望比它更小的字段的值发生变化,很多情况下这是不可能的,因为更小字段的最大最小值可能已经改变了。
如GregorianCalendar最初被设置为1999年8月31日。调用add(Calendar.MONTH, 13)
,由于8+13=21>12造成了MONTH字段的值越界,所以MONTH值对12取模为9,同时更大的字段YEAR需要增加为2000,由由于9月没有31日,所以将DAY_OF_MONTH调整为最接近的30,所以最终的时间为2000年9月30日。
注意:add(f, delta)
方法会立即重新计算calendar的milliseconds值和所有字段。roll(f, delta)
方法同样可以给f字段的值设置delta偏移,但不会对更大的字段产生影响。相当于“调用后更大字段的值不会发生改变”规则的add(f, delta)
方法调用。
注意:使用add(f, delta)
或roll(f, delta)
方法时一定要谨慎,如1999年1月31日当用户按下一个月的按钮时最好的结果是1999年2月28日,如果继续按下一个月按钮,那显示1999年3月31日还是1999年3月28日。
Calendar
类的一些字段:
- JANUARY=0, FEBRUARY=1, ……UNDECIMBER=12。各个月份(MONTH)常量为int类型,从0开始,UNDECIMBER在GregorianCalendar不会用到,但中国农历日历会用到(因为可能会多闰一个月,即一年13个月)。
- SUNDAY=1, MONDAY=2, SATURDAY=7。各个星期(DAY_OF_WEEK)常量为int类型,从1开始。
- ERA:纪年。如AD、BC
- YEAR:年。如1970
- MONTH:月。如1(FEBRUARY)
- WEEK_OF_YEAR:当前年的周数。由
getFirstDayOfWeek()
和getMinimalDaysInFirstWeek()
决定。 - WEEK_OF_MONTH:当前月的周数。由
getFirstDayOfWeek()
和getMinimalDaysInFirstWeek()
决定。 - DATE:日。与DAY_OF_MONTH同义,指当月多少日/多少号。
- DAY_OF_MONTH:日。与DATE同义。
- DAY_OF_YEAR:当前年的总天数。
- DAY_OF_WEEK:周几。取值为SUNDAY - SATURDAY。
- DAY_OF_WEEK_IN_MONTH:当前月的第几周。与DAY_OF_WEEK字段一起使用时,就可以唯一地指定某月中的某一天。不会受
getFirstDayOfWeek()
和getMinimalDaysInFirstWeek()
影响。DAY_OF_MONTH 1-7总会对应DAY_OF_WEEK_IN_MONTH 1,也就是说每个月的1-7号总是当前月的第一周,2-8号是第二周,以此类推。DAY_OF_MONTH 0表示第一周的前一周,而负值表示当前月的倒数第几周,由于对齐方式和正向不一样,所以一个月的最后一个星期天被指定为DAY_OF_WEEK=SUNDAY, DAY_OF_WEEK_IN_MONTH=-1。 - AM_PM:上午/下午。判断HOUR是指上午(AM)还是下午(PM)。
- HOUR:时。12小时制的时。
- HOUR_OF_DAY:时。24小时制的时。
- MINUTE:分。
- SECOND:秒。
- MILLISECOND:毫秒。
- ZONE_OFFSET:非夏令时时区偏移的毫秒数。相当于
TimeZone#getRawOffset()
- DST_OFFSET:夏令时时区偏移的毫秒数。相当于
TimeZone#getDSTSavings()
- FIELD_COUNT:Calendar的总字段个数。
Calendar
的一些方法:
public boolean after(Object calendar)
比较两个Calendar所持有的Date
对象,不依赖Calendar时区。public boolean before(Object calendar)
比较两个Calendar所持有的Date
对象,不依赖Calendar时区。public final void clear()
清除所有时间字段,标记为reset并清零。public final void clear(int field)
清除某个字段,标记为reset并清零。public long getTimeInMillis()
返回Calendar
所表示的时间毫秒数(1970-01-01T00:00:00Z所经过的毫秒数),如果必要会重新计算时间。public final Date getTime()
将getTimeInMillis()
返回的时间包装成Date
对象返回。public final void set(int year, int month, int day)
系列方法 其中month从0开始算起,所以month最好使用Calendar.JANUARY、Calendar.FEBRUARY等常量。public final void setTime(Date date)
public void setTimeInMillis(long milliseconds)
java.text.DateFormat
:
用来格式化/解析 日期和时间。DateFormat
是一个抽象类,最常用的是它的子类SimpleDateFormat
。
可以根据想要格式化/解析的时Date还是Time还是DateTime的使用getDateInstance()
、getDateInstance(int style)
、getDateInstance(int style, Locale locale)
、getTimeInstance()
、getTimeInstance(int style)
、getTimeInstance(int style, Locale locale)
、getDateTimeInstance()
、getDateTimeInstance(int dateStyle, int timeStyle)
、getDateTimeInstance(int dateStyle, int timeStyle, Locale locale)
不同的静态方法获取不同的DateFormat
实例。
public final StringBuffer format(Object object, StringBuffer buffer, FieldPosition field)
方法,可以将Date/Number日期类型的数据根据指定模式串格式化成想要显示的字符串。其中第一个参数必须是Date
或Number
对象。
public Date parse(String string)
方法可以利用指定规则将字符串解析成Date
对象。
java.text.SimpleDateFormat
:
以本地化敏感的方式格式化/解析时间数据,可以使用format
把Date
转成String
,也可以使用parse
把String
转成Date
。
Symbol | Meaning | Kind | Example |
{@code D} | day in year | (Number) | 189 |
{@code E} | day of week | (Text) | {@code E}/{@code EE}/{ |
{@code F} | day of week in month | (Number) | 2 (2nd Wed in July) |
{@code G} | era designator | (Text) | AD |
{@code H} | hour in day (0-23) | (Number) | 0 |
{@code K} | hour in am/pm (0-11) | (Number) | 0 |
{@code L} | stand-alone month | (Text) | {@code L}:1 {@code LL} |
{@code M} | month in year | (Text) | {@code M}:1 {@code MM} |
{@code S} | fractional seconds | (Number) | 978 |
{@code W} | week in month | (Number) | 2 |
{@code Z} | time zone (RFC 822) | (Time Zone) | {@code Z}/{@code ZZ}/{ |
{@code a} | am/pm marker | (Text) | PM |
{@code c} | stand-alone day of week | (Text) | {@code c}/{@code cc}/{ |
{@code d} | day in month | (Number) | 10 |
{@code h} | hour in am/pm (1-12) | (Number) | 12 |
{@code k} | hour in day (1-24) | (Number) | 24 |
{@code m} | minute in hour | (Number) | 30 |
{@code s} | second in minute | (Number) | 55 |
{@code w} | week in year | (Number) | 27 |
{@code y} | year | (Number) | {@code yy}:10 {@code y |
{@code z} | time zone | (Time Zone) | {@code z}/{@code zz}/{ |
{@code ‘} | escape for text | (Delimiter) | {@code ‘Date=’}:Date= |