UTC 到本地时间(以 Milli 为单位)

UTC to local time in millis using JodaTime

我正在尝试使用 Jodatime 显示特定时间段内的交易。

我们的服务器要求开始日期和结束日期为 UTC(这可能很明显)。因此,围绕这些的任何业务逻辑都使用时区设置为 DateTimeZone.UTC 的 DateTime 对象,例如

mStartDate = DateTime.now(UTC).withTimeAtStartOfDay();

除了涉及到 显示 时,效果很好,我不知道如何针对本地(系统默认)时区增加它。理想情况下,我想使用 DateUtils formatDateRange 函数传入两个本地时间戳。但是 getMillis() 函数似乎没有考虑局部偏移:

我也试过这个:

mTimePeriodTitle.setText(DateUtils.formatDateRange(mContext, f, mStartDate.getMillis(),
    mEndDate.getMillis(), DateUtils.FORMAT_SHOW_TIME,
    TimeZone.getDefault().getID()).toString());

但这并没有什么不同。所以我的问题是如何获得具有 2 个 UTC 时间戳的格式良好的本地日期范围?

如果您的 DateTime 是 UTC 并且您想将其转换为其他时区,您可以使用 withZone 方法进行转换。

对于下面的示例,我的默认时区是 America/Sao_Paulo(您可以使用 DateTimeZone.getDefault() 检查您的时区):

// create today's date in UTC
DateTime mStartDate = DateTime.now(DateTimeZone.UTC).withTimeAtStartOfDay();
// date/time in UTC
System.out.println(mStartDate); // 2017-06-13T00:00:00.000Z
// date/time in my default timezone (America/Sao_Paulo)
System.out.println(mStartDate.withZone(DateTimeZone.getDefault())); // 2017-06-12T21:00:00.000-03:00

输出为:

2017-06-13T00:00:00.000Z
2017-06-12T21:00:00.000-03:00

请注意,withZone 方法正确地将日期和时间转换为我的时区(在 America/Sao_Paulo 中,当前偏移量为 UTC-03:00),因此进行了相应的调整。

如果你只想得到时间(hour/minute/second),你可以使用toLocalTime()方法:

System.out.println(mStartDate.withZone(DateTimeZone.getDefault()).toLocalTime()); // 21:00:00.000

输出为:

21:00:00.000

如果您想要其他格式(例如,不打印秒的小数部分的 3 位数字),您可以使用 DateTimeFormatter。好处是你可以在格式化程序中设置时区,所以你不需要转换 DateTime:

// create formatter for hour/minute/second, set it with my default timezone
DateTimeFormatter fmt = DateTimeFormat.forPattern("HH:mm:ss").withZone(DateTimeZone.getDefault());
System.out.println(fmt.print(mStartDate)); // 21:00:00

输出为:

21:00:00

要获得范围,您可以将上述方法之一用于 DateTime 的(mStartDatemEndDate),并使用 DateTimeFormatter更改为您需要的任何格式。


PS: 我认为您在使用 getMillis() 时缺少的是两个日期时间(在 UTC 和默认时区中)代表 同一瞬间。你只是将这一瞬间转换为当地时间,但毫秒是相同的(想想,现在,此刻,世界上的每个人都在同一瞬间(相同的毫秒),但他们的当地时间可能不同取决于他们在哪里)。因此,当将 UTC DateTime 转换为另一个时区时,我们只是找到那个区域的本地时间,它对应于相同的毫秒。

您可以在两个对象上使用 getMillis() 方法进行检查:

System.out.println(mStartDate.getMillis()); // 1497312000000
System.out.println(mStartDate.withZone(DateTimeZone.getDefault()).getMillis()); // 1497312000000

请注意,即使我将对象转换为另一个时区,毫秒仍保持不变 (1497312000000)。那是因为两者代表相同的时刻,我只是将它们移动到另一个时区,那里各自的当地时间不同。


Java新Date/TimeAPI

Joda-Time 它正在停产并被新的 APIs 取代,所以我不建议用它开始一个新项目。如果是这种情况,您可以考虑使用新的 Date/Time API,但是如果您有一个使用 Joda 的大型代码库或者现在不想迁移它,您可以不考虑其余的答案。

无论如何,即使在 joda's website 它说:“请注意,Joda-Time 被认为是一个基本上“完成”的项目。没有计划进行重大改进。如果使用 Java SE 8,请迁移到 java.time (JSR-310).".*

如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs。我不确定它是否已经适用于所有 Android 版本(但请参阅下面的替代方案)。

如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's a way to use it, with the ThreeTenABP (more on how to use it ).

下面的代码适用于两者。 唯一的区别是包名称(在 Java 8 中是 java.time,在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但是 类和方法名称相同。

要以 UTC 格式获取当天开始的当前日期,您可以执行以下操作:

// UTC's today at start of the day
ZonedDateTime utc = LocalDate.now(ZoneOffset.UTC).atStartOfDay(ZoneOffset.UTC);
System.out.println(utc); // 2017-06-13T00:00Z

首先,我 LocalDate.now(ZoneOffset.UTC) 找到了 UTC 格式的当前本地日期。如果我只使用 LocalDate.now(),它会在我的默认时区中获取当前日期,这不是我们想要的(它可能与 UTC 不同,具体取决于您所在的位置和时间以及默认时区是)。

然后我使用 atStartOfDay(ZoneOffset.UTC) 在 UTC 开始一天。我知道两次使用 UTC 听起来多余,但是 API 允许我们在此方法中使用任何时区,并且 IMO 它明确了我们想要的时区(如果日期在夏令时更改的时区中,则一天的开始可能不是午夜 - 时区参数是为了保证设置正确的值。

输出为:

2017-06-13T00:00Z

要转换为我的默认时区,我可以使用 ZoneId.systemDefault(),在我的例子中是 returns America/Sao_Paulo。要转换它并仅获取本地时间部分,只需执行以下操作:

System.out.println(utc.withZoneSameInstant(ZoneId.systemDefault()).toLocalTime()); // 21:00

输出为:

21:00

如果你想改变它,你也可以使用格式化程序:

// formatter for localtime (hour/minute/second)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
System.out.println(fmt.format(utc.withZoneSameInstant(ZoneId.systemDefault()))); // 21:00:00

输出为:

21:00:00

java.time

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

区域

Our server requires a start date and end date to be in UTC (which is probably obvious).

是的,将 UTC 用于您的大部分业务逻辑,以及用于记录、存储和交换日期时间值。将 UTC 视为唯一真实时间,其他时区只是变体。仅当您的业务逻辑中的特定规则需要时或向用户展示时才应用时区。

对于 UTC 中的值,请使用 InstantInstant class represents a moment on the timeline in UTC with a resolution of nanoseconds(最多九 (9) 位小数)。

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

要查看特定时区的同一时刻,请分配一个 ZoneId 以获得一个 ZonedDateTime 对象。同一时刻,时间轴上的同一点,不同的挂钟时间。

ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;

在另一个区域看到同一时刻。

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtAuckland = instant.atZone( zAuckland );

字符串

So my question is how can I get a nicely formatted local date range with 2 UTC timestamps?

如上所示从 UTC 调整到时区后,生成字符串来表示它们的值。

要以标准 ISO 8601 格式生成表示任何这些对象的字符串,只需调用 toString

String output = instant.toString() ;

2018-01-23T01:23:45.123456Z

String output = zdtAuckland.toString() :

2018-01-23T14:23:45.123456+13:00[Pacific/Auckland]

要生成其他格式的字符串,请定义格式化模式。或者让java.time自动本地化。

要本地化,请指定:

  • FormatStyle 确定字符串的长度或缩写。
  • Locale 以确定 (a) 用于翻译日期名称、月份名称等的人类语言,以及 (b) 决定缩写、大写、标点符号、分隔符等问题的文化规范,诸如此类

示例:

Locale l = Locale.CANADA_FRENCH ; 
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );

mardi 23 janvier 2018 à 14:23:45 heure avancée de la Nouvelle-Zélande

请注意,时区与 Locale没有任何关系。一个用于内容,另一个用于展示。

区间

当表示一对时刻,一对启停时刻时,使用 ThreeTen-Extra 库中的 Interval class (链接如下)。这个 class 代表一对 Instant 对象。

toString方法生成的字符串是标准的ISO 8601格式。对于其他格式,以及对其他区域的调整,请使用上面看到的代码对每个 Instant 应用一个 ZoneId 以生成一个 ZonedDateTime。通过 getStart & getEnd.

访问每个 Instant
Interval interval = Interval.of( start , stop ) ;

interval.toString(): 2007-12-03T10:15:30/2007-12-04T10:15:30


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

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