我正在尝试使用Jodatime显示特定时间段内的交易。
我们的服务器要求开始日期和结束日期为UTC(这可能是显而易见的)。因此,围绕这些的任何业务逻辑都使用DateTime对象,时区设置为DateTimeZone.UTC
,例如
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时间戳?
答案 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分〇〇秒
要获得您的范围,您可以使用上述方法之一加上DateTime
(mStartDate
和mEndDate
),并使用DateTimeFormatter
更改为你需要什么样的格式。
PS:我认为您在使用getMillis()
时缺少的是日期时间(以UTC和默认时区为单位)代表同一时刻。你只是将这一瞬间转换为当地时间,但毫秒是相同的(想想,此时此刻,世界上的每个人都处于同一时刻(相同的毫秒),但他们的当地时间可能会有所不同取决于他们在哪里)。因此,在将UTC DateTime
转换为另一个时区时,我们只是找到该区域中的当地时间,该时间对应于相同的毫秒数。
您可以在两个对象上使用getMillis()
方法进行检查:
System.out.println(mStartDate.getMillis()); // 1497312000000
System.out.println(mStartDate.withZone(DateTimeZone.getDefault()).getMillis()); // 1497312000000
请注意,即使我将对象转换为另一个时区,millis也保持不变(1497312000000
)。那是因为两者都代表同一时刻,我只是将它们移动到另一个时区,其中各自的当地时间不同。
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)
Joda-Time项目现在处于维护模式,项目建议迁移到 java.time 类。
我们的服务器要求开始日期和结束日期为UTC(这可能是显而易见的)。
是的,将UTC用于大部分业务逻辑,以及记录,存储和交换日期时间值。将UTC视为唯一的真实时间,其他区域仅仅是变化。仅在业务逻辑中的特定规则需要时应用时区或向用户显示时区。
对于UTC中的值,请使用Instant
。 Instant
类代表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格式。对于其他格式以及对其他区域的调整,请使用上面的代码应用于每个Instant
和ZoneId
以生成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 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance 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的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。