I'm writing a class that is dependent on months and how many days each month has. At the beginning of my class, I've written:
static final int JAN = 1;
static final int FEB = 2;
...
static final int DEC = 12;
It looks like a messy way of doing this, and I feel that enums would be more efficient. I need to be able to "increment" the month (where DEC + 1 = JAN), and need to be able to know have many days there are in the month (with FEB having 28).
I see two ways of doing this:
My current way, defining static final ints and using a switch statement to find the number of days, and when incrementing I always checks for whether the value would be greater than 12.
Create a set of enums, each with inputs for which month of the year it is and how many days it contains, and defining an "increment" method.
Is there a better way of doing this, or is (presumably) 2. the best way of doing this?
I recommend using java.util.Calendar for this sort of situation. It already has constants defined for virtually every date- and time-related concept that you'll ever have to use; plus, it is smart enough to know about things like February's wishy-washy length (29 days 25% of the time).
Java has a Class that does what you need. See the Calendar class, which also has constants for Month and Day of Week:
https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html
Related
I have a Duration, like P3M (3 months). How can I get number of days it is from now?
All I have now is this:
Duration.parseWeekBasedPeriod("P3M")
I know the period parameter will never be shorter than 1 week, so parseWeekBasedPeriod() should be ok. But I'm reading JavaDoc, and I can't figure out how to get those days.
I understand, the problem is that months can has 31, 30, 29 and 28 days.
Using parseWeekBasedPeriod(...) is certainly wrong if you want to apply durations measured in months. This very special method handles week based years which can last either 364 or 371 days (52 or 53 weeks). So I suggest just to use the standard parsing method for calendar-based durations. The following code also strongly simplifies the evaluation of days between two dates (see last line).
Duration<CalendarUnit> duration = Duration.parseCalendarPeriod("P3M");
PlainDate today = PlainDate.nowInSystemTime();
PlainDate later = today.plus(duration);
long days = CalendarUnit.DAYS.between(today, later);
By the way, I have tested the method for weekbased durations once again. It will usually throw an exception if it tries to parse months. You didn't seem to have seen any exception so I assume that the fact that you use untyped constructs like "val" has shadowed the necessary type information in processing the duration string (and Time4J is a strongly typed library). Hence - if technically possible for you -, I strongly recommend to use type-safe code as shown in my solution.
Finaly figured it out:
val period = Duration.parseWeekBasedPeriod("P3M")
val start = PlainDate.nowInSystemTime()
val end = start.plus(period)
val days: Long = Duration.`in`(CalendarUnit.DAYS).between(start, end).getPartialAmount(CalendarUnit.DAYS)
I need to determine the number of days in the month represented by the month portion of a java.time.ZonedDateTime. I've come up with three ways that appear to work. Given:
ZonedDateTime date = _some date_;
Option 1:
int daysInMonth = date.getMonth().length(date.getChronology().isLeapYear(date.getYear()));
Option 2:
int daysInMonth = date.getMonth().length(date.toLocalDate().isLeapYear());
Option 3:
int daysInMonth = YearMonth.from(date).lengthOfMonth();
I discovered option 3 while checking this question for duplicates (Number of days in particular month of particular year?). Am I missing any other options? Is one of these options superior to another? Under what circumstances will that superiority manifest?
If I'm reading things correctly, the first one appears to be the only one capable of supporting chronologies other than IsoChronology. That's not important for the use case at hand, but I do like to write flexible, reusable code, and I'd like for this to not fall down in other situations. I'm not an expert on those alternative chronologies, and I don't know if they have leap years or anything else that might cause the length of a month to vary. Heck, I don't even know if they have months.
EDIT: Option 4, per #Ole V.V.'s answer, below:
int daysInMonth = date.toLocalDate().lengthOfMonth();
int daysInMonth = date.toLocalDate().lengthOfMonth();
I understand your confusion. There are several ways to do this, and the above is not the only good way. In particular I’m pretty fond of your option 3 too. The options explicitly involving isLeapYear() are not to my taste, I find them too low-level, too manual.
Under what circumstances will that superiority manifest?
Readability and absence of surprises are the kings. From there your judgement is at least as good as mine. Chances are that you know the people who are going to maintain your code better than I do.
You may also take a step back and ask yourself what you need the number of days in a month for. I am mentioning this because one typical use would be for calculations that java.time can itself do better than you can, like adding 24 days to a date, finding the count of days between two dates in different months or finding out which day of the year a certain date is. java.time has got methods for all of these and many more.
This:
com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl sun = new com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl();
final long aTime = TimeUnit.DAYS.toMillis(32)
System.err.println(sun.newDurationDayTime(aTime));
will print P1DT0H0M0.000S, or 0 months and 1 day if I use getMonths and getDays respectively.
I realize the months should not be set given what's said in the javadocs
whose lexical representation contains only day, hour, minute, and second components.
So months being 0 seems fine, but the number of days in that case should be 32.
If I use org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl I get the expected results, 32 days or P32DT0H0M0.000S.
Is the com.sum version broken, or is there something in the contract that is not mention that the plain xerces implementation is not strict about where that method cannot be used for durations >= one month?
Edit:
I can see in DurationDayTimeImpl w/in com.sum the constructor used in this case seems to get the duration correct. After the call to super the months are 1 and the days are 1. But then after canonicalizing the time component, the years and months are wiped out:
this.years = null;
this.months = null;
This is how it ends up with a value of a single day. I can see why it clears those values given what the javadocs say, but that seems like a bug to not down convert those into days.
The plain xerces impl simply divides off the individual components; the com.sun version uses XMLGregorianCalendar.
I wonder if it's possible to parse any string (at least to try) to sql Date without specifing the string format? In other words I want to make a generic method who take as input a string and return an sql Date.
For instance I have:
String date1="31/12/2099";
String date2="31-12-2099";
and call parseToSqlDate(date1) and parseToSqlDate(date2) which will returns sql dates.
Short answer: No
Why: Parsing any string to a valid date is a task you as an intelligent being could not do (there is no "logical" way to determine the correct date), so you cannot "tell" a computer(program) to do that for you (see JGrice's comment, and there we still have 4-digit years).
Long answer: Maybe, if you are willed to either take risks or do not need a high rate of success.
How:
Define your minimal (format) requirements of a date. E.g. "a minimal date contains 1-8 numbers; 01/01/2001 , 01-01-01 , 01.01 (+current year) , 1.1 (+current year), 1 (+current month + current year) and/or "..contains 1-6 numbers and the letters for months"; 01-Jan-2001 and so on.
Split the input along any non-number/non-month-name characters, with a regex like [^0-9a-zA-Z] (quick thought, may hold some pitfalls)
You now have 1 to 3 (actually more if e.g. the time is included) separate numbers + 1 month name which can be aligned for year/month/day any way you like
For this "alignment", there are several possibilities:
Try a fixed format at first, if it "fits", take it, else try another (or fail)
(only of you get more than one entry at a time) guess the format by assuming all entries have the same (e.g. any number block containing values > 12 is not a month and > 31 is not a day)
BUT, and this is a big one, you can expect any such method to become a major PITA at some point, because you can never fully "trust" it to guess correctly (you can never be sure to have missed some special format or introduced some ambiguous interpretation). I outlined some cases/format, but definitely not all of them, so you will refine that method very often if you actually use it.
Appendix to your comment: "May be to add another parameter and in this way to know where goes day , month and so on?" So you are willed to add "pseudo-format-string" parameter specifying the order of day, month and year; that would make it a lot easier (as "simply" filtering out the delimiters can be achieved).
table structure:
sysdurationtimeday , sysdurationtimehour, sysdurationtimeminute
1, 12,10
3, 23,10
0, 0,10
i have these 3 fields from database, after getting these 3 values, what is the technique that i can use do cast to which Java Object? (maybe Calendar.class, TimeStamp.class) ?
and use it to compared with record is spent less than 1 day, more than 1 day + less than 3 days. etc?
As long as you're talking durations and not absolute times, this is pretty easy. Just express the time in a convenient unit, say seconds:
time_in_seconds = 86400*sysdurationtimeday +
3600*sysdurationtimehour +
60*sysdurationtimeminute
In Java the standard way to represent this is actually as a long value in milliseconds, ala System.currentTimeMillis().
All the standard Java classes are intended to handle absolute times and need to deal with daylight savings, leap years, and all that crap. At least with the data you gave us, you don't have the required info anyway: there's no way to tell if the day was a daylight savings day and therefore took 23 or 25 hours instead of 24.
I would prefer my own class, overriding the "essential" methods.
public class SysDuration implements Comparable {
int day;
int hour;
int min;
public SysDuration(int day,int hour,int min) {
}
public boolean equals(Object obj) {
}
public int hashCode() {
}
public int compareTo(Object obj) {
}
public boolean spendLess(SysDuration dur) {
}
}
Lots of good answers already.
A sugegstion, perhaps out of scope, if you use durations in java I would prefer to
just calculate and store this in one variable, typically a long in milliseconds
if this resolution is good enough. The splitting in 3 variables usually
make most of the code more complicated.
Calculations are easier and intergartion with libs such as jodatime and similar will be
even more simple.
If you literally want "more than 1 day", that is there's no rounding so d=1, h=23, m=59 gives you 1 day, not 2 days then you can just use sysdurationtimeday and completely ignore hours and minutes. (That assumes you don't have more than 24 in sysdurationtimehour).
Classes such as Calendar don't help, they are for manipulating actual dates, you already are working in durations.
Jodatime supports durations and then you get the operations needed for free.
If you are hesitant to add another dependency and learning curve, I would create a little custom class with a field storing the duration as a number in the desired precision. Then add some methods to do your comparisons or return a Calendar object or a Date aded and subtracted with your duration.
My guess is this will end up being cleaner than using the standard Java API's which always end up in complicated, clunky code when you start manipulating time.