Jadenkun
Jadenkun

Reputation: 327

Constructing String from given Date object in Java

I'm working with a calendar and a NoSQL database that stores data whenever a user clicks on a specific date in the calendar.

I'm currently storing data using nodes in format of "YYYYMMDD"

e.g. -

20190823:
       generatedKeyofUser: <user's data> 
20190824:
       generatedKeyofAnotherUser: <another's data>

I wish to be able to fetch not only the date the user has clicked on but also up to 4 days prior.

for example, if the user clicked on Jan 2nd 2019, I wish to query the database with the following dates "20190102", "20190101, "20181231", "20181230"

Currently I'm constructing a string in said format like so -

calendarView.setOnDayClickListener(event-> {
            Calendar clickedDate = event.getCalendar();
            Intent intent = new Intent(CalendarActivity.this, DateActivity.class);
            SharedPreferences.Editor editor = sp.edit();
            String year = Integer.toString(clickedDate.get(Calendar.YEAR));
            String month = new DecimalFormat("00").format(clickedDate.get(Calendar.MONTH) + 1);
            String day = new DecimalFormat("00").format(clickedDate.get(Calendar.DAY_OF_MONTH));
            editor.putString("year", year);
            editor.putString("month", month);
            editor.putString("day",day );
            editor.apply();
            startActivity(intent);
        });

sending it to the other activity so it can fetch the user's data from the database.

Edit: I edited my code as shown below -

calendarView.setOnDayClickListener(event-> {
            Intent intent = new Intent(CalendarActivity.this, DateActivity.class);
            SharedPreferences.Editor editor = sp.edit();

            // Chosen date
            Calendar clickedDate = event.getCalendar();

            // Yesterday's chosen date
            Calendar clickedDateYesterday = (Calendar) clickedDate.clone();
            clickedDateYesterday.add(Calendar.DAY_OF_YEAR, -1);

            // Two days ago from chosen date
            Calendar clickedDateTwoDaysAgo = (Calendar) clickedDate.clone();
            clickedDateTwoDaysAgo.add(Calendar.DAY_OF_YEAR, -2);

            // Three days ago from chosen date
            Calendar clickedDateThreeDaysAgo = (Calendar) clickedDate.clone();
            clickedDateYesterday.add(Calendar.DAY_OF_YEAR, -3);

            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
            String formattedChosenDate = formatter.format(clickedDate.getTime());
            String formattedYesterdayChosenDate = formatter.format(clickedDateYesterday.getTime());
            String formattedTwoDaysAgoChosenDate = formatter.format(clickedDateTwoDaysAgo.getTime());
            String formattedThreeDaysAgoChosenDate = formatter.format(clickedDateThreeDaysAgo.getTime());
            Log.i("dates", "Chosen Date - " + formattedChosenDate + " ; Yesterday - " + formattedYesterdayChosenDate + " ; Two Days Ago - "+
                    formattedTwoDaysAgoChosenDate + " ; Three Days Ago - " + formattedThreeDaysAgoChosenDate);
            editor.apply();
            startActivity(intent);
        });

But the Log shows

Chosen Date - 20190808 ; Yesterday - 20190804 ; Two Days Ago - 20190806 ; Three Days Ago - 20190808

instead of

Chosen Date - 20190808 ; Yesterday - 20190807 ; Two Days Ago - 20190806 ; Three Days Ago - 20190805

Upvotes: 0

Views: 78

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 340200

tl;dr

( ( GregorianCalendar ) myCalendar )   // Cast from interface to likely concrete class.
.toZonedDateTime()                     // Convert from legacy class to modern. Returns a `ZonedDateTime` object.
.minusDays( 1 )                        // Subtract a day to get same time-of-day yesterday. Returns a new `ZonedDateTime` object.
.format(                               // Generate text.
    DateTimeFormatter.BASIC_ISO_DATE   // Specify standard ISO 8601 "basic" format of YYYYMMDD.
)                                      // Returns a `String`. 

20190807

…and…

GregorianCalendar.from (                    // Convert from modern `ZonedDateTime` class to legacy class.
    ( ( GregorianCalendar ) myCalendar )    // Cast from interface to likely concrete class.
    .toZonedDateTime()                      // Convert from legacy class too modern. Returns a `ZonedDateTime` object.
    .minusDays( 1 )                         // Subtract a day to get same time-of-day yesterday in the same zone. Returns a new `ZonedDateTime` object.
)                                           // Returns a `GregorianCalendar` object, which is also a `Calendar` object.

Avoid legacy date-time classes

The terrible legacy date-time classes such as Calendar and SimpleDateFormat were supplanted years ago by the modern java.time classes defined in JSR 310.

Apparently you are using an old calendaring widget that has yet to be updated to java.time. If so, convert by calling new to…/from… methods added to the old classes.

ZonedDateTime

To convert from Calendar to ZonedDateTime, first cast to GregorianCalendar, the common concrete implementation.

GregorianCalendar gc = ( GregorianCalendar ) c ;

Now convert.

ZonedDateTime zdt = gc.toZonedDateTime() ;

You want only the date apparently, without the time-of-day and without the time zone. So extract a LocalDate.

LocalDate ld = zdt.toLocalDate() ;

Generate a textual representation of that date value using DateTimeFormatter class. Your desired format YYYYMMDD happens to be the “basic” variant of the standard ISO 8601 format YYYY-MM-DD. Your desired formatting pattern is already defined as DateTimeFormatter.BASIC_ISO_DATE.

String output = ld.format( DateTimeFormatter.BASIC_ISO_DATE ) ;

Date math

You also want yesterday and day before that.

LocalDate yesterday = ld.minusDays( 1 ) ;
LocalDate dayBeforeYesterday = ld.minusDays( 2 ) ;

Start of day

If you want to convert back to a Calendar object, you need to think first about the time-of-day. A Calendar represents a moment, a date with time-of-day in the context of a time zone.

If you want to get the first moment of the day, let java.time determine that moment. Do not assume the day starts at 00:00. Some days on some dates in some time zones may start at another time such as 01:00 because of anomalies such as Daylight Saving Time (DST).

For the necessary zone, we can extract the zone used in our ZonedDateTime object converted from the GregorianCalendar.

ZoneId z = zdt.getZone() ;
ZonedDateTime startOfYesterday = yesterday.atStartOfDay( z ) ;

Convert to GregorianCalendar.

 GregorianCalendar gc = GregorianCalendar.from( startOfYesterday ) ;

Pass that gc (which is also a Calendar as well as a GregorianCalendar) to your calendaring widget.

If instead of first moment of the day, if you had wanted the same time-of-day as zdt we started with, do the date math on that object.

ZonedDateTime zdtYesterdaySameTime = zdt.minusDays( 1 ) ;
ZonedDateTime zdtDayBeforeYesterdaySameTime = zdt.minusDays( 2 ) ;

Tip: Try to find a calendaring widget that uses LocalDate class, if the date is all you care about without time-of-day and without time zone.


Table of date-time types in Java, both modern and legacy.


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.

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

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

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?

Upvotes: 2

faogustavo
faogustavo

Reputation: 146

First of all, to get the range, you can clone the current selection and remove the days you want (in your example, 4 days).

Calendar clickedDate = event.getCalendar();
Calendar offsetDate = clickedDate.clone();
offset.add(Calendar.DAY_OF_YEAR, -4)

To format the value as you want, you can create a DateFormater to achieve that.

SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
String initialDate = formatter.format(offsetDate.getTime());
String finalDate = formatter.format(clickedDate.getTime());

If you wave to get all strings from the dates in the range, you can just run a "for", to iterate and generate the values between the two values, using the .add function as I showed in the first example.

Upvotes: 1

Related Questions