带时区的datetime格式

iswrvxsc  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(547)

我需要将具有用户指定的区域设置和时区的任何传入日期时间字符串解析为唯一的模式,以便稍后将其正确存储在数据库中:

String inputDatetime = "Mon Dec 21 21:18:37 GMT 2020";
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withLocale(Locale.getDefault()).withZone(ZoneOffset.UTC);
TemporalAccessor date = fmt.parse(inputDatetime);

但我得到以下错误:

java.time.format.DateTimeParseException: Text 'Mon Dec 21 21:18:37 GMT 2020' could not be parsed at index 0

这个代码有什么问题?

ndasle7k

ndasle7k1#

假设你的数据库有 timestamp with time zone 数据类型,这是您应该用来存储字符串中的日期和时间。您的输入字符串明确地定义了一个时间点,也定义了一个时间点 timestamp with time zone .
接下来,不应将日期时间存储为数据库喜欢的特定格式的字符串。存储适当的日期时间对象。由于JDBC4.2,这意味着对象的类型来自java.time,这是您已经在使用的现代java日期和时间api。那你就不需要关心格式了。一切都为你准备好了。如果数据库数据类型为 timestamp with time zone ,存储 OffsetDateTime 进入那个专栏。相反,它是 timestamp 没有时区或 datetime ,而不是存储 LocalDateTime . jdbc驱动程序的文档应该会提供更多的细节。

这个代码有什么问题?

我发现你的代码有不止一个问题。
正如您在评论中所说的,您正在尝试使用带模式的格式化程序来解析字符串 yyyy-MM-dd HH:mm:ss ,但你的线显然不在 yyyy-MM-dd HH:mm:ss 格式。所以这是注定要失败的。更具体地说,格式字符串以 yyyy 例如2020年。因此格式化程序希望在字符串的开头找到一个四位数的年份数字。相反,它发现 Mon 并抛出异常。异常消息通知我们字符串 could not be parsed at index 0 . 索引0是字符串的开头,其中 Mon 是。我不确定,但似乎你一直混淆输入和输出格式。将日期时间从一种格式的字符串转换为另一种格式的字符串需要两个操作:
首先,使用描述原始字符串格式的格式化程序将字符串解析为日期时间对象。
其次,使用描述结果字符串格式的格式化程序将datetime格式化为字符串。
因为原始字符串是英语的,所以在解析它时必须使用讲英语的区域设置。使用 Locale.getDefault() 会在讲英语的设备上工作,当有一天你在另一种语言设置的设备上运行它时会突然失败。所以这是个坏主意。 TemporalAccessor 是一个我们很少使用的低级接口。而是将字符串解析为 ZonedDateTime 因为它包含日期、时间和时区(在一个字符串中) GMT 计算为时区)。
如果要将日期时间格式化为 DateTimeFormatter -正如我所说,我不认为这是你应该想要的-以下是可行的:

DateTimeFormatter inputParser = DateTimeFormatter
            .ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT);
    DateTimeFormatter databaseFormatter
            = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");

    String inputDatetime = "Mon Dec 21 21:18:37 GMT 2020";
    OffsetDateTime dateTimeToStore = ZonedDateTime.parse(inputDatetime, inputParser)
            .toOffsetDateTime()
            .withOffsetSameInstant(ZoneOffset.UTC);
    String formattedString = dateTimeToStore.format(databaseFormatter);

    System.out.println(formattedString);

输出:
2020-12-21 21:18:37

cgyqldqp

cgyqldqp2#

正如您已经猜到的,错误的根本原因是日期时间字符串中的模式与您在中使用的模式不匹配 DateTimeFormatter . 如果已经知道获取日期时间字符串的所有日期时间模式,则可以创建 DateTimeFormatter 具有多个可选模式(通过将模式括在方括号中)。如果您收到一个未知模式的日期时间(即您尚未输入的模式 DateTimeFormatter ),您可以根据需要抛出或处理异常。
我需要将具有用户指定的区域设置和时区的任何传入日期时间字符串解析为唯一的模式,以便稍后将其正确存储在数据库中:
这个要求有两个部分:a。解析并将用户指定的区域设置和时区中的日期时间转换为等效的日期时间 UTC (不仅是推荐的,而且是某些数据库所要求的,例如。 PostgreSQL )b。保存到数据库中。
满足第一部分要求的步骤是:
由于接收到的日期时间位于用户指定的时区中,请忽略日期时间字符串中包含的时区并将其解析为 LocalDateTime .
转换 LocalDateTimeZonedDateTime 在用户指定的时区。
转换此 ZonedDateTimeZonedDateTime 在utc。
最后,转换 ZonedDateTimeOffsetDateTime .
一旦你有 OffsetDateTime ,您可以将其存储到数据库中,如下所示:

PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, odt);// odt is the instance of OffsetDateTime
st.executeUpdate();
st.close();

您可以使用以下测试线束来测试需求的第一部分:

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Test
        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.print("Enter the date-time string (press Enter without entering anything to quit): ");
            String strDateTime = scanner.nextLine();
            if (strDateTime.isBlank()) {
                break;
            }

            boolean valid;

            // Create Locale
            Locale locale = null;
            do {
                valid = true;
                System.out.print("Enter language code e.g. en, fr, in: ");
                String languageTag = scanner.nextLine();
                if (!isValidForLocale(languageTag)) {
                    System.out.println("Invalid code. Please try again.");
                    valid = false;
                } else {
                    locale = Locale.forLanguageTag(languageTag);
                }
            } while (!valid);

            // Create ZoneId
            ZoneId zoneId = null;
            do {
                valid = true;
                System.out.print("Enter timezone in the format Continent/City e.g. Asia/Calcutta: ");
                String timezone = scanner.nextLine();
                try {
                    zoneId = ZoneId.of(timezone);
                } catch (Exception e) {
                    System.out.println("Invalid timezone. Please try again.");
                    valid = false;
                }
            } while (!valid);

            try {
                System.out.println(getDateTimeInUTC(strDateTime, locale, zoneId));
            } catch (DateTimeParseException e) {
                System.out.println("The date-time string has the following problem:\n" + e.getMessage());
                System.out.println("Please try again.");
            }
        }
    }

    static OffsetDateTime getDateTimeInUTC(String strDateTime, Locale locale, ZoneId zoneId)
            throws DateTimeParseException {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("[uuuu-M-d H:m:s][EEE MMM d H:m:s zzz uuuu]", locale);

        // Ignore the timezone contained in strDateTime and parse strDateTime to
        // LocalDateTime. Then, convert the LocalDateTime to ZonedDateTime at zoneId.
        // Then, convert this ZonedDateTime to ZonedDateTime at UTC. Finally, convert
        // the ZonedDateTime to OffsetDateTime and return the same.
        ZonedDateTime zdt = LocalDateTime.parse(strDateTime, dtf).atZone(zoneId).withZoneSameInstant(ZoneOffset.UTC);
        return zdt.toOffsetDateTime();
    }

    static boolean isValidForLocale(String languageTag) {
        return Arrays.stream(Locale.getISOLanguages()).anyMatch(l -> Objects.equals(l, languageTag));
    }
}

示例运行:

Enter the date-time string (press Enter without entering anything to quit): Mon Dec 21 21:18:37 GMT 2020
Enter language code e.g. en, fr, in: en
Enter timezone in the format Continent/City e.g. Asia/Calcutta: Asia/Calcutta
2020-12-21T15:48:37Z
Enter the date-time string (press Enter without entering anything to quit): 2020-1-23 5:15:8
Enter language code e.g. en, fr, in: en
Enter timezone in the format Continent/City e.g. Asia/Calcutta: Asia/Calcutta
2020-01-22T23:45:08Z
Enter the date-time string (press Enter without entering anything to quit):

相关问题