为什么Java';s Calendar date将小时转换为日期时将其重置为另一个值?

为什么Java';s Calendar date将小时转换为日期时将其重置为另一个值?,java,Java,我想在2020年10月26日做一个简单的脚本,每10分钟设置一次时间。 我的循环如下所示。 然而,即使当我输出'time'变量时,时间通常看起来是正确的,然后我将其转换为Date对象,并对该'd'变量执行toString(),然后它似乎将其转换为另一个时间。如果您想看到它在联机java编译器上运行,您可以在此处看到它的运行: 你可以看到它做一些奇怪的事情,比如: 上午0:00 星期一10月26日10:00:00格林威治标准时间2020 上午0:10 2020年10月26日星期一10:10:00

我想在2020年10月26日做一个简单的脚本,每10分钟设置一次时间。
我的循环如下所示。
然而,即使当我输出'time'变量时,时间通常看起来是正确的,然后我将其转换为Date对象,并对该'd'变量执行toString(),然后它似乎将其转换为另一个时间。如果您想看到它在联机java编译器上运行,您可以在此处看到它的运行:

你可以看到它做一些奇怪的事情,比如:
上午0:00
星期一10月26日10:00:00格林威治标准时间2020
上午0:10
2020年10月26日星期一10:10:00格林尼治标准时间

下午12:00
星期一10月26日22:00:00格林威治标准时间2020
下午12:10

星期一10月26日22:10:00格林威治标准时间2020

一件不同寻常的事情是,我将在IDE中设置一个12小时的断点。。。如果我调试缓慢,它将设置正确的时间。然后,我将删除断点并播放脚本的其余部分,然后时间再次不正确。我在Java中还没有见过非常不寻常的行为。
我可以这样做来重置小时数(或使用不同的技术):

但我现在更想弄清楚为什么Java会这样做

  for(int hour=0; hour < 24; hour++ ) {
      for(int minute = 0; minute < 6; minute++) {

          String str_min = minute==0 ? "00" : String.valueOf(minute*10);
          String time = String.valueOf( hour > 12 ? hour-12 : hour) +":"+ str_min +" "+ ((hour >= 12 ?  Calendar.PM : Calendar.AM)==1 ? "PM" : "AM"); 
          //note: got lazy and not outputting "00" for hour
          System.out.println( time );

          Calendar cal = Calendar.getInstance();
          cal.set(Calendar.MONTH, 9); //Oct=9
          cal.set(Calendar.DAY_OF_MONTH, 26);
          cal.set(Calendar.YEAR, 2020);
          cal.set(Calendar.HOUR_OF_DAY, hour );           
          cal.set(Calendar.MINUTE, minute*10     ); //0=0, 1=10, ... 5=50
          cal.set(Calendar.AM_PM, (hour >= 12 ?  Calendar.PM : Calendar.AM) );            
          cal.set(Calendar.SECOND,      0);
          cal.set(Calendar.MILLISECOND, 0);
          Date d = cal.getTime();

          System.out.println( d.toString() );

      }
  }
for(int hour=0;hour<24;hour++){
对于(int-minute=0;minute<6;minute++){
String str_min=minute==0?“00”:String.valueOf(minute*10);
String time=String.valueOf(hour>12?hour-12:hour)+“:”+str_min++”((hour>=12?Calendar.PM:Calendar.AM)==1?:“PM”:“AM”);
//注意:变懒了,一个小时没有输出“00”
系统输出打印LN(时间);
Calendar cal=Calendar.getInstance();
校准设置(日历月,9);//十月=9
校准设置(日历日/月,26);
校准集(日历年,2020年);
校准设置(日历小时/天,小时);
cal.set(Calendar.MINUTE,MINUTE*10);//0=0,1=10,…5=50
校准设置(Calendar.AM\u PM,(小时>=12?Calendar.PM:Calendar.AM));
校准设置(日历秒,0);
校准设置(日历毫秒,0);
日期d=cal.getTime();
System.out.println(d.toString());
}
}

java.util.Date是个谎言。它不代表日期;它代表时间上的一个瞬间,它不知道时区,也不知道“小时”的概念。这就是为什么所有东西(除了基于epoch-millis的方法和构造函数)都被标记为不推荐使用

日历API试图解决这个问题,但它是一个非常可怕的API

改用
java.time
。丑陋的API消失了,与旧的API不同,您拥有的类型实际上完全代表了您想要的。考虑到您没有在这里弄乱时区,您需要
LocalDateTime

for (int hour = 0; hour < 24; hour++) {
  for (int minute = 0; minute < 60; minute += 10) {
    LocalDateTime ldt = LocalDateTime.of(2020, 10, 26, hour, minute, 0);
    System.out.println(ldt);
    // or better:
    System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(ldt));
  }
}
for(int hour=0;hour<24;hour++){
对于(整数分钟=0;分钟<60;分钟+=10){
LocalDateTime ldt=LocalDateTime.of(2020,10,26,小时,分钟,0);
系统输出打印项次(ldt);
//或者更好:
System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(ldt));
}
}
请注意在这个API中10月是如何的“10”,因为这个API不是疯狂的,例如。您可以在DTF中使用许多预定义的格式化程序中的任何一个,也可以使用一个模式编写自己的格式化程序,以精确地控制日期的呈现方式

如果你想代表其他事物,你可以这样做
ZoneDateTime
按照人类的说法,指定一个特定的时间(比如:我和牙医约好了,他在罗马,时间是2020年11月2日下午2点一刻)。这样一个概念的要点是:如果罗马决定改变时区,那么你约会的实际时间应该随之改变

如果你想要一个瞬间,有
instant
。这是一个特定的时刻(在过去或未来),在它发生之前,它不会改变剩余的毫秒数。即使面对各国改变其欧元区

LDT就是LDT:它只代表一年、一个月、一天、小时、分钟和秒,没有时区。

tl;博士 避免遗留日期时间类 您正在使用可怕的日期时间类,这些类在几年前被java.time类取代。Sun、Oracle和the放弃了这些课程,您也应该放弃

与其尝试去理解那些糟糕的类,我建议您将精力投入到学习java.time上

使用java.time 当您需要当前日期时间时,更容易捕获
即时
对象

请注意,java.time使用。我们生成一个新对象,而不是改变现有对象

今天每十分钟代表一个时刻:

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;  // Or "America/New_York", etc.
LocalDate today = LocalDate.now( z ) ;
您的代码做出了错误的假设。天或不总是24小时长。它们可能是23小时、25小时或其他长度。也不是每个时区的每一天都从00:00开始。让java.time确定一天中的第一个时刻

ZonedDateTime start = today.atStartOfDay( z ) ;
从第二天开始

ZonedDateTime stop = start.plusDays( 1 ) ;
循环10分钟的时间。将该区块表示为持续时间

Duration d = Duration.ofMinutes( 10 ) ;

List< ZonedDateTime > results = new ArrayList<>() ;
ZonedDateTime zdt = start ;
while( zdt.isBefore( stop ) ) 
{
    results.add( zdt ) ;
    zdt = zdt.plus( d ) ;
}
特别是,
Locale.US
FormatStyle.MEDIUM
可能适合您。开始做实验


关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

该项目现已启动,建议迁移到类

您可以直接与数据库交换java.time对象。使用兼容的或更高版本。不需要字符串,也不需要
java.sql.*
类。Hibernate5和JPA2.2支持java.time

从哪里获得java.time类

  • 、和更高版本-标准Java API的一部分,带有捆绑实现。
    • 带来了一些小功能和
      ZonedDateTime stop = start.plusDays( 1 ) ;
      
      Duration d = Duration.ofMinutes( 10 ) ;
      
      List< ZonedDateTime > results = new ArrayList<>() ;
      ZonedDateTime zdt = start ;
      while( zdt.isBefore( stop ) ) 
      {
          results.add( zdt ) ;
          zdt = zdt.plus( d ) ;
      }
      
      Locale locale = Locale.CANADA_FRENCH ; // Or Locale.US etc.
      DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale ) ;
      String output = myZonedDateTime.format( f ) ;