如何解析日期时间并将其与相应的时区关联

时间:2019-12-24 20:16:33

标签: java scala date datetime timezone

我正在尝试读取格式为2019-12-23 13:35:00的字符串以创建DateTime对象,并将其与我知道它对应的特定时区(GMT-5)关联

我正试图通过这种方式做到这一点:

 val string1 = "2019-12-23 13:35:00"
    val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    sdf.setTimeZone(StaticDateTimeZone.forID("Etc/GMT-5").toTimeZone)
    val date = sdf.parse(string1)

输出不是我想要的,因为它不是假设datetime不在GMT-5时区,而是在系统的时区(GMT + 0)中,因此将datetime调整为GMT-5,例如:{{ 1}}

如何创建一个不会更改输入,而只是将时区与之关联的日期时间对象?

4 个答案:

答案 0 :(得分:4)

似乎您正在使用JodaTime,然后转换为java.util.Date。您可以直接使用Java 8中的java.time软件包:

val string1 = "2019-12-23 13:35:00"
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val date = LocalDateTime.parse(string1, formatter)
date.atZone(ZoneId.of("Etc/GMT-5"))

给出:

date: java.time.LocalDateTime = 2019-12-23T13:35
res0: java.time.ZonedDateTime = 2019-12-23T13:35+05:00[Etc/GMT-5]

LocalDateTime产生一个没有附加时区的日期。最后一行从上一个日期有效地创建了一个新的ZonedDateTime,并在其顶部添加了给定的时区。如果您需要返回到java.util.Date,请参见this answer

答案 1 :(得分:4)

(使用Java语法而不是Scala)

tl; dr

  

我知道它对应的特定时区(GMT-5)

GMT-5不是时区,而是纯偏移量。时区的名称为Continent/Region,例如Africa/Tunis。时区是区域使用的偏移量变化的历史记录。

不能根据偏移量可靠地确定时区。许多时区在特定时刻可能共享相同的偏移量,但在其他时刻可能有所不同。因此,您只会做出疯狂的猜测。仅当确定地知道特定时区时,才应用时区。

相反,使用偏移量本身来确定不带区域的时刻。这是一线解决方案。

LocalDateTime                                     // Represent a date with time-of-day but lacking the context of an offset-from-UTC or a time zone.
.parse(                                           // When parsing, no need to specify a formatting pattern when the input complies with the ISO 8601 standard.        
    "2019-12-23 13:35:00".replace( " " , "T" )    // Comply with ISO 8601 standard formatting, replacing SPACE in the middle with an uppercase `T`. 
)                                                 // Returns a `LocalDateTime` object.
.atOffset(                                        // Determine a moment by placing the date and time-of-day of a `LocalDateTime` into the context of an offset-from-UTC.
    ZoneOffset.ofHours( -5 )                      // Represent an offset-from-UTC with `ZoneOffset`, merely a number of hours-minutes-seconds ahead or behind the prime meridian. 
)                                                 // Returns a `OffsetDateTime` object.
.toString()                                       // Generate text in standard ISO 8601 format.

Run code live

  

2019-12-23T13:35-05:00

java.time

ISO 8601

您的输入字符串几乎采用ISO 8601标准格式。为了符合要求,请将中间的空格替换为T

String input = "2019-12-23 13:35:00".replace( " " , "T" ) ;

LocalDateTime

鉴于您的输入缺少时区或自UTC偏移,请解析为LocalDateTime

LocalDateTime ldt = LocalDateTime.parse( input ) ;

请注意,LocalDateTime不是时间,不是时间轴上的一点。在您大约下午1:30的示例中,我们不知道这是否意味着日本东京为1:30 PM或美国俄亥俄州托莱多为1:30 PM,这两个小时相差很远。

OffsetDateTime

您声称肯定确定要通过UTC(通常在本初子午线以西)之后五小时的偏移量来观看此日期和时间。

ZoneOffset offset = ZoneOffset.ofHours( -5 ) ;  // Five hours before UTC.

因此,我们将ZoneOffset应用于OffsetDateTime

OffsetDateTime odt = ldt.atOffset( offset ) ;

请参阅此code run live at IdeOne.com

  

odt.toString():2019-12-23T13:35-05:00

ZonedDateTime

时区总是比仅从UTC偏移更好。偏移量是小时-分钟-秒,仅此而已。时区更多。时区是特定区域的人们过去,现在和将来对偏移量的更改的历史记录。

但不要在某个时区猜测。仅当您知道LocalDateTime对象最初用于该区域时,才应用区域。

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4个字母的缩写,例如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Panama" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

Table of date-time types in Java, both modern and legacy


关于 java.time

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

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

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

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

例如,我编写了一个方法:

    public static Date getDateWithTimeZone(String dateString, String format, String timeZone) {
        DateFormat df = new SimpleDateFormat(format);
        Date dateObj = null;

        try {
            dateObj = df.parse(dateString);
            DateTime dateTime = new DateTime(dateObj);
            TimeZone zone = TimeZone.getTimeZone(timeZone);
            return dateTime.withZoneRetainFields(DateTimeZone.forTimeZone(zone))
                    .toDate();
        } catch (ParseException e) {
            logger.error("Error while converting string to date",e);
        }
        return null;
    }
  • 您可以根据需要更改方法/异常处理。 希望这会有所帮助。

答案 3 :(得分:0)

您可以执行以下操作:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "2019-12-23 13:35:00";
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt = LocalDateTime.parse(strDateTime, format);
        ZoneId gmtZoneId = ZoneId.of("UTC+00:00");
        ZoneId myZoneId = ZoneId.of("UTC-05:00");
        System.out.println(format.format(ldt.atZone(gmtZoneId).withZoneSameInstant(myZoneId)));
    }
}

输出:

2019-12-23 08:35:00

有用的文档:

  1. https://docs.oracle.com/javase/9/docs/api/java/time/LocalDateTime.html
  2. https://docs.oracle.com/javase/9/docs/api/java/time/ZonedDateTime.html
  3. https://docs.oracle.com/javase/9/docs/api/java/time/format/DateTimeFormatter.html

You should not use SimpleDateFormat.

[更新]

根据Basil Bourque的评论发布以下代码:

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "2019-12-23 13:35:00";
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt = LocalDateTime.parse(strDateTime, format);
        System.out.println(format.format(ldt.atZone(ZoneOffset.of("+00:00")).withZoneSameInstant(ZoneOffset.of("-05:00"))));
    }
}

输出:

2019-12-23 08:35:00