Java日历/日期:AM和PM未正确设置

时间:2017-12-18 01:29:30

标签: java date

我有一个函数,它接受一个自定义字符串并将其转换为Date。我的目标是存储今天的日期,但是使用字符串提供的自定义时间:分钟。

由于某种原因,调试器显示AM / PM最后切换(但流程正确)。当我传入12:05am时,Date对象存储为PM值,而如果我传入12:05pm,则Date对象存储为AM值。应该是相反的。

代码:

public class DateUtils {

    private static final String AM_LOWERCASE = "am";
    private static final String AM_UPPERCASE = "AM";

    public static Date getDateFromTimeString(String timeStr) {

        Calendar calendar = Calendar.getInstance();

        if (StringUtils.hasText(timeStr)) {

            if (timeStr.indexOf(AM_LOWERCASE) != -1 || timeStr.indexOf(AM_UPPERCASE) != -1) {
                calendar.set(Calendar.AM_PM, Calendar.AM);
            } else {
                calendar.set(Calendar.AM_PM, Calendar.PM);
            }

            // Set custom Hours:Minutes on today's date, based on timeStr
            String[] timeStrParts = timeStr.replaceAll("[a-zA-Z]", "").split(":");
            calendar.set(Calendar.HOUR, Integer.valueOf(timeStrParts[0]));
            calendar.set(Calendar.MINUTE, Integer.valueOf(timeStrParts[1]));
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
        }

        return calendar.getTime();

    }
}

调试器显示:

输入:12:05am -> Sun Dec 17 12:05:00 EST 2017

输入:12:05pm -> Mon Dec 18 00:05:00 EST 2017

应该与此相反。如果我使用SimpleDateFormat写回这些字符串,我会看到输入1在12:05 PM返回,输入2在12:05 AM返回。

此外,对于#2,日期不应该在一天前进。应该存储的日期是两个情况下的今天日期,上午12:05或下午12:05。

我错过了什么吗?目标:

12:05am   ->    Sun Dec 17 00:05:00 EST 2017
12:05pm   ->    Sun Dec 17 12:05:00 EST 2017

3 个答案:

答案 0 :(得分:5)

问题是Calendar.HOUR的值范围从011,而不是112。当您将小时设置为12时,日历将其标准化为当天的另一半...即,您“溢出”到第二天的一半。

public static void main(String[] args)
{
    Calendar c1 = Calendar.getInstance();
    System.out.printf("Initial: %s\n",c1.getTime().toString());
    c1.set(Calendar.AM_PM, Calendar.AM);
    System.out.printf("Set(AM): %s\n",c1.getTime().toString());
    c1.set(Calendar.HOUR, 12);
    System.out.printf("Set(12): %s\n\n",c1.getTime().toString());

    Calendar c2 = Calendar.getInstance();
    System.out.printf("Initial: %s\n",c2.getTime().toString());
    c2.set(Calendar.AM_PM, Calendar.PM);
    System.out.printf("Set(PM): %s\n",c2.getTime().toString());
    c2.set(Calendar.HOUR, 12);
    System.out.printf("Set(12): %s\n\n",c2.getTime().toString());
}

输出

Initial: Sun Dec 17 17:53:52 PST 2017
Set(AM): Sun Dec 17 05:53:52 PST 2017
Set(12): Sun Dec 17 12:53:52 PST 2017

Initial: Sun Dec 17 17:53:52 PST 2017
Set(PM): Sun Dec 17 17:53:52 PST 2017
Set(12): Mon Dec 18 00:53:52 PST 2017

除此之外,您应该使用自Java 8以来已成为Java一部分的新Time类。它们取代了多年来已知不太理想的遗留类(即日历,日期等)。

正如@Andreas所建议的,以下是如何以现代方式解析它:

LocalTime.parse(
    "12:05am", 
     new DateTimeFormatterBuilder().
         parseCaseInsensitive().
         appendPatt‌​‌​ern("hh:mma").
         toFo‌​rm‌​atter(Locale.US)‌​);

答案 1 :(得分:2)

tl;dr

foreach (Action action in BackProp)
{
    action();
}

java.time

You are using troublesome old date-time classes that are now legacy, supplanted by java.time classes.

ZonedDateTime.of( LocalDate.now( ZoneId.of( "Africa/Casablanca" ) ) , LocalTime.parse( "12:05am".toUppercase() , DateTimeFormatter.ofPattern( "hh:mma" , Locale.US ) ) , ZoneId.of( "Africa/Casablanca" ) )

Your input strings use lowercase "am"/"pm" which are incorrect. Those letters are actually initial-letter abbreviations and so should be uppercase. We must force the uppercase to facilitate parsing.

java.time.LocalTime

You are inappropriately trying to represent a time-of-day with a date-time class. Instead use DateTimeFormatter f = DateTimeFormatter.ofPattern( "hh:mma" , Locale.US ) ; LocalTime lt = LocalTime.parse( "12:05am".toUppercase() , f ) ; class as it is meant for a time-of-day with no date and no zone/offset.

Today

Getting the current date requires a time zone.

LocalTime

Combine to get a ZoneId z = ZoneId.of( "Pacific/Auckland" ) ; LocalDate ld = LocalDate.now( z ); .

ZonedDateTime

If that time-of-day on that date in that zone is not valid because of anomalies such as Daylight Saving Time, java.time automatically adjusts. Read the doc to be sure you understand and agree with the adjustment algorithm.

答案 2 :(得分:1)

java.time

<label>Personal Skills</label> <select multiple size=14 input type="text" id="skill1" name="skill1" class="form-control" value="<?php echo $out['skill1'];?>"> <option value="" disabled selected hidden>Select...</option> <option value='=COMPASSIONATE'>COMPASSIONATE</option> <option value='EFFECTIVE COMMUNICATOR'>EFFECTIVE COMMUNICATOR</option> <option value='LEADERSHIP'>LEADERSHIP</option> <option value='INSIGHTFUL'>INSIGHTFUL</option> <option value='PERCEPTIVE'>PERCEPTIVE</option> <option value='CREATIVE'>CREATIVE</option> <option value='FLEXIBLE'>FLEXIBLE</option> <option value='INNOVATIVE'>INNOVATIVE</option> <option value='LOGICAL THINKING'>LOGICAL THINKING</option> <option value='PROBLEM SOLVING'>PROBLEM SOLVING</option> <option value='SOCIABLE'>SOCIABLE</option> <option value='CONSISTENT'>CONSISTENT</option> <option value='MARKETING'>MARKETING</option> <option value='CRITICAL THINKING '>CRITICAL THINKING</option> </select> 日期时间 API 及其格式化 API java.util 已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*

避免执行 SimpleDateFormat 操作

执行 String 操作(例如正则表达式匹配、转大写、转小写等)可能容易出错且不完整。而是使用 DateTimeFormatterBuilder,它允许一组丰富的选项(例如不区分大小写的解析、指定可选模式、使用默认值解析等)。

演示:

String

输出:

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()   //To parse in case-insensitive way e.g. AM, am
                .appendPattern("h:m[ ]a") // Notice single h and m and optional space in square bracket
                .toFormatter(Locale.ENGLISH);
        
        // Test
        Stream.of(
                    "08:20 am",
                    "08:20 pm",
                    "08:20 AM",
                    "08:20 PM",
                    "8:20 am",
                    "08:5 pm",
                    "08:5pm",
                    "8:5am"
        ).forEach(s -> System.out.println(LocalTime.parse(s, dtf)));
    }
}

请注意单个 08:20 20:20 08:20 20:20 08:20 20:05 20:05 08:05 h,它们既适用于一位数,也适用于两位数的小时和分钟。另一件需要注意的事情是使用方括号指定的可选空间。您可以在单个 m 中指定许多可选模式。查看 this answer 演示 DateTimeFormatter 的一些更强大的功能。

需要今天的日期和时区以及时间?

使用 ZonedDateTime#of(LocalDate, LocalTime, ZoneId) 创建一个。

DateTimeFormatter

ONLINE DEMO

modern Date-Time API 中详细了解 String strTime = "08:20 am"; ZoneId tz = ZoneId.of("America/Los_Angeles"); System.out.println(ZonedDateTime.of(LocalDate.now(tz), LocalTime.parse(strTime, dtf), tz)); Trail: Date Time*


* 出于任何原因,如果您必须坚持使用 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