使用 java 7 来回将毫秒转换为日期字符串
Convert milliseconds to date string back and forth using java 7
我有以下代码,它使用了类似问题中讨论的所有建议。
public class DateUtils {
static String secondsToDate(String seconds) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(Long.parseLong(seconds) * 1000);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
return String.format("%d-%d-%d", year, month, day);
}
static String dateToSeconds(String date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parsed = format.parse(date);
long timeInMillis = parsed.getTime();
return Long.toString(timeInMillis / 1000);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String timestamp = "1409515200";
String date = secondsToDate(timestamp);
String timestamp2 = dateToSeconds(date);
System.out.printf("%s %s", timestamp, timestamp2);
}
}
代码的结果:
1409515200 1406836800
如您所见,来回转换不起作用。怎么了?
你的问题是四舍五入。在第一种方法中,您将时间戳(即从 1970 年开始的 毫秒 的数量)转换为日期。您现在只获取日期,丢弃小时、分钟、秒和毫秒并将其转换回来。这意味着您将始终拥有丢弃的数量差异(在 00:00:00:000 的 0 和 23:59:59:999 的 86400000 之间)。要修复它,只需更改您的日期格式以包含精确到毫秒的小时数。
正确,应该接受。
一些进一步的提示……
使用日期时间 classes 作为日期时间值,而不是字符串。使用日期时间对象执行业务逻辑,并在代码中传递此类对象而不是字符串。
避免将日期时间跟踪为纪元计数。当您确实需要序列化为文本时,请使用 ISO 8601 标准定义的明确且易于阅读的格式,例如 2016-05-09T16:47:54Z
.
您正在使用旧的、麻烦的遗留 classes,它们已被 java.time framework built into Java 8 and later. Much of that functionality has been back-ported to Java 6 & 7 in the ThreeTen-Backport project, and further adapted for Android in the ThreeTenABP 项目取代。
使用 java.time classes 会让你的工作更轻松,你的代码更容易理解,不太可能遇到问题中出现的混乱。
Instant
是 UTC 时间轴上的一个时刻,分辨率为纳秒。 class 提供了一个方便的工厂方法 ofEpochSecond
,因此无需乘以一千毫秒。
String input = "1409515200";
long seconds = Long.parseLong( input );
Instant instant = Instant.ofEpochSecond( seconds );
得到wall-clock time for some locality, assign a time zone to get a ZonedDateTime
.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
要生成标准 ISO 8601 格式的字符串,请调用 toString
。请注意,此方法扩展了该格式以在方括号中附加时区名称。例如,2007-12-03T10:15:30+01:00[Europe/Paris]
.
String output = zdt.toString();
要获取问题中的仅限日期,请提取一个 LocalDate
对象。
LocalDate localDate = zdt.toLocalDate();
从那里您可以确定一天的第一时刻。第一时刻并不总是一天中的时间 00:00:00.0
,因此让 java.time 确定。
ZonedDateTime zdtStartOfDay = localDate.atStartOfDay( zoneId );
要获得问题中看到的两个长整数秒数,从我们的每个 ZonedDateTime
对象中提取一个 Instant
,并询问自纪元以来的秒数。请注意,您可能会丢失数据,因为 ZonedDateTime
/Instant
对象可以存储分辨率高达纳秒的值。从纪元开始要求整秒的调用意味着任何一小部分秒都被截断了。
long seconds1 = zdt.toInstant().getEpochSecond();
long seconds2 = zdtStartOfDay.toInstant().getEpochSecond();
我有以下代码,它使用了类似问题中讨论的所有建议。
public class DateUtils {
static String secondsToDate(String seconds) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(Long.parseLong(seconds) * 1000);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
return String.format("%d-%d-%d", year, month, day);
}
static String dateToSeconds(String date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parsed = format.parse(date);
long timeInMillis = parsed.getTime();
return Long.toString(timeInMillis / 1000);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String timestamp = "1409515200";
String date = secondsToDate(timestamp);
String timestamp2 = dateToSeconds(date);
System.out.printf("%s %s", timestamp, timestamp2);
}
}
代码的结果:
1409515200 1406836800
如您所见,来回转换不起作用。怎么了?
你的问题是四舍五入。在第一种方法中,您将时间戳(即从 1970 年开始的 毫秒 的数量)转换为日期。您现在只获取日期,丢弃小时、分钟、秒和毫秒并将其转换回来。这意味着您将始终拥有丢弃的数量差异(在 00:00:00:000 的 0 和 23:59:59:999 的 86400000 之间)。要修复它,只需更改您的日期格式以包含精确到毫秒的小时数。
一些进一步的提示……
使用日期时间 classes 作为日期时间值,而不是字符串。使用日期时间对象执行业务逻辑,并在代码中传递此类对象而不是字符串。
避免将日期时间跟踪为纪元计数。当您确实需要序列化为文本时,请使用 ISO 8601 标准定义的明确且易于阅读的格式,例如 2016-05-09T16:47:54Z
.
您正在使用旧的、麻烦的遗留 classes,它们已被 java.time framework built into Java 8 and later. Much of that functionality has been back-ported to Java 6 & 7 in the ThreeTen-Backport project, and further adapted for Android in the ThreeTenABP 项目取代。
使用 java.time classes 会让你的工作更轻松,你的代码更容易理解,不太可能遇到问题中出现的混乱。
Instant
是 UTC 时间轴上的一个时刻,分辨率为纳秒。 class 提供了一个方便的工厂方法 ofEpochSecond
,因此无需乘以一千毫秒。
String input = "1409515200";
long seconds = Long.parseLong( input );
Instant instant = Instant.ofEpochSecond( seconds );
得到wall-clock time for some locality, assign a time zone to get a ZonedDateTime
.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
要生成标准 ISO 8601 格式的字符串,请调用 toString
。请注意,此方法扩展了该格式以在方括号中附加时区名称。例如,2007-12-03T10:15:30+01:00[Europe/Paris]
.
String output = zdt.toString();
要获取问题中的仅限日期,请提取一个 LocalDate
对象。
LocalDate localDate = zdt.toLocalDate();
从那里您可以确定一天的第一时刻。第一时刻并不总是一天中的时间 00:00:00.0
,因此让 java.time 确定。
ZonedDateTime zdtStartOfDay = localDate.atStartOfDay( zoneId );
要获得问题中看到的两个长整数秒数,从我们的每个 ZonedDateTime
对象中提取一个 Instant
,并询问自纪元以来的秒数。请注意,您可能会丢失数据,因为 ZonedDateTime
/Instant
对象可以存储分辨率高达纳秒的值。从纪元开始要求整秒的调用意味着任何一小部分秒都被截断了。
long seconds1 = zdt.toInstant().getEpochSecond();
long seconds2 = zdtStartOfDay.toInstant().getEpochSecond();