从1970到2020.01.01的毫秒数

dba5bblo  于 2021-07-09  发布在  Java
关注(0)|答案(5)|浏览(350)

我试着从epoch到2020.01.01的毫秒。我对date使用了旧方法,还想使用新的sexy 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毫秒)。为什么我得到不同的结果?

vxqlmq5t

vxqlmq5t1#

我不想评论为什么会有不同,因为我认为两种原始方法都有问题。你需要密切关注诸如时区之类的事情;你真的应该避免对代表日期的数值做任何算术运算。
您需要特别注意指定要测量的时间点:如果您需要毫秒数,那么您可能真的希望将这些点指定为时间上的瞬间。”1970年“不是一瞬间,而是一年的时间“2020-01-01”也不是一个瞬间,而是一个时间段,它的意义随着时区的变化而变化——在地球上的某个地方,大约有48小时的瞬间被认为是这个日期。
正确的方法是(假设您希望在首选时区的epoch和一天的开始之间有毫秒):

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") ,因为这会影响一天的开始时间,从而影响一天的毫秒数。

wsewodh2

wsewodh22#

关键是对传统api和现代api使用相同的时区(例如utc)。

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

从trail:date-time了解有关现代日期时间api的更多信息。
请注意,遗留的日期时间api( java.util 日期时间类型及其格式api, SimpleDateFormat )过时且容易出错。建议完全停止使用并切换到 java.time ,现代日期时间api*。

  • 出于任何原因,如果您必须坚持使用Java6或Java7,您可以使用threeten backport,它将大部分java.time功能向后移植到Java6和Java7。如果您正在为一个android项目工作,并且您的android api级别仍然不符合java-8,请检查通过desugaring提供的java8+api以及如何在android项目中使用threetenabp。
kmbjn2e3

kmbjn2e33#

新日期(y,m,d)使用默认时区

其他一些答案是正确的,非常有用。但我想简单明了地说明你的代码哪里出错了:
➥ 已弃用的构造函数 java.util.Date 对于年-月-日参数隐式地应用jvm的当前默认时区。
取代码中关键行的第一部分:

new Date(2020 - 1900, 0, 1).toInstant()

…一个 Instant 根据定义,总是以utc(零小时分秒的偏移量)为单位。在我的机器上,jvm中的当前默认时区是 America/Los_Angeles . 在你的日期和时间,这个区域比utc晚了8个小时。
让我们试试下面三行代码:

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 ,俗称 PST . 那天的区域比utc晚了8个小时。我们可以在第三行看到这个事实,当我们打电话的时候 toInstant 已调整为utc,其中一天的时间是上午8点。
美国/洛杉矶
太平洋标准时间2020年1月1日星期三00:00:00
2020-01-01t08:00:00z

避免日期

从大局来看,停止使用 Date 同学们!
研究一个人的行为是没有好处的 java.util.Date . 那个类是绝对可怕的,写的人谁不明白日期时间处理。随着 Calendar , java.sql.Date ,和 SimpleDateFormat ,不应使用这些类。
这些遗留类在几年前被jsr310中定义的java.time类所取代。sun、oracle和jcp社区一致放弃了这些课程。你也应该这样。

wwodge7n

wwodge7n4#

不同的时区

为什么我得到不同的结果?
约阿希姆Solr已经说过了:这是因为不同的时区。 millis1 是到2020年1月1日00:00(utc)的毫秒计数。 millis2 直到2020年1月1日00:00在您当地的时区,大概是欧洲/华沙。在冬季,波兰与utc的偏移量为+01:00,这解释了两者相差1小时的原因。一切都很顺利。纪元是一个时间点,与时区无关。它通常被定义为1970年1月1日00:00 utc。
我同意安迪·特纳的观点,这两种计算方法都有问题。

一个很好的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

dohp0rv5

dohp0rv55#

你的问题是:

long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay; // 1577836800000

您使用localdate类,它为您获取本地时间(在您的时区中),而java中的时间(以毫秒为单位)是两个时间段之间经过的时间量 01.01.1970 UTC (世界协调时间)这是(在您要求的日期, 01.01.2020 00:00:00 UTC ):

1577836800000

你得到的差异是由于在当地时间观察到的时间偏移(一个小时,可能你在中欧时间——cet--)

相关问题