简体   繁体   中英

android how to identify the same days of a week

To manage dates I'm using the class Calendar. I'm saving timestamps in a DB, using

Calendar timestamp = Calendar.getInstance();

I'm able to save and retrieve the dates from the DB using long values to store dates.

This is my problem: when I have a timestamp to store, I need to know if in the DB there is already a timestamp belonging to the same week. To do this I thought I could use the couple of methods:

timestamp.get(Calendar.YEAR) and timestamp.get(Calendar.WEEK_OF_YEAR)

to uniquely identify a week, but this isn't true for weeks having days belonging to two consecutive years.

This is an example of what I get for the last week of the year 2014.

Saturday, December 27, 2014 year: 2014 week: 52

Sunday, December 28, 2014 year: 2014 week: 1

Monday, December 29, 2014 year: 2014 week: 1

Tuesday, December 30, 2014 year: 2014 week: 1

Wednesday, December 31, 2014 year: 2014 week: 1

Thursday, January 1, 2015 year: 2015 week: 1

Friday, January 2, 2015 year: 2015 week: 1

Saturday, January 3, 2015 year: 2015 week: 1

Sunday, January 4, 2015 year: 2015 week: 2

The days from 28/12/2014 to 3/1/2015 belong to the same week (in java the week starts on Sunday), but they got a different year, so the couple (YEAR, WEEK_OF_YEAR) doesn't give me the info that they belong to the same week.

How can I solve?

As suggested by @basil, I tried the Joda-Time library and I found there the solution to my problem. To get the week of the year I can use the method getWeekOfWeekyear() and to get the year I can use the method getWeekyear()

In this way, the couple of values uniquely identify the day belonging to the same week, with the week starting in Monday and ending in Sunday (another added value compared to the Java calendar class).

Following the same example of the question.

DateTime[] timestamp = new DateTime[10];

for(int i=0; i<10; i++){
   //set the start of the period
   DateTime time = new DateTime(2014, 12, 27, 18, 30, 50);
   //add i days to the start date
   timestamp[i] = time.plus(Period.days(i));

   int weekYear = timestamp[i].getWeekyear();
   int weekOfWeekyear = timestamp[i].getWeekOfWeekyear();

   Log.d(Constants.LOG_TAG, timestamp[i].toString("EEEE, MMMM d, yyyy") + " | year.week = " + weekYear + "." + weekOfWeekyear);
}

The result is:

Saturday, December 27, 2014 | year.week = 2014.52

Sunday, December 28, 2014 | year.week = 2014.52

Monday, December 29, 2014 | year.week = 2015.1

Tuesday, December 30, 2014 | year.week = 2015.1

Wednesday, December 31, 2014 | year.week = 2015.1

Thursday, January 1, 2015 | year.week = 2015.1

Saturday, January 3, 2015 | year.week = 2015.1

Sunday, January 4, 2015 | year.week = 2015.1

Monday, January 5, 2015 | year.week = 2015.2

It looks like there are a lot of other advantages in using this library and it will be useful for other calculations I have to perform.

Thank you @basil.

To get the first and last day of the week relative to a date, do this:

Calendar cal = Calendar.getInstance();
cal.setTime(date); // date to check
cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
Date startOfWeek = cal.getTime();
cal.add(Calendar.DAY_OF_MONTH, 6);
Date endOfWeek = cal.getTime();

Disclaimer: I stole part of that from How to get the first day of the current week and month?

Then use a where clause that check the date range.

SELECT * FROM mytable WHERE mydate >= ? AND mydate <= ?

If your stored dates include time, you'd want the start of next week as the upper-exclusive boundary, by adding 7 instead of 6 days above.

SELECT * FROM mytable WHERE mydate >= ? AND mydate < ?

To show the date logic, here's some test code:

// Start on 12/27/2014
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2014, Calendar.DECEMBER, 27);

// Collect 10 dates to test (start date + next 9 days)
Date[] dates = new Date[10];
dates[0] = cal.getTime();
for (int i = 1; i < dates.length; i++) {
    cal.add(Calendar.DAY_OF_MONTH, 1);
    dates[i] = cal.getTime();
}

// Test loop    
SimpleDateFormat fmt = new SimpleDateFormat("EEE d/M/yyyy");
for (Date date : dates) {

    // Code to test
    cal.setTime(date);
    cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
    Date startOfWeek = cal.getTime();
    cal.add(Calendar.DAY_OF_MONTH, 6);
    Date endOfWeek = cal.getTime();

    // Show result
    System.out.println(fmt.format(date) + "  ->  " + fmt.format(startOfWeek) +
                                             " - " + fmt.format(endOfWeek));
}

Output

Sat 27/12/2014  ->  Sun 21/12/2014 - Sat 27/12/2014
Sun 28/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
Mon 29/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
Tue 30/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
Wed 31/12/2014  ->  Sun 28/12/2014 - Sat 3/1/2015
Thu 1/1/2015  ->  Sun 28/12/2014 - Sat 3/1/2015
Fri 2/1/2015  ->  Sun 28/12/2014 - Sat 3/1/2015
Sat 3/1/2015  ->  Sun 28/12/2014 - Sat 3/1/2015
Sun 4/1/2015  ->  Sun 4/1/2015 - Sat 10/1/2015
Mon 5/1/2015  ->  Sun 4/1/2015 - Sat 10/1/2015

tl;dr

You must understand the difference between a “normal” calendar year and a standard week-based year . The first/last few days of a calendar year may appear in the previous/next week-based year.

2014年12月日历年的屏幕截图,显示标准的基于周的周年

The calendar year of 2014 ends on December 31, 2014 of course. But that last day of 2014 lands in the next week-based-year, 2015.

The last day of week-based year 2014 ends on December 28, 2014 (calendar year). The first day of 2015 week-based-year is December 29, 2014 (calendar year). The calendar graphic above should make these facts clear.

2014-12-28 = 2014-W52-7

2014-12-29 = 2015-W01-1

2015-01-01 = 2015-W01-4

Ask for week-based week and year number:

myZonedDateTime.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR )

…and…

myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR )

Simply put: For the last/first few days of the year, calling myZonedDateTime.getYear() and myZonedDateTime.get( IsoFields.WEEK_BASED_YEAR ) may differ in results .

java.time

The modern approach uses the java.time classes that supplanted the troublesome old legacy date-time classes.

To manage dates I'm using the class Calendar. I'm saving timestamps in a DB, using Calendar timestamp = Calendar.getInstance();

Instead, use ZoneDateTime to capture the current moment with a wall-clock time used by people of a certain region (time zone).

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;

I'm able to save and retrieve the dates from the DB using long values to store dates.

No, use an approriate data type, both in defining your database column and your Java code.

To store a moment, a specific moment on the timeline, in a SQL standard compliant database, define your column as TIMESTAMP WITH TIME ZONE .

With JDBC 4.2 and later, you can directly exchange java.time objects with your database.

myPreparedStatement.setObject( … , zdt ) ;

Retrieve as an Instant , a moment in UTC, may be the best route in the other direction. Then adjust into your desired time zone.

Instant instant = myResultSet.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

This is my problem: when I have a timestamp to store, I need to know if in the DB there is already a timestamp belonging to the same week. To do this I thought I could use the couple of methods: timestamp.get(Calendar.YEAR) and timestamp.get(Calendar.WEEK_OF_YEAR)

Unfortunately, the meaning of a week in the confusing Calendar class varies by locale.

Perhaps your own definition agrees with the standard ISO 8601 definition of a week .

  • The first day is Monday, running through Sunday.
  • Week number one of a week-based year contains the first Thursday of the calendar year.
  • A week-based year has either 52 or 53 weeks.
  • The first/last few days of a calendar year may appear in the previous/next week-based year.

Note that determining a week requires determining a date. And determining a date requires a time zone. For any given moment, the date varies around the globe by zone. So be clear on your use of time zone ( ZoneId ) as seen above. In other words, a week number is contextual; it depends on a date in a particular zone.

Access a TemporalField in ZonedDateTime::get . The IsoFields class defines the constants you need.

int week = zdt.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) ;
int weekBasedYear = zdt.get( IsoFields.WEEK_BASED_YEAR ) ;

Even better, add the ThreeTen-Extra library to your project to use YearWeek class.

org.threeten.extra.YearWeek yw = YearWeek.from( zdt ) ;

to uniquely identify a week, but this isn't true for weeks having days belonging to two consecutive years.

This is an example of what I get for the last week of the year 2014.

Saturday, December 27, 2014 year: 2014 week: 52

In a standard week, 2014-12-27 is in week 52.

YearWeek.from( LocalDate.parse( "2014-12-27" )  ).toString()

2014-W52

The end of 2014 happens to land in the following week-based year (2015).

YearWeek.from( LocalDate.parse( "2014-12-31" ) ).toString()

2015-W01

You can compare YearWeek . Call equals , isBefore , or isAfter methods.

boolean sameYearWeek = YearWeek.from( someZdt ).equals( YearWeek.from( otherZdt ) ) 

Terminology

So, be sure that in your documentation, code comments, and discussions, you always distinguish between a calendar year and week-based year. Never say "the year" as that is terribly ambiguous.

Add in financial years for even more confusion. And some industries have their own definitions of years and seasons and so on. So be careful with your terms.

Beware of calendaring software settings

Never assume the definition of a week number. Be sure the source of such a number has the same definition of week as you, such as ISO 8601 definition.

For example, the Calendar app supplied by Apple with macOS defaults to a "Gregorian" calendar definition of week. As for what that means, I do not know as I could not find any documentation as to their intent/definition. For ISO 8601 weeks, you must change a setting away from default .


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes.

To learn more, see the Oracle Tutorial . And search Stack Overflow for many examples and explanations. Specification is JSR 310 .

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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