简体   繁体   English

解析 MM/DD/YY 格式的日期并将其调整为当前/上个世纪的最佳方法是什么?

[英]What is the best way to parse a date in MM/DD/YY format and adjust it to the current / previous century?

One of our customers wants to be able to enter a date with only 2 digits for the year component.我们的一位客户希望能够输入年份部分只有 2 位数字的日期。 The date will be in the past, so we want it to work for the previous century if the 2 digit year is after the current year, but work for the current century if the 2 digit year is equal to or less than the current year.日期将是过去的日期,因此如果 2 位数年份晚于当前年份,我们希望它适用于上个世纪,但如果 2 位数年份等于或小于当前年份,则它适用于当前世纪。

as of today 10/30/2008截至今天 10/30/2008

01/01/01 = 01/01/2001 01/01/01 = 01/01/2001

01/01/09 = 01/01/1909 01/01/09 = 01/01/1909

This is a strange requirement, and I solved the problem, I just don't like my solution.这是一个奇怪的要求,我解决了这个问题,我只是不喜欢我的解决方案。 It feels like there is a better way to do this.感觉有更好的方法可以做到这一点。

Thanks for the help.谢谢您的帮助。

public static String stupidDate(String dateString)
{
    String twoDigitYear = StringUtils.right(dateString, 2);
    String newDate = StringUtils.left(dateString, dateString.length() - 2);
    int year = NumberUtils.toInt(twoDigitYear);
    Calendar c = GregorianCalendar.getInstance();
    int centuryInt = c.get(Calendar.YEAR) - year;
    newDate = newDate + StringUtils.left(Integer.toString(centuryInt), 2) + twoDigitYear;
    return newDate;
}

Groovy script (easy enough to throw into java) demonstrating the point @bobince made about SimpleDateFormat. Groovy脚本(很容易引入java)演示@bobince关于SimpleDateFormat的观点。

import java.text.SimpleDateFormat

SimpleDateFormat sdf = new SimpleDateFormat('MM/dd/yy')
SimpleDateFormat fmt = new SimpleDateFormat('yyyy-MM-dd')

Calendar cal = Calendar.getInstance()
cal.add(Calendar.YEAR, -100)
sdf.set2DigitYearStart(cal.getTime())

dates = ['01/01/01', '10/30/08','01/01/09']
dates.each {String d ->
  println fmt.format(sdf.parse(d))
}

Yields 产量

2001-01-01
2008-10-30
1909-01-01

SimpleDateFormat already does two-digit year parsing for you, using the two-letter 'yy' format. SimpleDateFormat已经为您使用两个字母的“yy”格式进行了两位数的年份解析。 (It'll still allow four digits, obviously.) (显然它仍然允许四位数。)

By default it uses now-80→now+20, so it's not exactly the same rule you propose, but it's reasonable and standardised (in the Java world at least), and can be overridden using set2DigitYearStart() if you want. 默认情况下,它使用now-80→now + 20,因此它与你提出的规则不完全相同,但它是合理的和标准化的(至少在Java世界中),并且如果你愿意,可以使用set2DigitYearStart()覆盖它。

DateFormat informat= new SimpleDateFormat("MM/dd/yy");
DateFormat outformat= new SimpleDateFormat("MM/dd/yyyy");
return outformat.format(informat.parse(dateString));

In the longer term, try to migrate to ISO8601 date formatting (yyyy-MM-dd), because MM/dd/yy is approximately the worst possible date format and is bound to cause problems eventually. 从长远来看,尝试迁移到ISO8601日期格式(yyyy-MM-dd),因为MM / dd / yy大约是最糟糕的日期格式,并且最终会导致问题。

How about this: 这个怎么样:

public static String anEasierStupidDateWithNoStringParsing(String dateString) {
    DateFormat df = new SimpleDateFormat("MM/dd/yyyy");

    //handling ParseExceptions is an exercise left to the reader!
    Date date = df.parse(dateString);
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);

    Calendar now = Calendar.getInstance();
    if (cal.after(now)) {
        cal.add(Calendar.YEAR, -100);
    }

    return cal;
}

In other words, let SimpleDateFormat parse the String and just adjust the year to be the previous century if SimpleDateFormat ( which has it's own rules for interpreting year strings ) returns a date that is after the current date. 换句话说,如果SimpleDateFormat( 它有自己的解释年份字符串的规则 )返回当前日期之后的日期,让SimpleDateFormat解析String并将年份调整为上个世纪。

This would guarantee that all dates returned are in the past. 这样可以保证返回的所有日期都是过去的。 However, it doesn't account for any dates that might be parsed as before this past century - for example, with the format MM/dd/yyyy , a date string like "01/11/12" parses to Jan 11, 12 AD 但是,它没有说明在上个世纪之前可能被解析的任何日期 - 例如,格式为MM/dd/yyyy ,日期字符串如“01/11/12”解析为公元12年1月11日

If Joda Time is an option: 如果Joda Time是一个选项:

String inputDate = "01/01/08";
// assuming U.S. style date, since it's not clear from your original question
DateTimeFormatter parser = DateTimeFormat.forPattern("MM/dd/yy");
DateTime dateTime = parser.parseDateTime(inputDate);
// if after current time
if (dateTime.isAfter(new DateTime())) {
    dateTime = dateTime.minus(Years.ONE);
}

return dateTime.toString("MM/dd/yyyy");

I know Joda Time isn't part of Java SE, and as I've said in another thread, I usually do not condone using a third-party library when there's a Java library that does the same thing. 我知道Joda Time不是Java SE的一部分,正如我在另一个帖子中所说的那样,当有一个Java库做同样的事情时,我通常不会宽恕使用第三方库。 However, the person who is developing Joda Time is also leading JSR310 - the Date and Time API that'll make it into Java 7. So I Joda Time is basically going to be in future Java releases. 然而,正在开发Joda Time的人也在领导JSR310--日期和时间API,它将进入Java 7.所以我Joda Time基本上将在未来的Java版本中。

The accepted answer uses legacy date-time API which was the correct thing to do in 2008 when the question was asked.接受的答案使用遗留日期时间 API,这是 2008 年提出问题时的正确做法。 In March 2014, java.time API supplanted the error-prone legacy date-time API . 2014 年 3 月, java.time API 取代了容易出错的遗留日期时间 API Since then, it has been strongly recommended to use this modern date-time API.从那时起,强烈建议使用这个现代日期时间 API。

java.time API java.time API

  1. You can put optional patterns between DateTimeFormatterBuilder#optionalStart and DateTimeFormatterBuilder#optionalEnd and create a formatter which can parse a date string with either a four-digit year or a two-digit year.您可以在DateTimeFormatterBuilder#optionalStartDateTimeFormatterBuilder#optionalEnd之间放置可选模式,并创建一个格式化程序,它可以解析具有四位数年份或两位数年份的日期字符串。
  2. Using the DateTimeFormatterBuilder#appendValueReduced , you can specify a base value for the year as per your requirement.使用DateTimeFormatterBuilder#appendValueReduced ,您可以根据需要指定年份的基值。

Demo :演示

import java.time.LocalDate;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter parser = new DateTimeFormatterBuilder()
                .appendPattern("M/d/")
                .optionalStart()
                .appendPattern("uuuu")
                .optionalEnd()
                .optionalStart()
                .appendValueReduced(ChronoField.YEAR, 2, 2, Year.now().minusYears(100).getValue())
                .optionalEnd()
                .toFormatter(Locale.ENGLISH);

        // Test
        Stream.of(
                "1/2/2022",
                "01/2/2022",
                "1/02/2022",
                "01/02/2022",
                "1/2/22",
                "1/2/21",
                "1/2/20",
                "1/2/23",
                "1/2/24"
            )
            .map(s -> LocalDate.parse(s, parser))
            .forEach(System.out::println);
    }
}

Output : Output :

2022-01-02
2022-01-02
2022-01-02
2022-01-02
1922-01-02
2021-01-02
2020-01-02
1923-01-02
1924-01-02

Note that the dates with a two-digit year greater than the current year are parsed into a LocalDate with the last century.请注意,年份大于当前年份的两位数的日期将被解析为具有上个世纪的LocalDate


How to switch from the legacy to the modern date-time API?如何从旧版切换到现代日期时间 API?

You can switch from the legacy to the modern date-time API using Date#toInstant on a java-util-date instance.您可以在 java-util-date 实例上使用Date#toInstant从旧日期时间切换到现代日期时间 API。 Once you have an Instant , you can easily obtain other date-time types of java.time API. An Instant represents a moment in time and is independent of a time-zone ie it represents a date-time in UTC (often displayed as Z which stands for Zulu-time and has a ZoneOffset of +00:00 ).一旦你有了一个Instant ,你就可以很容易地获得其他日期时间类型java.time API。一个Instant代表一个时刻并且独立于时区,即它代表一个 UTC 日期时间(通常显示为Z代表祖鲁时间并且ZoneOffset+00:00 )。

Demo :演示

public class Main {
    public static void main(String[] args) {
        Date date = new Date();
        Instant instant = date.toInstant();
        System.out.println(instant);

        ZonedDateTime zdt = instant.atZone(ZoneId.of("Asia/Kolkata"));
        System.out.println(zdt);

        OffsetDateTime odt = instant.atOffset(ZoneOffset.of("+05:30"));
        System.out.println(odt);
        // Alternatively, using time-zone
        odt = instant.atZone(ZoneId.of("Asia/Kolkata")).toOffsetDateTime();
        System.out.println(odt);

        LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Kolkata"));
        System.out.println(ldt);
        // Alternatively,
        ldt = instant.atZone(ZoneId.of("Asia/Kolkata")).toLocalDateTime();
        System.out.println(ldt);
    }
}

Output : Output :

2022-11-20T20:32:42.823Z
2022-11-21T02:02:42.823+05:30[Asia/Kolkata]
2022-11-21T02:02:42.823+05:30
2022-11-21T02:02:42.823+05:30
2022-11-21T02:02:42.823
2022-11-21T02:02:42.823

Learn more about the modern Date-Time API from Trail: Date Time .Trail:Date Time了解有关现代日期时间 API 的更多信息。

Date deliverDate = new SimpleDateFormat("MM/dd/yy").parse(deliverDateString);
String dateString2 = new SimpleDateFormat("yyyy-MM-dd").format(deliverDate);

Working for me. 为我工作

Pivot Year In Joda-Time 乔达时代的枢纽年

The excellent Joda-Time 2.5 library includes this functionality. 优秀的Joda-Time 2.5库包含此功能。 No need to write any code yourself. 无需自己编写任何代码。 Simply call withPivotYear() on a DateTimeFormatter . 只需在DateTimeFormatter上调用withPivotYear() See that linked doc for examples of how to use it. 有关如何使用它的示例,请参阅该链接文档。

最好的方法是在Java中使用Joda Time API来表示日期和时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM