【问题标题】:StringDate to Date coming in different Time in SimpleDateFormat in javajava中SimpleDateFormat中不同时间的字符串日期到日期
【发布时间】:2020-06-09 23:51:47
【问题描述】:
    /*I want the same Date of my String has
     Tried with couple of options but nothing worked 
     pasting some of code here */

    public void stringToDate() {

        //Current format "13-FEB-20 03.21.08.100000000 PM" in Melbourne Timezone
        //Required Format yyyy-dd-MM HH:mm:ss.SSS in Melbourne Timezone

        String inputAM = "13-FEB-20 03.21.08.100000000 PM";
        try {
            DateFormat df1 = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.S aa");
            Date d1 = df1.parse(inputAM);
            System.out.println("Date-1: " + d1); //Fri Feb 14 19:07:48 AEDT 2020

            DateFormat df2 = new SimpleDateFormat("DD-MMM-yy hh.mm.ss.S aa");
            Date d2 = df2.parse(inputAM);
            System.out.println("Date-2: " + d2); //Tue Jan 14 19:07:48 AEDT 2020

        } catch (ParseException e) {
            e.printStackTrace();
        }

        SimpleDateFormat etDf = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSS");
        TimeZone etTimeZone = TimeZone.getTimeZone("Australia/Melbourne");
        etDf.setTimeZone(etTimeZone);

        DateFormat df3 = new SimpleDateFormat("dd-MMM-yy HH.mm.ss.SSSSSSSSS a");
        Date d3;
        try {
            d3 = df3.parse(inputAM);
            System.out.println("Date-3: " + d3); //Fri Feb 14 07:07:48 AEDT 2020
            System.out.println("Date-4: " + etDf.format(d3.getTime())); //2020-14-02 07:07:48.000
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

【问题讨论】:

标签: java java-8 simpledateformat localdate


【解决方案1】:

首先,在大多数情况下,不要将日期和时间从一种字符串格式转换为另一种格式。在您的程序中,将日期和时间保留为正确的日期时间对象,而不是字符串。当您接受字符串输入时,首先要解析它。只有当您需要提供字符串输出时,才将您的日期时间格式化为所需格式的字符串。

其次,使用 java.time,现代 Java 日期和时间 API 来处理所有日期和时间工作。它比旧的、设计不佳的和长期过时的类(包括 DateFormatSimpleDateFormatDate)要好得多。

使用 java.time 解析您的输入

    //Current format "13-FEB-20 03.21.08.100000000 PM" in Melbourne Timezone
    DateTimeFormatter currentFormatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("d-MMM-uu hh.mm.ss.SSSSSSSSS a")
            .toFormatter(Locale.ENGLISH);
    ZoneId zone = ZoneId.of("Australia/Melbourne");

    String inputAM = "13-FEB-20 03.21.08.100000000 PM";

    ZonedDateTime dateTime = LocalDateTime.parse(inputAM, currentFormatter).atZone(zone);

    System.out.println(dateTime);

目前的输出是:

2020-02-13T15:21:08.100+11:00[澳大利亚/墨尔本]

使用 java.time 格式化

    //Required Format yyyy-dd-MM HH:mm:ss.SSS in Melbourne Timezone
    DateTimeFormatter requiredFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS");
    String formatted = dateTime.format(requiredFormatter);
    System.out.println(formatted);

2020-02-13 15:21:08.100

你的代码出了什么问题?

当我第一次尝试你的代码时,FEB 的解析失败,因为你的代码没有指定语言环境,而我的 Java 使用了非英语的默认语言环境。

更正我能够解析字符串并得到与您相同的结果(仅在我的时区)。会发生什么:SimpleDateFormat 将大写的S 表示为毫秒,即千分之一秒,无论有多少S,也无论要解析的字符串中有多少位数字。因此,您的日期和时间增加了 100 000 000 毫秒。那是一天多一点。所以你得到的是 2 月 14 日而不是 2 月 13 日,而且一天中的时间也是错误的。与现代的DateTimeFormatter 相比,大写S 表示秒的分数,因此它可以按照我们预期的方式处理SSSSSSSSSSSS

大写的DD 表示一年中的某一天,因此使用它来解析你会得到一年中的第 13 天(与 1 月 13 日相同)加上你的 100 000 秒。

大写的 HH 表示一天中从 00 到 23 的时间。使用 HH 解析会得到一天中的 03:21(与上午 03:21 相同)加上您的 100 000 秒,不管它说的是 PM在你的字符串中。

链接

【讨论】:

    【解决方案2】:

    您的代码中存在三个主要问题:

    1. 您使用.SSSSSSSSS 的时间只有几分之一秒,而SimpleDateFormat 不支持超过毫秒的精度 (.SSS)。这也意味着您需要将几分之一秒的数字限制为三个。
    2. 您曾经以 12 小时格式(即上午/下午)使用 HH,而用于此的 correct patternhh。符号 HH 用于表示 24 小时格式的时间。
    3. 您已将DD 用于月中的一天,而正确的模式是dd。符号DD 用于一年中的一天

    除此之外,我建议您始终将 Locale 与日期解析/格式化 API 一起使用,因为日期时间字符串的某些部分在不同的 Locales 中以不同的方式表示。

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Locale;
    import java.util.TimeZone;
    
    public class Main {
        public static void main(String[] args) throws ParseException {
            SimpleDateFormat sdfInput = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a", Locale.ENGLISH);
            Date date = sdfInput.parse("13-FEB-20 03.21.08.100 PM");
    
            // Print Date#toString
            System.out.println(date);
    
            // Print the date-time in a custom format
            SimpleDateFormat sdfOutput = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSS", Locale.ENGLISH);
            System.out.println(sdfOutput.format(date));
    
            // If required, format date with a timezone
            SimpleDateFormat sdfOutputWithTz = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSS[zzzz]", Locale.ENGLISH);
            sdfOutputWithTz.setTimeZone(TimeZone.getTimeZone("Australia/Melbourne"));
            System.out.println(sdfOutputWithTz.format(date));
        }
    }
    

    输出:

    Thu Feb 13 15:21:08 GMT 2020
    2020-13-02 15:21:08.100
    2020-14-02 02:21:08.100[Australian Eastern Daylight Time]
    

    关于旧版 API 的一些事实:

    1. java.util.Date 对象不像modern date-time types 那样是真正的日期时间对象;相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即January 1, 1970, 00:00:00 GMT(或 UTC)。当你打印一个java.util.Date 的对象时,它的toString 方法返回JVM 时区中的日期时间,从这个毫秒值计算。如果您需要在不同的时区打印日期时间,则需要将时区设置为 SimpleDateFormat 并从中获取格式化字符串。
    2. java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,改用modern date-time API

    使用现代日期时间 API:

    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeFormatterBuilder;
    import java.util.Locale;
    
    public class Main {
        public static void main(String[] args) {
            DateTimeFormatter dtfInput = new DateTimeFormatterBuilder()
                    .parseCaseInsensitive() // For case-insensitive (e.g. AM/am) parsing
                    .appendPattern("dd-MMM-uu hh.mm.ss.SSSSSSSSS a")
                    .toFormatter(Locale.ENGLISH);       
            
            // Parse the date-time string
            LocalDateTime ldt = LocalDateTime.parse("13-FEB-20 03.21.08.100000000 PM", dtfInput);
            
            // Print LocalDateTime#toString
            System.out.println(ldt);
            
            // Print the date-time in a custom format
            DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("uuuu-dd-MM HH:mm:ss.SSS", Locale.ENGLISH);
            System.out.println(ldt.format(dtfOutput));
            
            // If required, convert the LocalDateTime to ZonedDateTime
            ZonedDateTime zdt = ldt.atZone(ZoneId.of("Australia/Melbourne"));
            System.out.println(zdt);
        }
    }
    

    输出:

    2020-02-13T15:21:08.100
    2020-13-02 15:21:08.100
    2020-02-13T15:21:08.100+11:00[Australia/Melbourne]
    

    注意:

    1. 如果您的日期时间字符串在几分之一秒内始终包含 9 位数字,您可以将 .SSSSSSSSS 替换为 .n
    2. 对于DateTimeFormatter,符号u 表示年份,而符号y 表示年代。在AD 时代,这一年没有任何区别,但在 BC 时代,这一年很重要。查看this answer 了解更多信息。
    3. LocalDateTime 没有时区信息。如果要显示时区信息,应考虑使用ZonedDateTimeOffsetDateTime。下表显示an overview of java.time data-time types

    通过 Trail: Date Time 了解有关现代日期时间 API 的更多信息。


    ◊ 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-20
      • 1970-01-01
      • 2021-10-07
      • 1970-01-01
      • 1970-01-01
      • 2016-04-02
      相关资源
      最近更新 更多