从 1970 到 2020.01.01 的毫秒数
Milliseconds from 1970 to 2020.01.01
我试图获取从纪元到 2020.01.01 的毫秒数。我对 Date 使用了旧方法,我也想使用新的性感 LocalDate 但我得到的两个结果是不同的:
long millisecondsInTheDay = 24 * 60 * 60 * 1000;
long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay; // 1577836800000
long millis2 = new Date(2020 - 1900, 0, 1).toInstant().toEpochMilli(); // 1577833200000
差异正好是一小时(3600_000 毫秒)。为什么我得到不同的结果?
我不想评论为什么你会有所不同,因为我认为这两种原始方法都有问题。您需要密切注意时区之类的事情;你真的应该避免对代表日期的数值进行任何类型的算术运算。
您需要特别注意指定要在其间测量的点:如果您想要毫秒数,大概您真的想将这些点指定为时间点。 “1970”不是一瞬间,而是一年的时间; “2020-01-01”也不是一个瞬间,而是一个时间段,其含义根据时区而变化 - 在地球上的某个地方,大约有 48 小时的瞬间被认为是那个日期。
执行此操作的正确方法(假设您希望在首选时区的纪元和一天的开始之间有毫秒数)是:
Duration between =
Duration.between(
Instant.EPOCH,
LocalDate.of(2020, Month.JANUARY, 1).atStartOfDay(zoneId));
long betweenMillis = between.toMillis(); // If you must - better to keep the type information that this is a Duration.
请注意,您需要指定 zoneId
,例如ZoneId.of("Europe/Warsaw")
,因为这会影响一天的开始时间,从而影响多少毫秒。
不同时区
Why I get different result?
Joachim Sauer 已经说过:这是因为不同的时区。 millis1
是截至 2020 年 1 月 1 日在 00:00 (UTC)的毫秒数。 millis2
计算到 2020 年 1 月 1 日 00:00 在您当地的时区 ,大概是 Europe/Warsaw。在冬季,波兰与 UTC 的偏移量为 +01:00,这解释了两者之间 1 小时的差异。一切都很好。纪元是一个时间点,与时区无关。通常定义为 1970 年 1 月 1 日 00:00 UTC。
也就是说,我同意 Andy Turner 的观点,即两种计算方式都有问题。
用java.time
计算得很好
这是我的选择,当然使用 java.time,现代 Java 日期和时间 API:
ZoneId targetZone = ZoneOffset.UTC;
long millis = LocalDate.of(2020, Month.JANUARY, 1).atStartOfDay(targetZone)
.toInstant()
.toEpochMilli();
System.out.println(millis);
输出:
1577836800000
如果您确实想要自己的时区,只需更改第一行:
ZoneId targetZone = ZoneId.of("Europe/Warsaw");
1577833200000
关键是对传统和现代使用相同的时区(例如 UTC)API。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneOffset;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
long millisUsingJavaUtilDate = sdf.parse("2020.01.01")
.getTime();
long millisUsingJavaTime = LocalDate.of(2020, Month.JANUARY, 1)
.atStartOfDay(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println(millisUsingJavaUtilDate);
System.out.println(millisUsingJavaTime);
}
}
输出:
1577836800000
1577836800000
我们换个时区试试,America/New_York
:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
long millisUsingJavaUtilDate = sdf.parse("2020.01.01")
.getTime();
long millisUsingJavaTime = LocalDate.of(2020, Month.JANUARY, 1)
.atStartOfDay(ZoneId.of("America/New_York"))
.toInstant()
.toEpochMilli();
System.out.println(millisUsingJavaUtilDate);
System.out.println(millisUsingJavaTime);
}
}
输出:
1577854800000
1577854800000
了解有关现代日期时间 API 的更多信息
请注意,旧版日期时间 API(java.util
日期时间类型及其格式 API、SimpleDateFormat
)已过时且容易出错。建议完全停止使用,改用java.time
,modern date-time API* .
* 无论出于何种原因,如果您必须坚持Java 6 或Java 7,您可以使用ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and 。
你的问题在这里:
long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay; // 1577836800000
您使用 LocalDate class,它为您获取当地时间(在您的时区),而 Java 中的时间(以毫秒为单位)是 [=14= 之间经过的时间量](世界协调时间)这是(在您请求的日期,01.01.2020 00:00:00 UTC
):
1577836800000
您得到的差异是由于在您当地时间观察到的时间偏移(一小时,可能您处于欧洲中部时间 --CET--)
编辑:
顺便说一句,我在你使用的答案(和你的代码)中看到:
new Date(2020 - 1900, 0, 1);
这是非常糟糕的代码。您假设以上内容等同于日期 2020.1.1
和 1900.1.1
之间的毫秒差,实际上,它表示日期 120.1.1
的时间戳,这是时间戳第一百二十年一月一日 (a.C) new Date()
运算符中的日期之间没有分配 属性。如果这些年的持续时间都相同,这可能是真的……但事实并非如此。一个好方法是使用:
long millis = new Date(2020, 0, 1).getTime() - new Date(1900, 0, 1).getTime();
但后面的不等同于上面写的
new Date( y , m , d )
使用默认时区
其他一些答案是正确的,非常有用。但我想把你的代码出错的地方说得非常简单明了:
➥ 已弃用的 constructor of java.util.Date
for year-month-day 参数隐式应用 JVM 当前的默认时区。
取代码中关键行的第一部分:
new Date(2020 - 1900, 0, 1).toInstant()
… 根据定义,其中 Instant
始终采用 UTC(零小时-分钟-秒的偏移量)。在我的机器上,我的 JVM 中的当前默认时区是 America/Los_Angeles
。在您的日期和时间,该时区比 UTC 晚八小时。
那么让我们试试这三行代码代码:
System.out.println(
ZoneId.systemDefault()
);
System.out.println(
new Date(2020 - 1900, 0, 1)
);
System.out.println(
new Date(2020 - 1900, 0, 1).toInstant()
);
当运行时,我们确实看到new Date
所代表的时刻是那天在时区America/Los_Angeles
中看到的第一个时刻,俗称[=18] =].该日期的该区域比 UTC 晚八小时。我们可以在第三行中看到这一事实,当调用 toInstant
时已调整为 UTC,其中时间为上午 8 点。
America/Los_Angeles
Wed Jan 01 00:00:00 PST 2020
2020-01-01T08:00:00Z
避免Date
从更大的角度来看,停止使用 Date
class!
研究 java.util.Date
的行为没有任何好处。 class 绝对是可怕的,由不懂日期时间处理的人写的。与 Calendar
、java.sql.Date
和 SimpleDateFormat
一起,绝不能使用这些 classes。
这些遗留 classes 在几年前被 java.time classes 取代,在 JSR 310 中定义。Sun、Oracle、 JCP 社区一致放弃了这些 classes。你也应该。
我试图获取从纪元到 2020.01.01 的毫秒数。我对 Date 使用了旧方法,我也想使用新的性感 LocalDate 但我得到的两个结果是不同的:
long millisecondsInTheDay = 24 * 60 * 60 * 1000;
long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay; // 1577836800000
long millis2 = new Date(2020 - 1900, 0, 1).toInstant().toEpochMilli(); // 1577833200000
差异正好是一小时(3600_000 毫秒)。为什么我得到不同的结果?
我不想评论为什么你会有所不同,因为我认为这两种原始方法都有问题。您需要密切注意时区之类的事情;你真的应该避免对代表日期的数值进行任何类型的算术运算。
您需要特别注意指定要在其间测量的点:如果您想要毫秒数,大概您真的想将这些点指定为时间点。 “1970”不是一瞬间,而是一年的时间; “2020-01-01”也不是一个瞬间,而是一个时间段,其含义根据时区而变化 - 在地球上的某个地方,大约有 48 小时的瞬间被认为是那个日期。
执行此操作的正确方法(假设您希望在首选时区的纪元和一天的开始之间有毫秒数)是:
Duration between =
Duration.between(
Instant.EPOCH,
LocalDate.of(2020, Month.JANUARY, 1).atStartOfDay(zoneId));
long betweenMillis = between.toMillis(); // If you must - better to keep the type information that this is a Duration.
请注意,您需要指定 zoneId
,例如ZoneId.of("Europe/Warsaw")
,因为这会影响一天的开始时间,从而影响多少毫秒。
不同时区
Why I get different result?
Joachim Sauer 已经说过:这是因为不同的时区。 millis1
是截至 2020 年 1 月 1 日在 00:00 (UTC)的毫秒数。 millis2
计算到 2020 年 1 月 1 日 00:00 在您当地的时区 ,大概是 Europe/Warsaw。在冬季,波兰与 UTC 的偏移量为 +01:00,这解释了两者之间 1 小时的差异。一切都很好。纪元是一个时间点,与时区无关。通常定义为 1970 年 1 月 1 日 00:00 UTC。
也就是说,我同意 Andy Turner 的观点,即两种计算方式都有问题。
用java.time
计算得很好这是我的选择,当然使用 java.time,现代 Java 日期和时间 API:
ZoneId targetZone = ZoneOffset.UTC;
long millis = LocalDate.of(2020, Month.JANUARY, 1).atStartOfDay(targetZone)
.toInstant()
.toEpochMilli();
System.out.println(millis);
输出:
1577836800000
如果您确实想要自己的时区,只需更改第一行:
ZoneId targetZone = ZoneId.of("Europe/Warsaw");
1577833200000
关键是对传统和现代使用相同的时区(例如 UTC)API。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneOffset;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
long millisUsingJavaUtilDate = sdf.parse("2020.01.01")
.getTime();
long millisUsingJavaTime = LocalDate.of(2020, Month.JANUARY, 1)
.atStartOfDay(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println(millisUsingJavaUtilDate);
System.out.println(millisUsingJavaTime);
}
}
输出:
1577836800000
1577836800000
我们换个时区试试,America/New_York
:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
long millisUsingJavaUtilDate = sdf.parse("2020.01.01")
.getTime();
long millisUsingJavaTime = LocalDate.of(2020, Month.JANUARY, 1)
.atStartOfDay(ZoneId.of("America/New_York"))
.toInstant()
.toEpochMilli();
System.out.println(millisUsingJavaUtilDate);
System.out.println(millisUsingJavaTime);
}
}
输出:
1577854800000
1577854800000
了解有关现代日期时间 API 的更多信息
请注意,旧版日期时间 API(java.util
日期时间类型及其格式 API、SimpleDateFormat
)已过时且容易出错。建议完全停止使用,改用java.time
,modern date-time API* .
* 无论出于何种原因,如果您必须坚持Java 6 或Java 7,您可以使用ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and
你的问题在这里:
long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay; // 1577836800000
您使用 LocalDate class,它为您获取当地时间(在您的时区),而 Java 中的时间(以毫秒为单位)是 [=14= 之间经过的时间量](世界协调时间)这是(在您请求的日期,01.01.2020 00:00:00 UTC
):
1577836800000
您得到的差异是由于在您当地时间观察到的时间偏移(一小时,可能您处于欧洲中部时间 --CET--)
编辑:
顺便说一句,我在你使用的答案(和你的代码)中看到:
new Date(2020 - 1900, 0, 1);
这是非常糟糕的代码。您假设以上内容等同于日期 2020.1.1
和 1900.1.1
之间的毫秒差,实际上,它表示日期 120.1.1
的时间戳,这是时间戳第一百二十年一月一日 (a.C) new Date()
运算符中的日期之间没有分配 属性。如果这些年的持续时间都相同,这可能是真的……但事实并非如此。一个好方法是使用:
long millis = new Date(2020, 0, 1).getTime() - new Date(1900, 0, 1).getTime();
但后面的不等同于上面写的
new Date( y , m , d )
使用默认时区
其他一些答案是正确的,非常有用。但我想把你的代码出错的地方说得非常简单明了:
➥ 已弃用的 constructor of java.util.Date
for year-month-day 参数隐式应用 JVM 当前的默认时区。
取代码中关键行的第一部分:
new Date(2020 - 1900, 0, 1).toInstant()
… 根据定义,其中 Instant
始终采用 UTC(零小时-分钟-秒的偏移量)。在我的机器上,我的 JVM 中的当前默认时区是 America/Los_Angeles
。在您的日期和时间,该时区比 UTC 晚八小时。
那么让我们试试这三行代码代码:
System.out.println(
ZoneId.systemDefault()
);
System.out.println(
new Date(2020 - 1900, 0, 1)
);
System.out.println(
new Date(2020 - 1900, 0, 1).toInstant()
);
当运行时,我们确实看到new Date
所代表的时刻是那天在时区America/Los_Angeles
中看到的第一个时刻,俗称[=18] =].该日期的该区域比 UTC 晚八小时。我们可以在第三行中看到这一事实,当调用 toInstant
时已调整为 UTC,其中时间为上午 8 点。
America/Los_Angeles
Wed Jan 01 00:00:00 PST 2020
2020-01-01T08:00:00Z
避免Date
从更大的角度来看,停止使用 Date
class!
研究 java.util.Date
的行为没有任何好处。 class 绝对是可怕的,由不懂日期时间处理的人写的。与 Calendar
、java.sql.Date
和 SimpleDateFormat
一起,绝不能使用这些 classes。
这些遗留 classes 在几年前被 java.time classes 取代,在 JSR 310 中定义。Sun、Oracle、 JCP 社区一致放弃了这些 classes。你也应该。