前言
时间是一个相对地区而言的概念,因此有一个基准地区,就是本初子午线穿过的地区。了解世界时间相关的概念可以更好地协调全球人们的活动,便于跨越不同地区的时差。
本初子午线
本初子午线指的是经过地球上经度为0度的那条线,也是国际日期变更线的起点。初定本初子午线位置的是英国皇家天文学家约翰·弗林特(John Flamsteed),他将伦敦格林威治的皇家天文台(Royal Observatory, Greenwich)选作本初子午线的起点,因为当时英国是世界的海上霸主,而格林威治恰好是伦敦的郊区,方便进行观测和记录。
在1884年的国际经度会议上,格林威治被正式确定为本初子午线的起点,这也是因为当时英国在全球范围内广泛使用格林威治作为交通、航海和科学测量等方面的标准,因此该地区得到了国际公认。至此,以格林威治为本初子午线的国际标准经线得以确立。
如果当时中国强大并且有观测时间,那么本初子午线可能定在中国
东经西经
东经和西经是以英国伦敦的本初子午线为基准线进行划分的。
本初子午线(也称为格林威治子午线)是指通过英国伦敦郊区的格林威治皇家天文台天文仪器的一个子午线。所有经度都是以它为基准线进行计算的。本初子午线的经度为0度。
根据国际标准,如果一个地点位于本初子午线以东,则称其为东经,其经度为正数;如果一个地点位于本初子午线以西,则称其为西经,其经度为负数。
例如,北京位于本初子午线以东116.4度,因此其为东经116.4度;而纽约位于本初子午线以西74度,因此其为西经74度。
国际日期变更线
理解东经西经,可以脑袋里面面向世界地图球体,想象一下伦敦左边就是西经,轮动右边就是东经,东经180=西经180 =国际日期变更线,位置在太平洋上空穿过美国阿里斯加州,有个概念就行了。
有一个奇怪的现象,当你从东经坐飞机,穿越国际日期变更线 到达西经,比如从2023年4月4日13:00从北京(UTC+8)飞到美国纽约(UTC-4),历时13个小时,那么你下飞机的纽约时间是多少?下飞机的北京时间是:2023年4月5日02:00 , 对应的纽约时间时间=对应的北京时间-12小时 = 2023年4月4日14:00,感觉只花了一个小时就从北京到纽约了, 是不是感觉洲际旅行指日可待?可惜都是假象
UTC和GMT区别
UTC和GMT都是代表世界标准时间概念的缩写,不过它们有一些不同之处:
GMT:代表格林威治标准时间(Greenwich Mean Time),是最初用于航海导航的时间标准,是以英国伦敦郊区的皇家格林威治天文台的天文观测结果为基础所确定的平均太阳时,其误差仍然受到地球自转的影响。GMT是UTC的前身,两者之间的时差通常为0秒,但由于GMT是基于天文测量而得出的时间计量,因此UTC更准确和稳定。
UTC:代表协调世界时(Coordinated Universal Time),是一种由国际原子时标准所定义的时间标准。UTC是基于原子钟的精确时间计量,将世界分成24个时区,每个时区都有一个与UTC相差整数小时的本地时间。UTC是世界上最准确的时间标准之一,因为它不受地球自转速度的影响。
总的来说,UTC是目前世界上最准确的时间标准之一,而GMT则是UTC的前身,目前用于历史和文化交流等方面。两者之间的时差通常为0秒。
原子时钟
原子时钟是一种非常精确的钟表,它使用原子物理中的变化规律作为时间的基准。原子时钟通常使用铯或氢原子的振荡频率作为时间基准,这些原子的振荡频率非常稳定和准确。 相对于大约每天两秒钟的误差,原子钟误差非常小,可以达到每天一秒钟以下,而且误差可以通过对钟表进行微调来纠正。原子时钟的精度是其他钟表所无法比拟的,因此它被广泛应用于科学、技术和导航等领域。
表示时间的维度
时区
为了方便人们使用时间,将地球划分为24个时区,每个时区以15度为一小时时差,东经为正,西经为负。各个时区的时间以UTC为基础,再根据地理位置偏移若干小时,得到本地时间。例如北京是东经116.4度,(157<116.4<158) 其对应的时区是 UTC+8(东8区)
世界时间(World Time)是指全球各个地区使用的标准时间。它主要是为了协调地球上人们的活动而设立的。下面是世界时间相关的一些概念:
UTC:世界协调时(Coordinated Universal Time),是世界上所有地区所采用的标准时间,以原子钟为基础,与格林威治标准时间(GMT)保持同步,精度达到纳秒级别。
时区:为了方便人们使用,将地球划分为24个时区,每个时区以15度为一小时时差,东经为正,西经为负。各个时区的时间以UTC为基础,再根据地理位置偏移若干小时,得到本地时间。
IANA 时区标识符
IANA是Internet Assigned Numbers Authority的缩写,是全球互联网的核心机构之一,负责管理互联网的IP地址、域名系统、协议参数等。它是由互联网工程任务组(IETF)在1988年设立的,现在是互联网体系结构中最关键的组成部分之一。
IANA时区指的是由IANA维护的时区数据库,也称为tz数据库或Olson数据库。这个数据库包含了全球几乎所有的时区和夏令时规则,是操作系统、编程语言、数据库等软件中用来确定时间的基础数据。IANA时区的命名规则通常是由一个大洲或地区的名称加上一个斜杆和城市或地区的名称组成,例如“America/New_York”、“Europe/London”,“Asia/Shanghai”等
夏令时
夏令时:又称夏时制,是一种节约能源的措施。在夏令时期开始时候,时钟向前调整一小时(02:00 改成03:00),让你早点起来,晚上的日落时间晚一个小时,早上的日出时间也晚一个小时。夏令时结束时候时钟向后调整一小时(03:00 改成02:00)
计算机怎么感知这个时间调整?
计算机系统中一般都会内置一个时钟,可以根据系统设置自动同步网络上的时间服务器,linux上的ntp。当政府调整夏令时的时间时,网络时间服务器会自动更新时间信息。计算机可以通过访问网络时间服务器来获取最新的时间信息,并自动调整系统时间。因此,当政府调整夏令时时,计算机系统会自动更新时间,无需人工干预。
夏令时违背了时区的划分逻辑,并且具有地区性,因此在计算时间的时候,需要充分考虑当地时间是否是夏令时时间,比如中国2019年开始不再实行
IANA 时区标识符 和 UTC-X的区别
IANA时区标识符和UTC-X都是用来表示时区的标识符,但它们之间有一些区别。
首先,IANA时区标识符是由一系列字符串组成的标识符,用来表示全球各个地区的时区。这些字符串通常由一个大洲或地区的名称加上一个斜杆和城市或地区的名称组成,例如“America/New_York”、“Europe/London”等。这些标识符包含了有关时区和夏令时规则的详细信息。
而UTC-X则是一种简化的时区表示方法,其中X表示偏移量,表示当前时区与协调世界时(UTC)之间的时间差。例如,UTC+8表示当前时区比UTC快8个小时,而UTC-5表示当前时区比UTC慢5个小时。
另外,IANA时区标识符可以表示一个地区内的不同时区和夏令时规则,而UTC-X只能表示一个时间偏移量。
总的来说,IANA时区标识符提供了更详细和准确的时区信息,而UTC-X则提供了一种简化的时区表示方法。
比如在美国,夏令时的使用是由州政府决定的。在美国加利福尼亚州,夏令时规则是从每年3月的第二个星期日开始,到每年11月的第一个星期日结束。如果使用IANA时区标识符,可以使用“America/Los_Angeles”来表示该时区,并包含有关夏令时规则的详细信息。而如果使用UTC-X,则可以使用“UTC-7”来表示该时区,仅表示该时区与协调世界时(UTC)之间的时间差为负7小时。这个偏移量不会考虑夏令时规则,因此需要进行手动计算来确定夏令时的偏移量。
"America/Los_Angeles" 是一个IANA时区标识符,包含了夏令时规则。这是因为,美国加州使用太平洋标准时间(Pacific Standard Time, PST)和太平洋夏令时(Pacific Daylight Time, PDT)。在冬季,加州使用PST,而在夏季,加州使用PDT。而 "America/Los_Angeles" IANA时区标识符包含了这些信息,可以自动在适当的时间将时间转换为PST或PDT。这是通过在IANA时区数据库中包含夏令时规则来实现的。因此,当使用 "America/Los_Angeles" IANA时区标识符时,计算机会自动考虑夏令时规则。
太平洋时区(英语:Pacific Time Zone)在美国、加拿大、墨西哥西海岸靠近太平洋的地区使用。也被称为太平洋时间(Pacific Time,PT)。标准时间为太平洋标准时间(Pacific Standard Time,PST),UTC-8;夏令时间为太平洋夏令时(Pacific Daylight Time,PDT),UTC-7。
也就是说按照时区算,洛杉矶和北京 之间的时间差异是16个小时, 但是一旦洛杉矶启用了夏令时两者之间的时间差异只有15个小时
星期
星期:根据七日循环的周期性分配一周七天的称呼。不同地区的星期开始日期不同,例如西方的星期日为一周的开始,而伊斯兰教国家的星期五为一周的开始。中国以星期一为一周的开始
ISO 8601日期格式
ISO 8601是一种国际标准的日期时间格式,例如"2021-09-01T12:30:00Z"表示2021年9月1日12点30分在UTC时区的时间。Java中可以使用java.time.format.DateTimeFormatter类来解析和格式化ISO 8601日期时间。
JAVA中时间的表达
System.currentTimeMills()
时间戳:时间戳是指从1970年1月1日00:00:00 UTC-0 到某个时间点的秒数,与时区无关。可以使用System.currentTimeMillis()方法获取当前时间戳。同一个时间戳,在不同的时区表达的时间是不一样的, 但是都是同一时刻.
java.util.Date
java.util.Date类本身不包含时区概念。它只是一个表示自1970年1月1日00:00:00 GMT以来经过的毫秒数的基本类。它的toString()方法在输出时会把Date对象转换为当前时区的本地时间并格式化输出,但Date对象本身并没有保存时区信息。
SimpleDateFormat的输出会有时区是因为它的默认行为是将Date对象转换为当前时区的本地时间并格式化输出。例如,如果您在中国使用SimpleDateFormat格式化一个Date对象,它将使用中国标准时间(CST,UTC+8)作为默认时区。
当您使用SimpleDateFormat的.format()方法时,它会将Date对象转换为一个字符串,并根据您指定的格式进行格式化。默认情况下,SimpleDateFormat会使用当前时区来解释和格式化Date对象,如果您需要使用其他时区,可以通过调用.setTimeZone()方法来设置。
需要注意的是,SimpleDateFormat的时区设置仅适用于输出格式化的日期字符串,它并不会修改Date对象本身的时区信息。
java.text.SimpleDateFormat
java.text.SimpleDateFormat类:SimpleDateFormat类用于格式化和解析日期和时间的字符串。
这里的"yyyy-MM-dd HH:mm:ss"字符串是一个日期时间格式的模板,其中:
yyyy表示年份;
MM表示月份;
dd表示日期;
HH表示小时(24小时制);
mm表示分钟;
ss表示秒。
//格式化
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = dateFormat.format(new Date());
//解析
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parsedDate = dateFormat.parse("2019-11-01 13:45:30");
//带时区的格式化
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String formattedDate = dateFormat.format(new Date());
java.time.LocalDate
Java 8中的日期时间API中的 LocalDate 类表示日期,不包含时间和时区信息。它是不可变的,一旦创建就不能修改,类似于字符串和基本数据类型。
LocalDate currentDate =LocalDate.now(); // 当前日期,这个是从本地的系统时间上取出来的,是与当前时区相关信息时间,但是LocalDate 并不包含时区等信息.
LocalDate birthday =LocalDate.of(2023,4,5); // 指定日期,不包含时区信息.
java.time.LocalTime
java.time.LocalTime是Java 8中的一个时间类,用于表示一个不含日期和时区的时间,它包含小时、分钟、秒和纳秒四个时间单位
LocalTime now =LocalTime.now();// 获取当前时间
LocalTime time1 =LocalTime.of(12,30);// 12:30:003
LocalTime time2 =LocalTime.of(12,30,15);// 12:30:15
LocalTime time3 =LocalTime.of(12,30,15,500000000);// 12:30:15.500000000
LocalTime time =LocalTime.of(12,30,15);2DateTimeFormatter formatter =DateTimeFormatter.ofPattern("HH:mm:ss");3String timeString = time.format(formatter);// 12:30:15
java.time.LocalDateTime
java.time.LocalDateTime是Java 8中的一个时间类,用于表示一个不含时区的日期时间,它包含日期、小时、分钟、秒和纳秒五个时间单位。
LocalDateTime now =LocalDateTime.now();// 获取当前日期时间
LocalDateTime datetime1 =LocalDateTime.of(2022,6,30,23,59,59);// 2022-06-30T23:59:59
LocalDateTime datetime2 =LocalDateTime.of(2022,6,30,23,59,59,500000000);// 2022-06-30T23:59:59.500000000
LocalDateTime datetime =LocalDateTime.of(2022,6,30,23,59,59);
DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String datetimeString = datetime.format(formatter);// 2022-06-30 23:59:59
Zone系列
java.time.ZonedDateTime
public class ZonedDateTime implements
Temporal, ChronoZonedDateTime<LocalDate>, Serializable{
private final LocalDateTime dateTime;
/**
* The offset from UTC/Greenwich. ,偏移量
*/
private final ZoneOffset offset;
/**
* The time-zone. 时区
*/
private final ZoneId zone;
}
ZonedDateTime是Java 8中提供的一个日期时间类,用来处理带时区的日期和时间。ZonedDateTime是一个不可变类,它封装了LocalDateTime和ZoneId,提供了丰富的操作方法。
ZonedDateTime有以下特点:
它可以表示任何时区的时间,包括夏令时。
它提供了丰富的操作方法,比如加减时间、获取时区等。
它是线程安全的,可以在多线程环境中安全地使用。
创建ZonedDateTime对象的方式有以下几种:
使用现有的LocalDateTime和ZoneId创建:ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), ZoneId.systemDefault());
使用指定的年月日时分秒和时区创建:ZonedDateTime zonedDateTime = ZonedDateTime.of(2022, 2, 23, 12, 0, 0, 0, ZoneId.of("Asia/Shanghai"));
使用Instant创建:ZonedDateTime zonedDateTime = Instant.now().atZone(ZoneId.systemDefault());
ZonedDateTime提供了丰富的操作方法,比如加减时间、获取时区等。以下是一些常用的方法:
plus(Duration duration):加上指定的时间间隔。
minus(Duration duration):减去指定的时间间隔。
withZoneSameInstant(ZoneId zone):返回一个新的ZonedDateTime对象,表示指定时区下的同一时刻。
withZoneSameLocal(ZoneId zone):返回一个新的ZonedDateTime对象,表示指定时区下的同一本地时间。
toInstant():将ZonedDateTime转换为Instant对象。
java.time.ZondId
public abstract class ZoneId implements Serializable {
/**
* <p>
* This maps as follows:
* <ul>
* <li>EST - -05:00</li>
* <li>HST - -10:00</li>
* <li>MST - -07:00</li>
* <li>ACT - Australia/Darwin</li>
* <li>AET - Australia/Sydney</li>
* <li>AGT - America/Argentina/Buenos_Aires</li>
* <li>ART - Africa/Cairo</li>
* <li>AST - America/Anchorage</li>
* <li>BET - America/Sao_Paulo</li>
* <li>BST - Asia/Dhaka</li>
* <li>CAT - Africa/Harare</li>
* <li>CNT - America/St_Johns</li>
* <li>CST - America/Chicago</li>
* <li>CTT - Asia/Shanghai</li>
* <li>EAT - Africa/Addis_Ababa</li>
* <li>ECT - Europe/Paris</li>
* <li>IET - America/Indiana/Indianapolis</li>
* <li>IST - Asia/Kolkata</li>
* <li>JST - Asia/Tokyo</li>
* <li>MIT - Pacific/Apia</li>
* <li>NET - Asia/Yerevan</li>
* <li>NST - Pacific/Auckland</li>
* <li>PLT - Asia/Karachi</li>
* <li>PNT - America/Phoenix</li>
* <li>PRT - America/Puerto_Rico</li>
* <li>PST - America/Los_Angeles</li>
* <li>SST - Pacific/Guadalcanal</li>
* <li>VST - Asia/Ho_Chi_Minh</li>
* </ul>
* The map is unmodifiable.
*/
public static final Map<String, String> SHORT_IDS;
static {
Map<String, String> map = new HashMap<>(64);
map.put("ACT", "Australia/Darwin");
map.put("AET", "Australia/Sydney");
map.put("AGT", "America/Argentina/Buenos_Aires");
map.put("ART", "Africa/Cairo");
map.put("AST", "America/Anchorage");
map.put("BET", "America/Sao_Paulo");
map.put("BST", "Asia/Dhaka");
map.put("CAT", "Africa/Harare");
map.put("CNT", "America/St_Johns");
map.put("CST", "America/Chicago");
map.put("CTT", "Asia/Shanghai");
map.put("EAT", "Africa/Addis_Ababa");
map.put("ECT", "Europe/Paris");
map.put("IET", "America/Indiana/Indianapolis");
map.put("IST", "Asia/Kolkata");
map.put("JST", "Asia/Tokyo");
map.put("MIT", "Pacific/Apia");
map.put("NET", "Asia/Yerevan");
map.put("NST", "Pacific/Auckland");
map.put("PLT", "Asia/Karachi");
map.put("PNT", "America/Phoenix");
map.put("PRT", "America/Puerto_Rico");
map.put("PST", "America/Los_Angeles");
map.put("SST", "Pacific/Guadalcanal");
map.put("VST", "Asia/Ho_Chi_Minh");
map.put("EST", "-05:00");
map.put("MST", "-07:00");
map.put("HST", "-10:00");
SHORT_IDS = Collections.unmodifiableMap(map);
}
private static final long serialVersionUID = 8352817235686L;
}
java.util.TimeZone
sun.util.calendar.ZoneInfo
abstract public class TimeZone implements Serializable, Cloneable {
/**
* Sole constructor. (For invocation by subclass constructors, typically
* implicit.)
*/
public TimeZone() {
}
/**
* A style specifier for <code>getDisplayName()</code> indicating
* a short name, such as "PST."
* @see #LONG
* @since 1.2
*/
public static final int SHORT = 0;
/**
* A style specifier for <code>getDisplayName()</code> indicating
* a long name, such as "Pacific Standard Time."
* @see #SHORT
* @since 1.2
*/
public static final int LONG = 1;
// Constants used internally; unit is milliseconds
private static final int ONE_MINUTE = 60*1000;
private static final int ONE_HOUR = 60*ONE_MINUTE;
private static final int ONE_DAY = 24*ONE_HOUR;
// Proclaim serialization compatibility with JDK 1.1
static final long serialVersionUID = 3581463369166924961L;
}
sun.util.calendar.ZoneInfo
public class ZoneInfo extends TimeZone {
private static final int UTC_TIME = 0;
private static final int STANDARD_TIME = 1;
private static final int WALL_TIME = 2;
private static final long OFFSET_MASK = 15L;
private static final long DST_MASK = 240L;
private static final int DST_NSHIFT = 4;
private static final long ABBR_MASK = 3840L;
private static final int TRANSITION_NSHIFT = 12;
private static final CalendarSystem gcal = CalendarSystem.getGregorianCalendar();
private int rawOffset;
private int rawOffsetDiff;
private int checksum;
private int dstSavings;
private long[] transitions;
private int[] offsets;
private int[] simpleTimeZoneParams;
private boolean willGMTOffsetChange;
private transient boolean dirty;
private static final long serialVersionUID = 2653134537216586139L;
private transient SimpleTimeZone lastRule;
}
java.time.ZoneRegion
final class ZoneRegion extends ZoneId implements Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = 8386373296231747096L;
/**
* The time-zone ID, not null.
*/
private final String id;
/**
* The time-zone rules, null if zone ID was loaded leniently.
*/
private final transient ZoneRules rules;
}
java.util.Calendar
Calendar类是一个抽象类,表示一个日历,可以用于处理日期和时间的计算和比较。