简体   繁体   English

Android日历问题与星期几

[英]Android Calendar problem with day of the week

I'm trying to list all days of the week for current week from Monday to Sunday. 我想列出周一到周日当周的所有日子。 For example, today (day of this posting) is September 4th, 2011 and it's Sunday. 例如,今天(发布的那天)是2011年9月4日,也就是星期天。

I'm starting calendar and setting first day of the week to Monday: 我正在开始日历并将一周的第一天设置为星期一:

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

When I check day of the month, I get correct result: 当我检查每月的某天时,我得到了正确的结果:

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

But when I set weekday to Monday, it jumps to the next week instead of returning Monday of this week: 但是当我将工作日设置为星期一时,它会跳到下周,而不是在本周一返回:

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)

Even setting weekday to Sunday returns next Sunday and not today. 即使设定工作日到周日也会在下周日返回,而不是今天。

Can someone explain why it works that way and what's the best way to solve this problem? 有人可以解释为什么它以这种方式工作,解决这个问题的最佳方法是什么?

In fact, when I check my own tests, it seems to work as expected, except when the date is not set again : 事实上,当我检查自己的测试时,它似乎按预期工作, 除非没有再次设置日期

Display 4-29 : 显示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);

Display 5-5 : 显示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);

Display 14-12 : 显示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);

So, this doesn't work: 所以,这不起作用:

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

and this works : 这工作

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

and this works too: 也有效:

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

But this one doesn't: 但是这个没有:

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

tl;dr TL;博士

Use LocalDate , never Calendar . 使用LocalDate ,从不使用Calendar

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

java.time java.time

Avoid the troublesome old legacy class Calendar as it is now legacy, supplanted by the java.time classes (specifically ZonedDateTime ). 避免使用麻烦的旧遗留类Calendar因为它现在已经遗留下来,取而代之的是java.time类(特别是ZonedDateTime )。

For date-only values without time-of-day, use LocalDate rather than a date+time type like Calendar or ZonedDateTime . 对于没有时间的仅限日期的值,请使用LocalDate而不是日期+时间类型,如CalendarZonedDateTime The LocalDate class represents a date-only value without time-of-day and without time zone. LocalDate类表示没有时间且没有时区的仅日期值。

A time zone is crucial in determining a date. 时区对于确定日期至关重要。 For any given moment, the date varies around the globe by zone. 对于任何给定的时刻,日期在全球范围内因地区而异。 For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec . 例如, 法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天”。

If no time zone is specified, the JVM implicitly applies its current default time zone . 如果未指定时区,则JVM会隐式应用其当前的默认时区 That default may change at any moment, so your results may vary. 该默认值可能随时更改,因此您的结果可能会有所不同。 Better to specify your desired/expected time zone explicitly as an argument. 最好明确指定您期望/预期的时区作为参数。

Specify a proper time zone name in the format of continent/region , such as America/Montreal , Africa/Casablanca , or Pacific/Auckland . continent/region的格式指定适当的时区名称 ,例如America/MontrealAfrica/CasablancaPacific/Auckland Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!). 切勿使用3-4字母缩写,例如ESTIST因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

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

If you want to use the JVM's current default time zone, ask for it and pass as an argument. 如果要使用JVM的当前默认时区,请求它并作为参数传递。 If omitted, the JVM's current default is applied implicitly. 如果省略,则隐式应用JVM的当前默认值。 Better to be explicit, as the default may be changed at any moment during runtime by any code in any thread of any app within the JVM. 最好是显式的,因为默认情况下可以在运行期间随时由JVM中任何应用程序的任何线程中的任何代码更改。

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

Or specify a date. 或指定日期。 You may set the month by a number, with sane numbering 1-12 for January-December. 您可以将月份设置为一个数字,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.

Or, better, use the Month enum objects pre-defined, one for each month of the year. 或者,更好的是,使用预定义的Month枚举对象,一年中的每个月一个。 Tip: Use these Month objects throughout your codebase rather than a mere integer number to make your code more self-documenting, ensure valid values, and provide type-safety . 提示:在整个代码库中使用这些Month对象而不仅仅是整数,以使代码更加自我记录,确保有效值并提供类型安全性

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

DayOfWeek

For day-of-week, use the DayOfWeek enum class. 对于星期几,请使用DayOfWeek枚举类。 It offers seven instances, one for each day of the week, Monday-Sunday. 它提供七个实例,一周中的每一天,周一至周日。

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

Adjusting date 调整日期

We need find the previous Monday, or stick with today's date if already a Monday. 我们需要找到上一个星期一,或者如果已经是星期一那么坚持今天的日期。 To move to such dates, use the TemporalAdjusters.previousOrSame implementation of TemporalAdjuster . 要移动到这样的日期,使用TemporalAdjusters.previousOrSame实施TemporalAdjuster

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

Collect your desired dates by incrementing with a call to LocalDate.plusDays() . 通过调用LocalDate.plusDays()来增加收集所需的日期。 Notice how java.time uses immutable objects . 请注意java.time如何使用不可变对象 We get a fresh object based on the values of the original rather than mutating (altering) the original. 我们根据原始值获得一个新对象,而不是改变 (改变)原始对象。

// 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 ) ;
}

About java.time 关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于Java 8及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat . 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes. 现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial . 要了解更多信息,请参阅Oracle教程 And search Stack Overflow for many examples and explanations. 并搜索Stack Overflow以获取许多示例和解释。 Specification is JSR 310 . 规范是JSR 310

You may exchange java.time objects directly with your database. 您可以直接与数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later. 使用符合JDBC 4.2或更高版本的JDBC驱动程序 No need for strings, no need for java.sql.* classes. 不需要字符串,不需要java.sql.*类。

Where to obtain the java.time classes? 从哪里获取java.time类?

The ThreeTen-Extra project extends java.time with additional classes. ThreeTen-Extra项目使用其他类扩展了java.time。 This project is a proving ground for possible future additions to java.time. 该项目是未来可能添加到java.time的试验场。 You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more . 您可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter ,和更多


UPDATE: The Joda-Time project is now in maintenance mode , with the team advising migration to the java.time classes. 更新: Joda-Time项目现在处于维护模式 ,团队建议迁移到java.time类。 This section left for history. 这部分留给了历史。

Joda-Time 乔达时间

what's the best way to solve this problem? 什么是解决这个问题的最佳方法?

The best way is to avoid using the notoriously troublesome java.util.Date & .Calendar classes, and instead use the Joda-Time library. 最好的方法是避免使用臭名昭着的java.util.Date和.Calendar类,而是使用Joda-Time库。 Joda-Time works in Android. Joda-Time适用于Android。

Joda-Time offers the LocalDate class for date-only values without any time or time zone. Joda-Time为没有任何时间或时区的仅限日期值提供LocalDate类。

Example Code 示例代码

Here is some example code using Joda-Time 2.3. 以下是使用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 ) );
}

When run… 跑的时候......

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

Week Number 周数

Bonus tip: If you want the week number as defined by the ISO 8601 standard, call the weekOfWeekYear method. 额外提示:如果您想要ISO 8601标准定义的周数,请调用weekOfWeekYear方法。 Like this: 像这样:

int weekNumber = firstDateOfWeek.getWeekOfWeekyear();

Your date is in week 35. 你的约会对象是在第35周。

Humans must not use language of android beings, just kidding, date entering, is another one nail to java coffin. 人类不能使用Android生物的语言,只是开玩笑,日期进入,是另一个钉入java棺材。 core of this misunderstanding problem is bad documentation and two facts, months numbered from 0, and year counts from 1900, but not in Calendar and his descendants. 这个误解问题的核心是糟糕的文件和两个事实,月份从0开始,年份从1900开始,但不在日历和他的后代中。 and one more fact, in Date Sunday is 0, but in Calendar Sunday is 1. 还有一个事实,在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);

another, much more convenient way 另一个,更方便的方式

//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);

quite simple, is not it? 很简单,不是吗?

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

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