UTC使用JodaTime以毫秒为单位的当地时间

时间:2017-06-13 09:45:54

标签: java android datetime jodatime datetime-conversion

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

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

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

除了显示时,我不知道如何为本地(系统默认)时区增加它,这很有效。理想情况下,我想使用传递两个本地时间戳的DateUtils formatDateRange函数。但getMillis()函数似乎没有考虑到本地偏移:

Timestamps returning same milisecond times

我也试过这个:

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

但它没有任何区别。所以我的问题是如何才能获得格式良好的本地日期范围和2个UTC时间戳?

2 个答案:

答案 0 :(得分:6)

如果您的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),因此会相应调整。

如果您想获得时间(小时/分钟/秒),可以使用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分〇〇秒

要获得您的范围,您可以使用上述方法之一加上DateTimemStartDatemEndDate),并使用DateTimeFormatter更改为你需要什么样的格式。

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

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

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

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

Java新日期/时间API

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

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

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

如果您使用的是 Java< = 7 ,则可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。对于 Android ,有一种方法可以使用ThreeTenABP(更多关于如何使用它here)。

以下代码适用于两者。 唯一的区别是包名称(在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(),在我的情况下会返回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分〇〇秒

答案 1 :(得分:1)

java.time

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

  

我们的服务器要求开始日期和结束日期为UTC(这可能是显而易见的)。

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

对于UTC中的值,请使用InstantInstant类代表UTC中时间轴上的一个时刻,分辨率为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 );

字符串

  

所以我的问题是如何才能获得格式精美的本地日期范围和2个UTC时间戳?

如上所示,在从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 [太平洋/奥克兰]

要生成其他格式的String,请定义格式设置模式。或者让 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 janvier2018à14:23:45heureavancéedela Nouvelle-Zélande

请注意,时区 Locale无关。一个是内容,另一个是演示。

间隔

当表示一对时刻,几个开始 - 停止时刻时,请使用 ThreeTen-Extra 库中的Interval类(链接如下)。此类表示一对Instant个对象。

toString方法生成的字符串为标准ISO 8601格式。对于其他格式以及对其他区域的调整,请使用上面的代码应用于每个InstantZoneId以生成ZonedDateTime。通过Instant&和getStart访问每个getEnd Interval interval = Interval.of( start , stop ) ;

java.sql.*
  

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

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要{{1}}类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore