How to get TimeZone id for specified ISO country code? - java

I have a requirement where I have to convert timezone from UTC to a specific timezone and vice-versa taking into account day light saving. I am using java.util.TimeZone class for it. Now, issue is that there are several hundred Ids for timezone which cannot be displayed to user.
As a work around now we have decided to have country list first and list time-zones for country selected. I am not able to get TimeZone for an ISO country code.
Here is code which I am currently using to convert timezones,
Timestamp convertedTime = null;
try{
System.out.println("timezone: "+timeZone +", timestamp: "+timeStamp);
Locale locale = Locale.ENGLISH;
TimeZone destTimeZone = TimeZone.getTimeZone(timeZone);// TimeZone.getDefault();
System.out.println("Source timezone: "+destTimeZone);
DateFormat formatter = DateFormat.getDateTimeInstance(
DateFormat.DEFAULT,
DateFormat.DEFAULT,
locale);
formatter.setTimeZone(destTimeZone);
Date date = new Date(timeStamp.getTime());
System.out.println(formatter.format(date));
convertedTime = new Timestamp(date.getTime());
/*long sixMonths = 150L * 24 * 3600 * 1000;
Date inSixMonths = new Date(timeStamp.getTime() + sixMonths);
System.out.println("After 6 months: "+formatter.format(inSixMonths));
I need to find out timezone Id to be used in above code for given country ISO code.
Update: tried many things and below code gets me to list of timezones with 148 entries (which is still large). Can any one please help me to shorten it. Or, suggest some other way to either have a shorten list of timezones or get timezones for a country,
Code:
public class TimeZones {
private static final String TIMEZONE_ID_PREFIXES =
"^(Africa|America|Asia|Atlantic|Australia|Europe|Indian|Pacific)/.*";
private List<TimeZone> timeZones = null;
public List<TimeZone> getTimeZones() {
if (timeZones == null) {
initTimeZones();
}
return timeZones;
}
private void initTimeZones() {
timeZones = new ArrayList<TimeZone>();
final String[] timeZoneIds = TimeZone.getAvailableIDs();
for (final String id : timeZoneIds) {
if (id.matches(TIMEZONE_ID_PREFIXES)) {
timeZones.add(TimeZone.getTimeZone(id));
}
}
Collections.sort(timeZones, new Comparator<TimeZone>() {
public int compare(final TimeZone a, final TimeZone b) {
return a.getID().compareTo(b.getID());
}
});
}

I think ICU4J package will help you.

You can shorten your list with hasSameRules()... this should reduce you selection to about 50:
iterate through -> file equal time zones -> choose the most recognizables
The country- list has to have about 200 entries with a whole lot of uninteresting ones such as Gibraltar or St Martin... don't like that idea

Was able to get things working. I have created own database table with all time-zones as appearing in windows OS and their corresponding TimeZone IDs. Conversion is done using java.util.TimeZone class.
Thanks Namal and Frank for your inputs.

Related

convert time variable to hh:mm format in java

I'm a beginner in java and I with my code below I can generate a random time in "hh:mm:ss" format . I have no idea how to tweak my code to display time in "hh:mm" format as I am not familiar with the
Date and Time java libraries . I have checked posts here like converting time from hh:mm:ss to hh:mm in java but it does not help here .
import java.util.Random;
import java.sql.Time;
final Random random = new Random();
final int millisInDay = 24*60*60*1000;
Time time = new Time((long)random.nextInt(millisInDay));
I have also tried :
// creates random time in hh:mm format for 0-12 hours but I want the full 24 hour timeline
public static String createRandomTime() {
DateFormat format = new SimpleDateFormat("h.mm aa");
String timeString = format.format(new Date()).toString();
return timeString;
}
I would appreciate your help .
You could write a method that creates proper random hours and random minutes, then construct a java.time.LocalTime of them and return a desired String representation.
Here's an example:
public static String createRandomTime() {
// create valid (in range) random int values for hours and minutes
int randomHours = ThreadLocalRandom.current().nextInt(0, 23);
int randomMinutes = ThreadLocalRandom.current().nextInt(0, 59);
// then create a LocalTime from them and return its String representation
return LocalTime.of(randomHours, randomMinutes).format(
// using a desired pattern
DateTimeFormatter.ofPattern("HH:mm")
);
}
Executing that method ten times in a main like this
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(createRandomTime());
}
}
will produce an output like (not necessarily equal to)
08:16
07:54
17:15
19:41
14:24
12:00
12:33
11:00
09:11
02:33
Please note that the int values and corresponding LocalTime created from them will not change if you just want another format. You can easily switch the pattern to another one (maybe make the pattern String a parameter of the method). E.g. you could make it "hh:mm a" for Strings like 10:23 AM.
Since you're using the SimpleDateFormat, I'd suggest taking a look to its documentation
In there, you can see that h is for hour in AM/PM format. Since you want the 24h format, you'll need either H or k, depending if you want it to be 0-23 or 1-24
You can try with the following code,
public void testDateFormat() {
String format = "HH:mm"; //24 hours format
//hh:mm aa for 12 hours format
DateFormat dateFormat = new SimpleDateFormat(format);
String date = dateFormat.format(new Date());
System.out.println(date);
}
There is a fantastic Javadoc is available to explain the details of various options. Please refer the Javadoc as well https://docs.oracle.com/javase/10/docs/api/java/text/SimpleDateFormat.html

Java 8 timezone conversion

I know there are similar questions like this, but I wasn't quite able to find the example similar to mine. I learned about LocalDateTime and ZonedDateTime but I don't know how to tell my ZonedDateTime what's assumed timezone of parsed date.
I'm migrating from Java 7 to Java 8. In my code, I have a method like this:
public String changeTZ(String tzFrom, String tzTo, String dateToChange) {
ZoneId zoneFrom = ZoneId.of(tzFrom);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat.toPattern());
LocalDateTime localtDateAndTime = LocalDateTime.parse(dateToChange, formatter);
ZonedDateTime dateAndTimeINeed = ZonedDateTime.of(localtDateAndTime, zoneFrom );
ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo));
return formatter.format(rezDate);
}
Example usage is:
String rez = changeTZ("CEST", "UTC", "2017-08-10 14:23:58");
As you can see it receives datetime in form of string, timezone date is in tzFrom variable and TZ I need (toTo variable).
Code which does that in Java 7 is absurd and complex so I won't enlist it here. Can you please tell me how to achieve the same in Java 8 without changing method interface (arguments and return type)? What about DST? Is it handled automatically in Java 8?
Note that method also supports timezones in form of "CEST", "CET", "UTC" as well as standard ones like "Europe/London".
Java 8 uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the short abbreviations (like CEST or PST) because they are ambiguous and not standard.
Actually, those names don't work with ZoneId mainly because of this ambiguity (CST, for example, can be "Central Standard Time", "Cuba Standard Time" or "China Standard Time"). Actually, some of them might work due to retro-compatibility reasons, but it's not guaranteed to work with all of them.
I'm assuming that CEST is the Central European Summer Time. There are lots of different countries (and timezones) that are currently in CEST, so the API can't decide which timezone to choose if you just pass "CEST" to it.
That's because a timezone contains all the different offsets a region had during its history. There may be lots of countries using CEST today, but their history differs in the past (some might had DST in different years, or used a different offset and then changed, etc), and that's why they have one timezone for each.
To use such short names (like CEST), though, you can define some defaults for each one (which will be an arbitrary choice) and put these choices in a map:
// map of custom zone names
Map<String, String> map = new HashMap<>();
// setting my arbitrary choices for each name
map.put("CEST", "Europe/Berlin"); // Berlin during DST period
map.put("CET", "Europe/Berlin"); // Berlin during non-DST period
// ... and so on
Then you can use this map to create the ZoneId:
// use the custom map to create the ZoneId
ZoneId zoneFrom = ZoneId.of(tzFrom, map);
...
// use the custom map to create the ZoneId
ZonedDateTime rezDate = dateAndTimeINeed.withZoneSameInstant(ZoneId.of(tzTo, map));
I've chosen Europe/Berlin, but of course you can change it to whatever timezone you need. You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
Using the map above:
System.out.println(changeTZ("CEST", "UTC", "2017-08-10 14:23:58"));
This code outputs:
2017-08-10 12:23:58
Note that 14:23 in CEST (which I chose to be Europe/Berlin) is 12:23 in UTC, which is correct because in August Berlin is in DST (offset is +02:00).
ZoneId and ZonedDateTime classes handle DST effects automatically. You can check this by choosing a date in January (when DST is not in effect in Berlin):
// January is not DST, so use CET
System.out.println(changeTZ("CET", "UTC", "2017-01-10 14:23:58"));
The output is:
2017-01-10 13:23:58
In January Berlin is not in DST, so the offset is +01:00, then 14:23 in Berlin becomes 13:23 in UTC.
Of course the ideal is to always use the full names (like Europe/Berlin), but the custom map is an alternative if you don't have control over the inputs.
Java 8 also has a built-in predefined map, but as any other predefined stuff, the choices are arbitrary and not necessarily the ones you need.
This solution uses the IANA timezones names mentioned by Hugo in the comments to get the ZoneId (more details here). Will throw an exception if you use it with CEST.
public static String changeTZ(String tzFrom, String tzTo, String dateToChange){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzFrom));
ZonedDateTime zdt = ZonedDateTime.parse(dateToChange, dtf);
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of(tzTo));
return zdt.format(dtf2);
}
Use like:
String rez = changeTZ("US/Alaska", "Europe/Berlin", "2017-08-10 14:23:58");
Here is a class that does the job.
public class TimeZoneConverter {
private static String datePattern = "yyyy-MM-dd HH:mm:ss";
public static void main(String[] args) throws ParseException {
String sourceDate = "2017-02-27 16:00:00";
String resultDate = convertTimeZone("EET", "UTC", sourceDate);
System.out.println("EET: "+ sourceDate);
System.out.println("UTC: "+ resultDate);
}
public static String convertTimeZone(String timeZoneFrom, String timeZoneTo, String date) throws ParseException {
long timestamp = stringToTimestamp(date, timeZoneFrom);
String result = timestampToString(timestamp, timeZoneTo);
return result;
}
public static long stringToTimestamp(String time, String timeZone) throws ParseException {
DateFormat format = getDateFormat(timeZone);
return format.parse(time).getTime();
}
public static String timestampToString(long timestamp, String timeZone) {
DateFormat format = getDateFormat(timeZone);
return format.format(new Date(timestamp));
}
private static DateFormat getDateFormat(String timeZone) {
DateFormat format = new SimpleDateFormat(datePattern);
format.setTimeZone(TimeZone.getTimeZone(timeZone));
return format;
}
}

Formating time output from Google calendar API

I found this code at Google Quickstart calendar API and I have been trying to format the time output but I'm not succeeding.
I tried changing time zones and reading documentation from Android developers but I didn't manage to fix it.
I'm trying to get something like
2017-07-29 16:00:00
But I'm getting
2017-07-29T14:00:00.000Z
Also time zone is different it should be +2.
private List<String> getDataFromApi() throws IOException {
DateTime now = new DateTime(System.currentTimeMillis());
List<String> eventStrings = new ArrayList<String>();
Events events = mService.events().list("primary")
.setMaxResults(10)
.setTimeMin(now)
.setOrderBy("startTime")
.setSingleEvents(true)
.execute();
List<Event> items = events.getItems();
for (Event event : items) {
DateTime start = event.getStart().getDateTime();
if (start == null) {
start = event.getStart().getDate();
}
eventStrings.add(
String.format("%s (%s)", event.getSummary(), start));
}
return eventStrings;
}
The Calendar API's default timezone is UTC (which is what you're seeing), formatted according to RFC3339.
The time, as a combined date-time value (formatted according to
RFC3339). A time zone offset is required unless a time zone is
explicitly specified in timeZone.
Try using toTimeString() to convert it to human-readable format. You can also check this SO post for other alternatives.
private String getFormattedDate(Date date)
{
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+05:30");
df.setTimeZone(TimeZone.getTimeZone("GMT+05:30"));
return df.format(date);
}

"Reverse" wrong parsed date

we run a REST-webservice which consumes different data, my current issue belongs to a date, received as String and parsed by a java.text.SimpleDateFormat (java 8):
We received a lot (>50k) of 'wrong' formatted Strings, which were parsed by the SimpleDateFormat anyways.
The SimpleDateFormat is configured with the pattern "yyyy-MM-dd".
We received Strings the other way around "dd-MM-yyyy".
For Example the String "07-07-1950" was parsed to the date "0012-10-31" (Starting from July in year 7, added 1950 days).
We fixed the implementation, so these Strings are now parsed as expected. But we have all the corrupt dates in the system. The final question is now:
Is there a way to conclude from the date "0012-10-31" to possible original inputs (e.g. "07-07-1950", "07-06-1980" and maybe more...)?
Best regards
I found a way to find possible inputs:
I can use Calendar to iterate through possible dates, parsing the dates in the "wron"g way, and build a map with these information.
public static Map<String, Collection<String>> createDateMapping() throws ParseException
{
final DateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd");
final DateFormat wrongFormat = new SimpleDateFormat("dd-MM-yyyy");
//starting today
final Calendar cal = Calendar.getInstance();
final Map<String, Collection<String>> inputMappings = new HashMap<>();
//rolling down to year zero is quite time consuming, back to year 1899 should be enough...
while (cal.get(Calendar.YEAR) > 1899)
{
//creating the "wrong" date string
final String formattedDate = wrongFormat.format(cal.getTime());
final String key = targetFormat.format(targetFormat.parse(formattedDate));
if (!inputMappings.containsKey(key))
{
inputMappings.put(key, new ArrayList<>());
}
inputMappings.get(key).add(targetFormat.format(cal.getTime()));
//roll calendar to previous day
cal.roll(Calendar.DAY_OF_YEAR, false);
if (cal.get(Calendar.DAY_OF_YEAR) == 1)
{
//roll down the year manually, since it is not rolled down automatically
cal.roll(Calendar.DAY_OF_YEAR, false);
//roll down the day again, to start at the last day of the year again
cal.roll(Calendar.YEAR, false);
}
}
return inputMappings;
}
by the use of this method I can:
final Map<String, Collection<String>> dateMapping = createDateMapping();
System.out.println(dateMapping.get("0012-10-31"));//[2011-05-07, 1980-06-07, 1950-07-07, 1919-08-07]
It will not solve the problem completely, but is at least a good starting point - hopefully there are some dates with more explicit results.
Building on Martin Ackermann's answer:
First of all, I simplified the code a bit.
public static Map<String, Set<LocalDate>> createDateMapping(LocalDate min, LocalDate max) throws ParseException {
DateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd");
DateTimeFormatter wrongFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy");
final Map<String, Set<LocalDate>> inputMappings = new LinkedHashMap<>();
for (LocalDate date = min; !date.isAfter(max); date = date.plusDays(1)) {
final String incorrectlyFormattedDate = date.format(wrongFormat);
final String key = targetFormat.format(targetFormat.parse(incorrectlyFormattedDate));
if (!inputMappings.containsKey(key)) {
inputMappings.put(key, new TreeSet<>());
}
inputMappings.get(key).add(date);
}
return inputMappings;
}
Easily fixing the invalid dates depends on what is the range of valid dates.
For example if max=2016-12-31 then the following table shows the number of unique dates that are fixable/ambiguous depending on min
min fixable ambiguous
-----------------------------
1990-01-01 9862 0
1980-01-01 8827 2344
1970-01-01 5331 5918
1960-01-01 1832 9494
1950-01-01 408 10950
1940-01-01 314 11054
1930-01-01 218 11160
1920-01-01 165 11223
1910-01-01 135 11263
1900-01-01 105 11303
Ambiguous matches for invalid dates occur at about 30 year intervals so if the actual dates fall in a period of 30 years then you are in luck
LocalDate max = LocalDate.of(2016, Month.DECEMBER, 31);
LocalDate min = max.minusYears(30);
Map<String, Set<LocalDate>> invalidDateMapping = createDateMapping(min, max);
long reversibleCount = invalidDateMapping.entrySet().stream().filter(e -> e.getValue().size() == 1).count(); // 10859
long ambiguousCount = invalidDateMapping.size() - reversibleCount; // 50
I don't think you will be able to figure out the original date of the corrupted input, but you should be able to find all corrupted dates and perhaps find a way to re-consume that data. This is because each date was altered by an unknown number of days, and reversing that process would require you to know either the number of days or the starting date, and it looks like you don't have that here.
That said, it will actually be fairly easy to narrow down any dates that were corrupted.
The largest value you will be given for a month should be 12. That means the latest "year" for your corrupted data will be the year 12. If your dates run right up to the present, the largest year (which was incorrectly parsed as days) will be 2016, which would be converted to about 5.5 years. So any dates with years below 18 or 19 are corrupted, and you should be able to at least remove them.
The only edge case here is if you have dates that have years that will validly land in the early teens. If that's the case, you'd have to go through those by hand. But that seems unlikely.
Have you tried setting SimpleDateFormat Lenient to false
package test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dateFormat2 = new SimpleDateFormat("dd-MM-yyyy");
dateFormat1.setLenient(false);
dateFormat2.setLenient(false);
Date d = null;
String invalidDate = "07-06-1980";
try {
d = dateFormat1.parse(invalidDate);
} catch (Exception e) {
System.out.println("reversed date " + invalidDate);
d = dateFormat2.parse(invalidDate);
}
System.out.println(parsed date " + dateFormat1.format(d));
}
}
reversed date 07-06-1980
parsed date 1980-06-07

Generating all days between 2 given dates in Java [duplicate]

This question already has answers here:
how to get a list of dates between two dates in java
(23 answers)
Closed 6 years ago.
I'm trying to get an array of Dates, while my input is a 'from'/'to' structure.
So my input is:
String date1 = "2014-01-01";
String date2 = "2014-05-01";
My output should be an Arraylist with all dates between date1 and date2.
I've already looked for this, but I could only find questions about the difference between 2 dates:
SimpleDateFormat myFormat = new SimpleDateFormat("dd MM yyyy");
String inputString1 = "23 01 1997";
String inputString2 = "27 04 1997";
try {
Date date1 = myFormat.parse(inputString1);
Date date2 = myFormat.parse(inputString2);
long diff = date2.getTime() - date1.getTime();
System.out.println ("Days: " + TimeUnit.DAYS.convert(diff,TimeUnit.MILLISECONDS));
} catch (ParseException e) {
e.printStackTrace();
}
Any hints or suggestions? All other questions are for iOS or SQL.
Take a look at JodaTime: http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTime.html
DateTime dateTime1 = new DateTime(date1);
DateTime dateTime2 = new DateTime(date2);
List<Date> allDates = new ArrayList();
while( dateTime1.before(dateTime2) ){
allDates.add( dateTime1.toDate() );
dateTime1 = dateTime1.plusDays(1);
}
Below is the code to get array of dates between the two string date.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
public class DateFormatExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
SimpleDateFormat myFormat = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
String date1 = "2014-01-01";
String date2 = "2014-05-01";
try {
Date d1 = myFormat.parse(date1);
Date d2 = myFormat.parse(date2);
List<Date> allDates = new ArrayList<Date>();
List<String> allDatesString = new ArrayList<String>();
while( d1.before(d2) ){
d1 = addDays(d1, 1);
allDates.add(d1);
allDatesString.add(formatter.format(d1));
}
System.out.println(allDates);
System.out.println(allDatesString);
} catch (ParseException e) {
e.printStackTrace();
}
}
private static Date addDays(Date d1, int i) {
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(d1);
cal.add(Calendar.DATE, 1);
return cal.getTime();
}
}
If you don't want to use third party libraries you can use Calendar:
Check here a working demo.
public static void main(String[] args) throws Exception {
SimpleDateFormat myFormat = new SimpleDateFormat("dd MM yyyy");
String inputString1 = "23 01 1997";
String inputString2 = "27 04 1997";
ArrayList<Date> dates = new ArrayList<Date>();
try {
Date date1 = myFormat.parse(inputString1);
Calendar c1 = DateToCalendar(date1);
Date date2 = myFormat.parse(inputString2);
Calendar c2 = DateToCalendar(date2);
while (!areEqualDate(c1, c2)) {
dates.add(c1.getTime());
System.out.println (c1.getTime());
c1.add(Calendar.DAY_OF_YEAR, 1);
}
} catch (ParseException e) {
e.printStackTrace();
}
// ArrayList<Date> dates >> contain all dates between both given days.
}
private static boolean areEqualDate(Calendar c1, Calendar c2) {
if (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR)) return false;
if (c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH)) return false;
if (c1.get(Calendar.DAY_OF_YEAR) != c2.get(Calendar.DAY_OF_YEAR)) return false;
return true;
}
public static Calendar DateToCalendar(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal;
}
I like JodaTime, but this can also be done without 3rd party libraries by using java.util.Calendar. Given a Calendar object, one can use its add method to increase certain fields of the date while honoring the calendar rules (like adding 1 day to the 31st of January gets you to the 1st of February, not to the 32nd of January).
First get the dates into one Calendar object each, in the correct chronological order so adding is going in the right direction later:
Calendar cStart = Calendar.getInstance(),
cStop = Calendar.getInstance();
if (date1.before(date2)) {
cStart.setTime(date1);
cStop.setTime(date2);
} else {
cStart.setTime(date2);
cStop.setTime(date1);
date1 and date2 are the parsed Date objects from your question, for simplicity's sake.
Next, loop over an "add 1 to day-of-year" instruction until this gets you beyond the stop date:
do {
System.out.println(pretty(cStart));
cStart.add(Calendar.DAY_OF_YEAR, 1);
} while (cStart.before(cStop));
And lastly print the stop date
System.out.println(pretty(cStop));
pretty() is just some mini method sending the calendar through a SDF, like the one you used for parsing the Strings in the first place.
This solution will print the date range, including the start and stop dates, and might need some tweaking around the edge cases (like date1==date2). Can be easily adapted to exclude the start and stop dates. Printing can be swapped for aggregation of course. To get a Date object from the calendar, use the getTime() method (returns a snapshot, not a live reference).
The documentation for the relevant (Gregorian)Calendar can be found here.
In case you are using Guava, there is a very elegant solution to this problem.
Guava has two neat classes, such as Range and ContiguousSet, which implement exactly what you need: first one operates on ranges of values, and second one - is able to convert a range to a set of discrete values.
Example of usage of both (together with JodaTime):
LocalDate start = LocalDate.parse("2015-01-01");
LocalDate end = LocalDate.parse("2019-02-01");
Range<LocalDate> range = Range.closed(start, end); //Creates a "closed" range, that is both dates are inclusive. There are also options like "openClosed", "closedOpen" and "open"
final Set<LocalDate> daySet = ContiguousSet.create(range, LocalDateDomain.INSTANCE); //Create a "virtual" set of days in given the range. "virtual" part means that if you create a set of 10 thousand years, it will not eat your memory at all
for (LocalDate day : daySet) {
//...operation...
}
Personally, I really prefer this way, as it eliminates some problems with understanding closed/open ranges, and makes code much easier to read and understand, while making no impact on performance. Also, it works with any kinds of dates, any libraries (you can swap YodaTime to Java8 Dates or even Java7- Date-based implementation).
Moreover, it allows you to do some neat operations on ranges like intersections, unions, spanning of ranges, incredibly fast "contains" and so on.
Only downsides are:
Dependence on Guava.
Need to create a special "DiscreteDomain" class, which Guava uses to understand where one date ends and other begins.
Example of LocalDateDomain implementation which operates as a bridge between Guava and JodaTime:
public class LocalDateDomain extends DiscreteDomain<LocalDate> {
public static final LocalDateDomain INSTANCE = new LocalDateDomain();
#Override
public LocalDate next(LocalDate value) {
return value.plusDays(1);
}
#Override
public LocalDate previous(LocalDate value) {
return value.minusDays(1);
}
#Override
public long distance(LocalDate start, LocalDate end) {
return Days.daysBetween(start, end).getDays();
}
}
I already know that OP isn't using Java 8 but here's the current solution - Java has been revamped and the new java.time API does every conceivable job in that regard:
//change these values :
LocalDate ld1 = LocalDate.ofEpochDay(0);
LocalDate ld2 = LocalDate.now();
//do NOT change these:
final LocalDate begin = ld1.isBefore(ld2) ? ld1 : ld2;
final LocalDate end = ld2.isAfter(ld1) ? ld2 : ld1;
for (int i = 0; i < begin.until(end, ChronoUnit.DAYS); i++) {
final LocalDate curDate = begin.plusDays(i);
System.out.println("current date : " + curDate);
}
This will output every valid day between the two dates whereas most of the other solutions will also give you invalid ones; heres the thing: temporal calculations need to be done on timezone-independent data - the output on the other hand may very well be timezone and/or chronology -dependent.
Thats why there are packages like java.time.format - simply calculate your time/date values and format them for your chosen region ... thats how its done correctly.
If you need to convert temporal input there are also useful functions in the time-API, i recommend doing a thorough tutorial on the subject, a few good introductions may be this and especially that :
There are two basic ways to represent time. One way represents time in
human terms, referred to as human time, such as year, month, day,
hour, minute and second. The other way, machine time, measures time
continuously along a timeline from an origin, called the epoch, in
nanosecond resolution. The Date-Time package provides a rich array of
classes for representing date and time. Some classes in the Date-Time
API are intended to represent machine time, and others are more suited
to representing human time.

Categories

Resources