Celvin
Celvin

Reputation: 31

Date as double digit

I've a code to get year, month and day for one of my application.

    package com.cera.hyperionUtils;
import java.util.*;

public class HypDate {

 public static int curdate(int field)
 {
  //1. Specify integer 1 for YEAR, 2 for MONTH, 5 DAY_OF_MONTH
  Calendar c = new GregorianCalendar();
  c.setLenient(true); //Allow overflow

  //2. Extract and Return result
   if (field == 2) {
    field = c.get(Calendar.MONTH) + 1;
  }   
  return c.get(field);
 }

 public static void main(String[] args)
 {
 System.out.println(HypDate.curdate(2));

 }
} 

But when i pass 2 it is giving 0 year and day prints correctly.....Also i was trying to make month as double digit. (like 01 for 1)

Can someone please help me....? (I''m very new to java coding)

Upvotes: 3

Views: 9905

Answers (5)

Ahmed Bērziņš
Ahmed Bērziņš

Reputation: 1

Separate concerns in your design

First I don’t like the design of your curdate method, so I encourage you to change it. It mixes two concerns in a way that is sure to lead to errors in rare cases:

  1. Getting the current date,
  2. getting a field from that date.

A method that returns one field of the current date invites to be called more than once if the caller needs two or three fields, which will often be the case. If two such calls happen over midnight, you are getting fields that you think belong together, but really are inconsistent since they belong to two different dates.

Instead draw the date from the clock only once and then extract the field or fields that you need.

java.time

Nowadays use java.time, the modern Java date and time API, for all of your date work. The Calendar and GregorianCalendar classes that you were trying to use, were cumbersome to work with and are outdated since the advent of java.time in Java 8, which was in 2014, more than 10 years ago.

You don’t need to write a method to get the current date since it is built in. Just call it:

    LocalDate today = LocalDate.now(ZoneId.of("Australia/Tasmania"));

Substitute your desired time zone since it is never the same date in all time zones.

Now you want to extract year, month and date fields as two-digit values. To represent two digits we need a String since an int has no idea whether its decimal representation has a leading 0 or not. A fairly general method for extracting such fields is:

public static String extractTwoDigitField(
        TemporalAccessor dateTime, TemporalField field) {
    if (! Objects.equals(Chronology.from(dateTime), IsoChronology.INSTANCE)) {
        throw new IllegalArgumentException("The date must have ISO chronology");
    }
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendValueReduced(field, 2, 2, LocalDate.from(dateTime))
            .toFormatter(Locale.of("en"));
    return formatter.format(dateTime);
}

The way to get one or more fields as a string from your date is through a DateTimeFormatter. Since year is in practice 4 digits, I am using the appendValueReduced mehtod of DateTimeFormatterBuilder to force the value to be only two digits.

To demonstrate the working of the method here we are extracting each of year, month and day:

    List<TemporalField> fields = List.of(
            ChronoField.YEAR, ChronoField.MONTH_OF_YEAR, ChronoField.DAY_OF_MONTH);
    for (TemporalField field : fields) {
        System.out.format(Locale.ROOT, "%11s: %s%n",
                field, extractTwoDigitField(today, field));
    }

When I ran the snippet just now, the output was:

       Year: 24
MonthOfYear: 12
 DayOfMonth: 08

We see that year (2024) and day-of-month (8) each come in two digits as we specified.

A simpler design for your (real) requirements

As discussed in the comments, a somewhat simpler design would just return all three relevant fields — year, month and day — and leave it to the caller which to use and which to ignore. To hold three fields we first declare a record type, for example:

public record DateFields(String year, String month, String day) {}

Now the method can be:

    private static DateTimeFormatter twoDigitYearFormatter
            = DateTimeFormatter.ofPattern("uu");
    private static DateTimeFormatter twoDigitMonthFormatter
            = DateTimeFormatter.ofPattern("MM");
    private static DateTimeFormatter twoDigitDayOfMonthFormatter
            = DateTimeFormatter.ofPattern("dd");

    public static DateFields extractTwoDigitFields(LocalDate date) {
        return new DateFields(date.format(twoDigitYearFormatter),
                date.format(twoDigitMonthFormatter),
                date.format(twoDigitDayOfMonthFormatter));
    }

Also trying this method out:

    LocalDate today = LocalDate.now(ZoneId.of("Australia/Tasmania"));
    DateFields fields = extractTwoDigitFields(today);
    System.out.println("       Fields: " + fields);
    System.out.println("         Year: " + fields.year());
    System.out.println("Month of year: " + fields.month());
    System.out.println(" Day of month: " + fields.day());

Example output:

       Fields: DateFields[year=24, month=12, day=15]
         Year: 24
Month of year: 12
 Day of month: 15

Depending on your needs you may of course opt to return a Map, a List or some other data structure instead of my record.

Link

Trail: Date Time - The Java™ Tutorials explaining how to use java.time

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 339332

Avoid legacy date-time classes

Calendar c = new GregorianCalendar();

In modern Java, never use these terribly-flawed legacy classes. Use only java.time classes.

java.time.LocalDate

For a date-only value, use java.time.LocalDate.

Time zone

You are ignoring the crucial issue of time zone. For any given moment, the time and therefore the date vary around the globe by time zone. It is “tomorrow” in Tokyo Japan while “yesterday” in Toledo Ohio US.

To determine the current date, you must specify a time zone.

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

switch on number for field access

As for your extracting the year-month-day from the date by some arbitrary number, I do not understand your use-case here. But if you insist on that, you can use switch expression in Java 14+.

int fieldNumber = 2;
int x =
        switch ( fieldNumber )
        {
            case 1 -> today.getYear ( );
            case 2 -> today.getMonthValue ( );
            case 3 -> today.getDayOfMonth ( );
            default -> throw new IllegalArgumentException ( "Invalid field number: " + fieldNumber );
        };

Your approach seems like a design problem. At the very least you likely should be using an Enum object rather than a mere int.

Upvotes: 1

Powerlord
Powerlord

Reputation: 88806

Rather than returning these one by one, you may just want to use a SimpleDateFormat to format it.

Say I want a date as year-month-day:

// Necessary imports
import java.text.DateFormat;
import java.text.SimpleDateFormat;

// Declare class and stuff before this

public static String getFormattedDate() {
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

    return df.format(new Date());
}

public static void main(String[] args) {
    System.out.println(getFormattedDate());
}

Outputs 2010-10-29

Edit:

Since you just want the month, you can do this:

public static String getFormattedMonth() {
    DateFormat df = new SimpleDateFormat("MM");

    return df.format(new Date());
}

Upvotes: 5

JohnnyO
JohnnyO

Reputation: 3068

The problem comes from the fact that when you are getting the month information, you call c.get() twice, which you don't want to do. Instead, you should directly return after you get the first value

  //1. Specify integer 1 for YEAR, 2 for MONTH, 5 DAY_OF_MONTH
  Calendar c = new GregorianCalendar();
  c.setLenient(true); //Allow overflow

  //2. Extract and Return result
   if (field == Calendar.MONTH) {
    return c.get(field) + 1;  //because Java months are 0-based
  } else {  
    return c.get(field);
 }

Upvotes: 1

Jack
Jack

Reputation: 133609

   if (field == 2) {
    field = c.get(Calendar.MONTH) + 1;
  }   
  return c.get(field);

You retrieve the correct month as an index and then use that index to retrieve another field that will be unknown and related in how the constants are saved. Just return the value before, without using a second get.

Maybe you meant

   if (field == 2) {
    field = Calendar.MONTH;
  }   
  return c.get(field) + 1;

but I don't get why you are redefining that constants instead that use the one already provided..

Upvotes: 3

Related Questions