如何在Java中将currentTimeMillis转换为日期?

170

我有一个服务器上生成的特定日志文件中包含毫秒数,我也知道该日志文件生成的语言环境,我的问题是将毫秒数按指定格式转换为日期。该日志的处理发生在位于不同时区的服务器上。当使用“SimpleDateFormat”进行转换时,程序会采用机器的日期,因此格式化后的日期不代表服务器的正确时间。有没有一种优雅的方式来处理这个问题?

long yourmilliseconds = 1322018752992l;
        //1322018752992-Nov 22, 2011 9:25:52 PM 

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS",Locale.US);

GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("US/Central"));
calendar.setTimeInMillis(yourmilliseconds);

System.out.println("GregorianCalendar -"+sdf.format(calendar.getTime()));

DateTime jodaTime = new DateTime(yourmilliseconds, 
                    DateTimeZone.forTimeZone(TimeZone.getTimeZone("US/Central")));
DateTimeFormatter parser1 = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss,SSS");

System.out.println("jodaTime "+parser1.print(jodaTime));

输出:

Gregorian Calendar -2011-11-23 08:55:52,992
jodaTime 2011-11-22 21:25:52,992

1
不要混淆Locale和时区。它们是完全独立的。Locale确定月份和日期的名称以及诸如月份、日期等部分的顺序等人类语言和文化规范。时区是相对于UTC的偏移量,加上处理夏令时等异常情况的规则。 - Basil Bourque
问题是毫秒数不是从1970-01-01T00:00:00Z开始计算的,而是从其他某个时刻开始计算的吗? - Basil Bourque
FYI,可怕的旧日期时间类(GregorianCalendarSimpleDateFormat等)和优秀的Joda-Time库现已被现代的java.time类取代。 - Basil Bourque
13个回答

284

您可以使用java.util.Date类,然后使用SimpleDateFormat格式化Date

Date date=new Date(millis);
我们可以使用Java SE 8中引入的DateTime API - java.time包(教程)。
var instance = java.time.Instant.ofEpochMilli(millis);
var localDateTime = java.time.LocalDateTime
                        .ofInstant(instance, java.time.ZoneId.of("Asia/Kolkata"));
var zonedDateTime = java.time.ZonedDateTime
                            .ofInstant(instance,java.time.ZoneId.of("Asia/Kolkata"));

// 格式化日期

var formatter = java.time.format.DateTimeFormatter.ofPattern("u-M-d hh:mm:ss a O");
var string = zonedDateTime.format(formatter);

2
不是所有的 java.util.Date 方法都已经过时了。(在 JDK8 的 java.time 中有改进的日期和时间 API) - KV Prajapati
1
@RafaelZeffa,'''Date(long date)''' 构造函数未被弃用。 - gokan
提供Date对象是为了向后兼容。最好按照上面建议使用Calendar。 - Ton Snoei
3
FYI,诸如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat等旧的日期时间类现已被标记为遗留系统,并被内置于Java 8及更高版本中的java.time类所取代。请参阅Oracle的教程 - Basil Bourque

136
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timeStamp);

int mYear = calendar.get(Calendar.YEAR);
int mMonth = calendar.get(Calendar.MONTH);
int mDay = calendar.get(Calendar.DAY_OF_MONTH);

我不得不将Calendar设置为final才能使它工作。 final Calendar calendar = Calendar.getInstance(); - Victor Augusto
5
通常认为日历对象比较庞大,所以应尽可能避免使用。如果日期对象具备您所需的功能,则使用日期对象会更好。由用户AVD在其他答案中提供的"Date date=new Date(millis);"将是最佳选择 :) - Dick Lucas
1
不知何故,对于1515436200000l,我得到了calendar.get(Calendar.MONTH)的值为0 - Sajib Acharya
1
值得一提的是,像 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 等老旧的日期时间类现已被称为 遗留系统,并被 Java 8 及更高版本内置的 java.time 类所取代。请参阅 Oracle 的 教程 - Basil Bourque
2
@SajibAcharya 注意,Calendar.MONTH的实例从0开始;您需要加1才能得到我们所知道的“真正”月份。例如:0 = 一月,1 = 二月,2 = 三月等。 - shagberg

26

简而言之

Instant.ofEpochMilli( 1_322_018_752_992L )     // Parse count of milliseconds-since-start-of-1970-UTC into an `Instant`.
       .atZone( ZoneId.of( "Africa/Tunis" ) )  // Assign a time zone to the `Instant` to produce a `ZonedDateTime` object.

细节

其他答案使用过时或不正确的类。

避免使用旧的日期时间类,例如java.util.Date/.Calendar。它们被证明设计不佳、令人困惑和麻烦。

java.time

The java.time framework is integrated into Java 8 and later versions. Many of its features are backported to Java 6 & 7 and adapted to Android. It was developed by some of the same people who created Joda-Time.
An Instant represents a specific moment on the timeline in UTC with a precision of nanoseconds. Its epoch is the first moment of 1970 in UTC.
假设您的输入数据是从1970-01-01T00:00:00Z以毫秒为单位计数的(问题中不太清楚),那么我们可以轻松地实例化一个Instant
Instant instant = Instant.ofEpochMilli( 1_322_018_752_992L );

instant.toString(): 2011-11-23T03:25:52.992Z

在标准的ISO 8601格式化字符串中,Z代表Zulu,表示UTC

使用正确的时区名称来应用时区,以获取ZonedDateTime

ZoneId zoneId = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( zoneId );

请查看这个在IdeOne.com上实时运行的代码

Asia/Kolkata时区?

我猜你的代码受到了印度时区的影响。我们可以看到,调整为Asia/Kolkata时区后,报告的时间与您报告的相同,08:55,比我们的UTC值03:25快五个半小时。

2011-11-23T08:55:52.992+05:30[Asia/Kolkata]

默认时区

您可以应用JVM的当前默认时区。请注意,在运行时期间默认值可能会发生更改。JVM中任何应用程序的任何线程中的任何代码都可以更改当前默认设置。如果重要,请询问用户其所需/预期的时区。

ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

关于 java.time

java.time 框架内置于 Java 8 及以上版本中。这些类取代了老旧的遗留日期时间类,如 java.util.Date, Calendar, 和 SimpleDateFormat,使得日期和时间的处理更加简单易用。

Joda-Time项目现在处于维护模式,建议迁移到java.time类。

想要了解更多,请查看Oracle教程。在Stack Overflow上搜索许多示例和解释。规范为JSR 310

使用符合JDBC 4.2或更高版本的JDBC驱动程序,您可以直接与数据库交换java.time对象,无需使用字符串或java.sql.*类。

如何获取java.time类?

这个 ThreeTen-Extra 项目扩展了 java.time 的额外类。该项目是 java.time 可能未来添加的试验场。您可能会在这里找到一些有用的类,例如 IntervalYearWeekYearQuarter更多

将您上面推荐的“Instant”转换为设备默认的本地时区(例如智能手机)是否容易? - AJW
1
@AJW 是的。调用 ZoneId.systemDefault()。请参见我回答末尾新增的部分。 - Basil Bourque
太好了,我会试一试。 - AJW

14

最简单的方法是使用Joda DateTime类,并指定毫秒级时间戳和所需的DateTimeZone。

我强烈建议避免使用内置的Java Date和Calendar类,它们非常糟糕。


格里高利历无法正常工作...由于某种原因,它采用了系统默认的时区。Joda运行得非常完美。 - Amit
请看一下示例代码,让我知道我们是否可以在公历方面做些不同的事情。 - Amit
1
FYI,Joda-Time 项目现已进入维护模式,建议迁移到java.time类。请参阅Oracle的教程 - Basil Bourque

13
如果 millis 值是自 1970 年 1 月 1 日 GMT 以来的毫秒数,这是 JVM 的标准,那么它是独立于时区的。如果您想使用特定的时区格式化它,您可以将其转换为 GregorianCalendar 对象并设置时区。之后有许多方法可以对其进行格式化。

1
这是使用GregorianCalendar和Joda的示例代码,我在Joda中获得了正确的输出,但在Gregorian中没有。 GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("US/Central")); calendar.setTimeInMillis(yourmilliseconds); DateTime jodaTime = new DateTime(yourmilliseconds,DateTimeZone.forTimeZone(TimeZone.getTimeZone("US/Central"))); DateTimeFormatter parser1 = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss,SSS"); - Amit
在日历对象中设置时区无效,但在日期格式化器对象中设置有效? - BillRobertson42
DateTime和DateTimeFormatter是什么? - SweetWisher ツ

13

我的解决方案

public class CalendarUtils {

    public static String dateFormat = "dd-MM-yyyy hh:mm";
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);

    public static String ConvertMilliSecondsToFormattedDate(String milliSeconds){
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(Long.parseLong(milliSeconds));
        return simpleDateFormat.format(calendar.getTime());
    }
}

7

最简单的方法:

private String millisToDate(long millis){

    return DateFormat.getDateInstance(DateFormat.SHORT).format(millis);
    //You can use DateFormat.LONG instead of SHORT

}

7
我是这样做的:

static String formatDate(long dateInMillis) {
    Date date = new Date(dateInMillis);
    return DateFormat.getDateInstance().format(date);
}

您还可以使用getDateInstance(int style),并使用以下参数:

DateFormat.SHORT

DateFormat.MEDIUM

DateFormat.LONG

DateFormat.FULL

DateFormat.DEFAULT

这些参数可用于格式化日期,并根据您的需求显示不同的日期格式。

5

FYI,自从采用JSR 310以来,那些可怕的类现在已被现代java.time类所取代。 - Basil Bourque

3

您可以尝试使用java.time api;

        Instant date = Instant.ofEpochMilli(1549362600000l);
        LocalDateTime utc = LocalDateTime.ofInstant(date, ZoneOffset.UTC);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接