【问题标题】:Android Calendar problem with day of the week星期几的Android日历问题
【发布时间】:2018-03-15 07:08:36
【问题描述】:

我正在尝试列出本周从星期一到星期日的所有日子。例如,今天(发帖日期)是 2011 年 9 月 4 日,是星期日。

我正在启动日历并将一周的第一天设置为星期一:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);

当我检查一个月中的哪一天时,我得到了正确的结果:

int check = cal.get(Calendar.DAY_OF_MONTH);
// check is equal to 4

但是当我将工作日设置为星期一时,它会跳转到下周而不是返回本周的星期一:

cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
// mon is equal to 5, when expected to be 29 (last Monday of August)

即使将工作日设置为周日也会返回下周日而不是今天。

有人能解释一下为什么它会这样工作吗?解决这个问题的最佳方法是什么?

【问题讨论】:

标签: java android


【解决方案1】:

事实上,当我检查自己的测试时,它似乎按预期工作,除非再次设置日期:

显示 4-29:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);
cal.set(2011, 8, 4);
int test = cal.get(Calendar.DAY_OF_MONTH);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
bTest.setText("" + test + "-" + mon);

显示 5-5:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);
cal.set(2011, 8, 5);
int test = cal.get(Calendar.DAY_OF_MONTH);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
bTest.setText("" + test + "-" + mon);

显示 14-12:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);
cal.set(2011, 8, 14);
int test = cal.get(Calendar.DAY_OF_MONTH);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
bTest.setText("" + test + "-" + mon);

所以,这不起作用:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);
//cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
int test = cal.get(Calendar.DAY_OF_MONTH);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
bTest.setText("" + test + "-" + mon); // Display 4-5

并且这行得通:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);

// Workaround
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));

int test = cal.get(Calendar.DAY_OF_MONTH);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
bTest.setText("" + test + "-" + mon); // Display 4-29

并且这也有效:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);

// Workaround
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
cal.get(Calendar.DAY_OF_MONTH);

cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
bTest.setText("" + mon); // Display 29

但是这个没有:

Calendar cal = Calendar.getInstance();
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);

// Workaround
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int mon = cal.get(Calendar.DAY_OF_MONTH);
bTest.setText("" + mon); // Display 5

【讨论】:

  • 在设置过程中可能有一些事情在直接使用实例时会丢失..
  • 谢谢你,我正要写手动设置日期的工作,干杯!
  • 您能否再帮我一个忙并在没有int test = cal.get(Calendar.DAY_OF_MONTH); 的情况下运行您的测试?如果我这样做 - 代码在第二个 cal.get(Calendar.DAY_OF_MONTH) 再次中断。这没什么大不了的,但我不明白为什么会发生这种情况。
  • 谢谢,很高兴知道我不是唯一一个。我会尝试进一步调查。
【解决方案2】:

tl;博士

使用LocalDate,从不使用Calendar。

LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
    .with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) )
    .plusDays( i ) )

java.time

避免使用麻烦的旧遗留类 Calendar,因为它现在已被 java.time 类(特别是 ZonedDateTime)取代。

对于没有时间的仅日期值,请使用 LocalDate 而不是像 Calendar 或 ZonedDateTime 这样的日期+时间类型。 LocalDate 类表示没有时间和时区的仅日期值。

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因区域而异。例如,Paris France 中午夜后几分钟是新的一天,而 Montréal Québec 中仍然是“昨天”。

如果未指定时区,JVM 会隐式应用其当前默认值time zone。该默认值可能随时更改,因此您的结果可能会有所不同。最好将您想要/预期的时区明确指定为参数。

以continent/region 的格式指定proper time zone name,例如America/Montreal、Africa/Casablanca 或Pacific/Auckland。切勿使用 3-4 个字母的缩写,例如 EST 或 IST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

如果你想使用 JVM 当前的默认时区,请求它并作为参数传递。如果省略,则隐式应用 JVM 的当前默认值。最好是明确的,因为默认值可能会在任何时候在运行时被 JVM 中任何应用程序的任何线程中的任何代码更改。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

或者指定一个日期。您可以通过数字设置月份,1 月至 12 月的编号为 1-12。

LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.

或者,更好的是,使用预定义的Month 枚举对象,一年中的每个月一个。提示:在整个代码库中使用这些 Month 对象,而不是仅仅使用整数,以使您的代码更具自记录性、确保有效值并提供 type-safety。

LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;

DayOfWeek

对于星期几,使用DayOfWeek 枚举类。它提供 7 个实例,一个用于每周的每一天,周一至周日。

DayOfWeek dow = ld.getDayOfWeek() ;  // Get an enum representing the day-of-week of this date, such as `DayOfWeek.MONDAY`.

调整日期

我们需要找到上一个星期一,或者如果已经是星期一,则坚持今天的日期。要移至此类日期,请使用TemporalAdjuster 的TemporalAdjusters.previousOrSame 实现。

TemporalAdjuster ta = TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) ;
LocalDate previousOrSameMonday = ld.with( ta ) ;

通过调用LocalDate.plusDays() 来收集您想要的日期。注意 java.time 如何使用immutable objects。我们根据原始值而不是 mutating(更改)原始值获得一个新对象。

// Hard-coded `7` is the seven days in a week.
List< LocalDate > dates = new ArrayList<>( 7 ) ;
for( int i = 0 , i < 7 , i ++ ) {
    LocalDate localDate = previousOrSameMonday.plusDays( i ) ;
    dates.add( localDate ) ;
}

关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

  • Java SE 8、Java SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实现。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 和 Java SE 7
    • ThreeTen-Backport 中的大部分 java.time 功能都向后移植到 Java 6 和 7。
  • Android
    • java.time 类的 Android 捆绑包实现的更高版本。
    • 对于早期的 Android (ThreeTenABP 项目采用 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…。

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。


更新:Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。本节留给历史。

乔达时间

解决这个问题的最佳方法是什么?

最好的方法是避免使用臭名昭著的麻烦 java.util.Date 和 .Calendar 类,而是使用Joda-Time 库。 Joda-Time 适用于 Android。

Joda-Time 为没有任何时间或时区的纯日期值提供 LocalDate 类。

示例代码

这是一些使用 Joda-Time 2.3 的示例代码。

LocalDate localDate = new LocalDate( 2011, DateTimeConstants.SEPTEMBER, 4 );
LocalDate firstDateOfWeek = localDate.withDayOfWeek( DateTimeConstants.MONDAY );
for ( int i = 0; i < 7; i++ ) {
    LocalDate someDateOfWeek = firstDateOfWeek.plusDays( i );
    System.out.println( "someDateOfWeek: " + someDateOfWeek + "  le jour de la semaine: " + someDateOfWeek.dayOfWeek().getAsText( Locale.CANADA_FRENCH ) );
}

运行时……

someDateOfWeek: 2011-08-29  le jour de la semaine: lundi
someDateOfWeek: 2011-08-30  le jour de la semaine: mardi
someDateOfWeek: 2011-08-31  le jour de la semaine: mercredi
someDateOfWeek: 2011-09-01  le jour de la semaine: jeudi
someDateOfWeek: 2011-09-02  le jour de la semaine: vendredi
someDateOfWeek: 2011-09-03  le jour de la semaine: samedi
someDateOfWeek: 2011-09-04  le jour de la semaine: dimanche

周数

额外提示:如果您想要 ISO 8601 标准定义的周数,请调用 weekOfWeekYear 方法。像这样:

int weekNumber = firstDateOfWeek.getWeekOfWeekyear();

您的日期是第 35 周。

【讨论】:

    【解决方案3】:

    人类千万不要使用android生物的语言,开玩笑的,输入日期,是Java棺材的又一钉子。 这个误解问题的核心是糟糕的文档和两个事实,月份从 0 开始,年份从 1900 开始计数,但在 Calendar 和他的后代中却没有。 还有一个事实,在 Date Sunday 中为 0,但在 Calendar Sunday 中为 1。

    //THIS WORKS CORRECTLY
    Date my = new Date(1986 - 1900, 04 - 1, 26);
    System.out.println(my);
    System.out.println(my.getDay());
    
    Calendar cal = Calendar.getInstance(TimeZone.getDefault());
    cal.set(1986, 04 - 1, 26);
    System.out.println(cal.getTime());
    System.out.println(cal.get(Calendar.DAY_OF_WEEK)-Calendar.SUNDAY);
    

    另一种更方便的方式

    //ONE AND ONLY, HUMAN FRIENDLY WAY TO ENTER DATE INTO JAVA
    String date = "1986-04-26:01:23:47";
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd:HH:mm:SS");
    Date convertedDate = (Date) formatter.parse(date);
    System.out.println(convertedDate);
    

    很简单,不是吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多