Listing 12 prior months - java

I have a situation that requires me to get the 12 past months in a list.
Example:
The current month is March, and the current year is 2012. So I want to get a list that looks like this:
March (2011), April (2011)...December (2011), January (2012), February (2012), and March (2012).
I've been trying to accomplish this using Date and Calendar, I didn't quite manage to get the alorithm correct.
Any ideas?

Here's a Groovy way to do it:
use(groovy.time.TimeCategory) {
def today = new Date()
println today
12.times { i ->
println today - (i+1).months
}
}
You can read more about Groovy dates here

Here's a complete, functioning version based on doelleri's suggestion:
use(groovy.time.TimeCategory) {
def today = new Date()
13.times { i ->
println( (today - i.months).format('MMMM (yyyy)') )
}
}
The changes I made:
I modified the loop so that the printing could occur in the same place (instead of printing the current month in a different place).
I used the format method to render the date exactly as you requested. If you want the output to line up nicer, try using 'MMM (yyyy)' as the format, which uses 3-letter months.
You'll probably want to get these as a list, so you might be better off using:
def months
use(groovy.time.TimeCategory) {
def today = new Date()
months = (0..13).collect { (today - it.months).format('MMMM (yyyy)') }
}
Which stores the month data into an array called months.

Related

Get next Date that meets conditions in Java

I know this should be simple, but I can't find anything on the internet.
Given some conditions, I want to get the next Date when they will be met. For example, if conditions are minute = 01 and second = 30 and now the time is 15:58:00, the function should return today at 16:01:30. How can I accomplish this in Java?
I need to be able to set conditions of, at least, seconds, minutes and hours, but I would like to have the possibility to set one condition to any value (like the example above, that doesn't specify an hour).
Thanks and sorry for my bad English.
EDIT:
I see there's something that might need clarification: I want to get a Date (or whatever) always after the current time that meets conditions. Also, this is for a Minecraft Spigot server, maybe this information can help.
You could use streams for this:
Optional<LocalDateTime> firstMatch =
Stream.iterate(
LocalDateTime.now(),
ldt -> ldt.plusSeconds(1L))
.filter(
// Insert your condition here
ldt -> ldt.getSecond() == 0)
.findFirst();
What this code does is take the current LocalDateTime and use it to generate a stream of LocalDateTime objects (advancing the time by 1 second each time). Once it encounters a LocalDateTime that matches the provided condition, it returns this objects and terminates the stream.
Keep in mind that using this approach will generate a lot of objects if it takes a while for the condition to become true, so it is not very efficient.
If you want to strip the nanoseconds replace LocalDateTime.now() by LocalDateTime.now().withNano(0).
You can use LocalDateTime#plus... to add duration to get the updated time.
Demo:
import java.time.LocalDateTime;
import java.time.LocalTime;
public class Main {
public static void main(String[] args) {
String strTime = "15:58:00";
LocalTime time = LocalTime.parse(strTime);
// Today at the specified time
LocalDateTime today = LocalDateTime.now().with(time);
System.out.println(today);
// Today with the added minutes and seconds
LocalDateTime updated = today.plusMinutes(1).plusSeconds(30);
System.out.println(updated);
}
}
Output:
2021-02-23T15:58
2021-02-23T15:59:30
If you want to switch to a different time, you can use LocalDateTime#with...
import java.time.LocalDateTime;
import java.time.LocalTime;
public class Main {
public static void main(String[] args) {
String strTime = "15:58:00";
LocalTime time = LocalTime.parse(strTime);
// Today at the specified time
LocalDateTime today = LocalDateTime.now().with(time);
System.out.println(today);
// Today with the updated minute and second
LocalDateTime updated = today.withMinute(1).withSecond(30);
System.out.println(updated);
}
}
Output:
2021-02-23T15:58
2021-02-23T15:01:30
Learn more about the modern date-time API from **[Trail: Date Time](https://docs.oracle.com/javase/tutorial/datetime/index.html)**.
Note: The java.util date-time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.
For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7.
If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
If I got you right, this should solve your problem
public String nextMeet(int minutes, int seconds) {
LocalDateTime currentDate = LocalDateTime.now();
if ((currentDate.getMinute()*60) + currentDate.getSecond() > (minutes*60) + seconds) {
return (currentDate.getHour() + 1) + ":" + minutes + ":" + seconds;
} else {
return currentDate.getHour() + ":" + minutes + ":" + seconds;
}
}
I've found another solution myself. This library can parse a cron expression and return the next time when it will run, which solves most of my issues. (taken from this question)
Thanks to #OleV.V. for the idea.

Java date handling in improper format

Firstly, I'm a C# dev learning Java. I'm converting a program I wrote in C# as an exercise and am having problems with parsing a date being submitted from an html form. The form is sent as an email and the java program reads the emails and parses the body. I have a drop down calendar for my peeps to select a date from but there's always some jerk who has to type it in and mess everything up. Currently I am doing this in my code:
public void SetDatePlayed(String datePlayed)
{
this.datePlayed = LocalDate.parse(datePlayed);
}
datePlayed being passed in is a string usually formatted as yyyy-MM-dd but of course someone typed in 3/7 instead of using the calendar drop down on the form. this.datePlayed is a LocalDate. In C# I would just end up with a date that assumed 2020 for the year - no problem. LocalDate really wants it in the yyyy-MM-dd format and I don't know what the best practice here is with Java. I've been googling it all morning and haven't come across this as being an issue for anyone else. I don't care if I'm using LocalDate but I do need it to be a date datatype so I can do date checks, sorts, searches, etc later on.
You can use DateTimeFormatterBuilder and parseDefaulting() to supply default value for the year.
Building on the answer by Sweeper, it can be done like this:
static LocalDate parseLoosely(String text) {
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.appendPattern("[uuuu-M-d][M/d/uuuu][M/d]")
.parseDefaulting(ChronoField.YEAR, Year.now().getValue())
.toFormatter();
return LocalDate.parse(text, fmt);
}
Warning: Do not cache the formatter in e.g. a static field, since it snapshots the year, if the program might be running across New Year's Eve, which a webapp would, unless you add logic to make the cache auto-refresh on year change.
Test
System.out.println(parseLoosely("2019-04-07"));
System.out.println(parseLoosely("2019-4-7"));
System.out.println(parseLoosely("4/7/2019"));
System.out.println(parseLoosely("4/7"));
Output
2019-04-07
2019-04-07
2019-04-07
2020-04-07
I see two possible interpretations of your question. I'm not sure which one it is, so I'll answer both.
How do I parse a date string in a format that has no year, such as M/d (3/7), to a LocalDate?
You don't. A LocalDate by definition must have year, month, and day. If you only have a month and a day, that's a MonthDay:
MonthDay md = MonthDay.parse("3/7", DateTimeFormatter.ofPattern("M/d"));
If you want the current year added to it, you can do it later:
LocalDate ld = md.atYear(Year.now(/*optionally insert time zone*/).getValue());
How do I handle both yyyy-MM-dd and M/d patterns?
Here's one way: create a DateTimeFormatter that recognises both patterns, parse the string to a TemporalAccessor, check if the TemporalAccessor supports the "year" field:
TemporalAccessor ta = DateTimeFormatter.ofPattern("[M/d][yyyy-MM-dd]").parse("3/7");
if (ta.isSupported(ChronoField.YEAR_OF_ERA)) { // yyyy-MM-dd
LocalDate ld = LocalDate.from(ta);
} else if (ta.isSupported(ChronoField.MONTH_OF_YEAR)) { // M/d
MonthDay md = MonthDay.from(ta);
} else {
// user has entered an empty string, handle error...
}

Java date-manipulation

I'm trying to get a date with a Month number, week of month number and a day of week number
I thought this will be easy and did this:
LocalDate nextbookingDate = LocalDate.now().plusYears(1);
nextBookingDate = nextBookingDate.with(Month.of(1));
nextBookingDate = nextBookingDate.with(WeekFields.ISO.weekOfMonth(), 1);
nextBookingDate = nextBookingDate.with(DayOfWeek.of(1));
System.out.println(nextBookingDate); //2019-12-30
nextBookingDateshould be 2020-01-06 because its the first Monday in January.
But why do I get 2019-12-30 and how do I solve this?
In each new line, you are overwriting what you have done in the previous line.
Try something like this:
nextBookingDate = now()
.with(Month.of(1))
.with(WeekFields.ISO.weekOfMonth(), 1)
.with(DayOfWeek.of(1));
but be aware that December 30, 2019 is actually the first day of week 1 of 2020.
Because the question has been updated, a more relevant answer would be:
LocalDate nextBookingDate = LocalDate.now().plusYears(1)
.with(Month.JANUARY)
.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.MONDAY));
and you can replace the 1 as argument for dayOfWeekInMonth with a number from 2 through 5 as appropriate.
It’s not completely clear to me what result you want in general, and why. If I may assume that you want the next date that is an nth some-day-of-week of some month, it’s a little more complicated than your code. EDIT: NorthernSky is correct in the comment under his/her answer that .with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.MONDAY)) more directly and briefly gets us what we need. This should work:
ZoneId zone = ZoneId.of("Africa/Bamako");
LocalDate today = LocalDate.now(zone);
LocalDate nextBookingDate = today.with(Month.JANUARY)
.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.MONDAY));
if (nextBookingDate.isBefore(today)) {
// Take next year instead
nextBookingDate = today.plusYears(1)
.with(Month.JANUARY)
.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.MONDAY));
}
System.out.println("Next booking date: " + nextBookingDate);
Output when I ran the code just now, was:
Next booking date: 2020-01-06
TemporalAdjusters.dayOfWeekInMonth() will get us the 1st Monday, 3rd Tuesday, etc., of the month. So feed any day of week and any number up to 4 or 5 into this method.
Please supply your desired time zone where I put Africa/Bamako since it is never the same date in all time zones.
Link: Documentation of TemporalAdjusters.dayOfWeekInMonth().

How to get the day (ex: Monday) from a time Instant in FreeMarker

So I got this Instant date in Java: "2018-05-19T22:00:00Z".
How can I get the day of the week? like monday..
I was trying this but it doesn't work:
${date?string["EEEE"]}
Thanks
Freemarker does not work with Instant, so you would need to convert that to string in Java (something like "2018-05-19T22:00:00Z") and then convert the string doing the following:
<#setting locale="en_US">
${"2018-05-19T22:00:00Z"?datetime.iso?string["EEEE"]}
First convert the string to datetime in iso format, and then back again to string with the pattern of your choosing.
By changing the locale setting, you may get the day of the week in different languages.
I think that the best way to work in Freemarker is to always have strings or integers variables.
At this point FreeMarker does not support Java 8 time, see the contribute page:
What should I contribute?
Usually, contributors come because they want to fix/improve a certain thing. But if you just want to help in general, here are some topics ...
Support for Java 8 date/time API-s (this is actually certainly a difficult one). Note that there's a 3rd party solution for this, https://github.com/amedia/freemarker-java-8, but it's not as seamless as a native solution could be.
...
So you could check out the third party solution.
See also:
Java.time (Java 8) support in Freemarker
${date?string["EEEE"]} works fine as long as date is a java.util.Date object.
You can test it in this way:
<#assign date = .now>
${date?string["EEEE"]}
I guess that it doesn't work because your date is a String, in this case you should parse it in Java (server-side) and expose a java.util.Date variable to the template.
I would advise you to use new Date Time API defined in Java 8.Using Date Time API you can fetch all data related to Date and Time easily.
Java 8 has defined a separate Enum for handling days of the week named – DayOfWeek (java.time.DayOfWeek)
java.time.DayOfWeek is an Enum which defines 7 constants representing the seven days of the week – MONDAY(int value=1), TUESDAY(2), WEDNESDAY(3)… till SUNDAY(7).
import java.time.*;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testLocalDateTime();
}
public void testLocalDateTime() {
// Get the current date and time
LocalDateTime currentTime = LocalDateTime.parse("2018-05-19T22:00:00");
System.out.println("Current DateTime: " + currentTime);
DayOfWeek dayOfWeek = currentTime.getDayOfWeek();
System.out.println(currentTime + " was a " + dayOfWeek);
}
}

Java DateFormat.SHORT in Android not working as expected

I want the "correct" localized time for users with US or German system locale. Meaning "1:00 PM" and "13:00"
Java API says: https://docs.oracle.com/javase/tutorial/i18n/format/dateFormat.html
Formats U.S. Locale German Locale
DEFAULT 7:03:47 AM 7:03:47
SHORT 7:03 AM 07:03
Correct so far.
Android API: https://developer.android.com/reference/java/text/DateFormat.html
SHORT is completely numeric, such as 12.13.52 or 3:30pm
Correct for US, if you set a German Locale, Android will "translate" AM/PM and not remove it, how the correct way is and how Java did it.
My question, why does Google do that? Am I stupid and lacking sleep, not to understand "the Google logic"? This is a trivial request, yet I tried for 2 hours to get a correct German short time presentation, that would also work for US localization. "13:57 nachm." is NOT a German time representation. No one uses that, that's why we have a 24 hour formating system. It's so awkward that it breaks every reading attempt.
Test code:
private void testGoogleLocaleLogic() {
TimeZone tz_de = TimeZone.getTimeZone("Europe/Berlin");
Calendar c_us = Calendar.getInstance(tz_de,Locale.US);
Calendar c_de = Calendar.getInstance(tz_de,Locale.GERMAN);
java.text.DateFormat df_date_us_short_ = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT,Locale.US);
java.text.DateFormat df_date_de_short = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT,Locale.GERMAN);
c_us.set(Calendar.YEAR,2018);
c_us.set(Calendar.MONTH,2);
c_us.set(Calendar.DAY_OF_YEAR,6);
c_us.set(Calendar.HOUR_OF_DAY,13);
c_de.set(Calendar.YEAR,2018);
c_de.set(Calendar.MONTH,2);
c_de.set(Calendar.DAY_OF_YEAR,6);
c_de.set(Calendar.HOUR_OF_DAY,13);
Log.d("localeTest","Android Dateformat getTimeInstance SHORT US: " + df_date_us_short_.format(c_us.getTime()));
Log.d("localeTest","Android Dateformat getTimeInstance SHORT DE: " + df_date_de_short.format(c_de.getTime()));
Log.d("localeTest","df_date_de_short is type of: " + df_date_de_short.getClass().getName());
}
Results in
Android Dateformat SHORT US: 1:57 PM
Android Dateformat SHORT DE: 1:57 nachm.
Why it's not 13:57 for German locale, although I set it two times in Calendar and DateFormat is also beyond my knowledge.
A solution to print out minutes and hours manually and then switch case between system locales to add or hide "PM/AM" is exactly why people invented Locales in the first place. To avoid that. Please tell me this is not the case.
UPDATES/MORE TESTING/MORE RESEARCH (forced use of java.text....):
My Moto X Style, Android 7, German locale prints:
Android Dateformat getTimeInstance SHORT US: 1:29 PM
Android Dateformat getTimeInstance SHORT DE: 1:29 nachm.
df_date_de_short is type of: java.text.SimpleDateFormat
Android Emulator NEXUS_5_API_26, US locale
Android Dateformat getTimeInstance SHORT US: 1:18 PM
Android Dateformat getTimeInstance SHORT DE: 13:18
df_date_de_short is type of: java.text.SimpleDateFormat
So the forced use of "java.text.SimpleDateFormat" works, but only on an emulator, not in real world? I'm close, maybe someone has the last 5 cents!
My guess earlier was right, sadly. If you don't search for it with this "guess", you'll never find it as date formatting is such a common topic. Long story short, it's an Android bug:
https://issuetracker.google.com/issues/37054851
It was fixed over a year ago. That's why the emulator works, but devices before API27 won't have the fixes as OEM don't care.
Google employee added this workaround in above bug report:
boolean use24Hour = android.text.format.DateFormat.is24HourFormat(context);
final String skeleton = use24Hour ? "Hm" : "hm";
final String pattern = android.text.format.DateFormat.getBestDateTimePattern(locale, skeleton);
DateFormat formatter = new SimpleDateFormat(pattern, locale);
... for SHORT date. MEDIUM would use skeletons of "Hms" / "hms" instead.
The code above bypasses the internal code that keep the (incorrect) in-memory state that tracks whether the user prefers 12 or 24 hour time formatting. AFAIK, android.text.format.DateFormat.is24HourFormat has always used the underlying user setting.
is24HourFormat() was added in API 3. getBestDateTimePattern() was only added in API 18.
That's exactly the switch case crap I feared humanity has to use in the year of 2018. We want to live on Mars, yet we can't figure out how to print time on planet Earth!
why don't u try using SimpleDateFormat
instead of using default DateFormat? maybe you can do something like this:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
dateFormat.setTimeZone(tz_de);
Log.d("localeTest",dateFormat.format(c_us.getTime())));

Categories