How can I use the Maya calendar in Java?
Has your calendar run out now? :-)
If you are really looking for a solution this Maya Calendar implementation looks quite good.
It implements a maya Tzolk'in calender using Java's GregorianCalendar. Dates can be retrieved both in Gregorian or Tzolk'in format.
Here are the core parts:
[...]
/** parses Date specified in Long Count format, e.g. "12.19.19.17.19" */
public void parseLongCountDate (String longCountDate) {
String [] components = longCountDate.split("\\.");
try {
if (components.length != 5)
throw new Exception("Expecting 5 numbers separated by dots");
int baktuns = Integer.valueOf(components[0]);
int katuns = Integer.valueOf(components[1]);
int tuns = Integer.valueOf(components[2]);
int winals = Integer.valueOf(components[3]);
int kins = Integer.valueOf(components[4]);
set (baktuns, katuns, tuns, winals, kins);
} catch (Throwable e) {
throw new IllegalArgumentException("Invalid long count date format: "
+ e.getMessage());
}
}
/** Set date to given long count date */
public void set (int baktuns, int katuns, int tuns, int uinals, int kins) {
assert MayaTimeUnit.Kin.toDays (1) == 1;
daysSinceGreatCycle =
MayaTimeUnit.Baktun.toDays (baktuns) +
MayaTimeUnit.Katun.toDays(katuns) +
MayaTimeUnit.Tun.toDays(tuns) +
MayaTimeUnit.Winal.toDays(uinals) +
kins;
}
[...]
/** #return day name number in Tzolk'in calendar, e.g. it returns 0 (Ajaw) for the day "4 Ajaw" */
public Tzolkin toTzolkinDayName () {
// The Tzolk'in date is counted forward from 4 Ajaw.
return Tzolkin.DAYS[(daysSinceGreatCycle + 19) % 20]; // relative to Ajaw
}
/** #return day number in Tzolk'in calendar, e.g. it returns 4 for the day "4 Ajaw" */
public int toTzolkinDayNumber () {
// The Tzolk'in date is counted forward from 4 Ajaw.
return (daysSinceGreatCycle + 4) % 13;
}
[...]
/** #return day name number in Haab calendar, e.g. it returns Yaxkin (5) for the day "14 Yaxk'in" */
public Haab toHaabDayName () {
int d = (daysSinceGreatCycle + 349) % 365;
return Haab.DAYS[d / 20];
}
/** #return day number in Haab calendar, e.g. it returns 14 for the day "14 Yaxk'in" */
public int toHaabDayNumber () {
int d = (daysSinceGreatCycle + 349) % 365;
return d % 20 - 1;
}
[...]
/** #return Gregorian calendar representation of currently set date */
public String toGregorianString () {
Calendar c = toGregorianDate ();
return format.format(c.getTime());
}
/** #return Converts currently defined date into Gregorian calendar */
public Calendar toGregorianDate () {
Calendar c = (Calendar)greatCycleStartDate.clone();
c.add(Calendar.DAY_OF_YEAR, daysSinceGreatCycle);
return c;
}
[...]
In any case: Cool question :-)
The best way of using other calendars/chronologies in Java is the excellent Joda-Time library. It doesn't have a Mayan chronology itself, but you could right your own implementation of the Mayan rules and plug it in. Shouldn't be too onerous.
Use JodaTime. Oops, sorry, just a reflex when reading a question about java.util.Calendar ;-)
There are some Java applets on the web that might be helpful to you.
LOL,
try setting the last selectable date to 21 December 2012 ?
but is does not really end there, it just starts over so you want to start counting again after 21 December 2012?
Related
I want to use the difference method.
It gets a certain date parameter, then calculates the difference between two dates (this and other)
The calculateDate is the way to get the days passed since the Christian counting. I wanted to use it inside the difference method, but I get the following error while trying to compile:
cannot find symbol - variable calculateDate
The difference has to be an absolute value, so I added the Math.abs.
public int difference (Date other) {
return Math.abs(this.calculateDate-other.calculateDate);
}
//computes the day number since the beginning of the Christian counting of years
private int calculateDate (int day, int month, int year)
{
if (month < 3)
{
year--;
month = month + 12;
}
return 365 * year + year/4 - year/100 + year/400 + ((month+1) * 306)/10 + (day - 62);
}
It would be easier to use java.time library instead of writing the day counting code by hand, unless you have a very specific requirement:
private int difference(LocalDate date) {
LocalDate start = LocalDate.of(0, 1, 1); // 1 Jan 0000
return ChronoUnit.DAYS.between(start, date);
}
You can map from java.util.Date to java.time.LocalDate with:
Date date = ...
LocalDate ld = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
This question already has answers here:
How can I pad an integer with zeros on the left?
(18 answers)
Adding zero to a single digit number, Is it possible?
(2 answers)
Generate random date of birth
(15 answers)
Closed 5 years ago.
I want to generate random DOB for the given range of year. So I tried the below code
private static String randomDataOfBirth(int yearStart, int yearEnd)
{
GregorianCalendar gc = new GregorianCalendar();
int year = randBetween(yearStart, yearEnd);
gc.set(Calendar.YEAR, year);
int dayOfYear = randBetween(1, gc.getActualMaximum(Calendar.DAY_OF_YEAR));
gc.set(Calendar.DAY_OF_YEAR, dayOfYear);
String date = null;
if(gc.get(Calendar.MONTH) == 0)
{
date = gc.get(Calendar.YEAR) + "-" + 1 + "-" + gc.get(Calendar.DAY_OF_MONTH);
}else
{
date = gc.get(Calendar.YEAR) + "-" + gc.get(Calendar.MONTH) + "-" + gc.get(Calendar.DAY_OF_MONTH);
}
return date;
}
private static int randBetween(int start, int end) {
// TODO Auto-generated method stub
return start + (int)Math.round(Math.random() * (end - start));
}
Main:-
public static void main(String[] args) {
String dob = randomDataOfBirth(1899, 1937);
System.out.println(dob);
}
I can be able to generate the random DOB. But only for the Month and Day, I want to add a prefix '0' for the range from 1 to 9
Month - 01, 02, 03 and ... up to 09
Day - 01, 02, 03 and ... up to 09
Apart from the desired formatting of you date I see some other problems with your code that I think you would want to address:
Assuming you want a usual month number, 01 for January through 12 for December, your handling of the month number is not correct. get(Calendar.MONTH) gives you a 0-based month: 0 for January through 11 for December. Therefore, your code not only will never give you 12 as month and 1 all too often. It will also give you non-existing dates. I have seen 1905-2-31 and 1929-4-31 (because you get 2 for March, which we interpret as February, etc.).
Possibly unimportant, your distribution gives each day in a leap year slightly smaller probablity than other days.
If you can, I suggest you use LocalDate. The class was introduced in Java 8:
private static String randomDataOfBirth(int yearStartInclusive, int yearEndExclusive) {
LocalDate start = LocalDate.ofYearDay(yearStartInclusive, 1);
LocalDate end = LocalDate.ofYearDay(yearEndExclusive, 1);
long longDays = ChronoUnit.DAYS.between(start, end);
int days = (int) longDays;
if (days != longDays) {
throw new IllegalStateException("int overflow; too many years");
}
int day = randBetween(0, days);
LocalDate dateOfBirth = start.plusDays(day);
return dateOfBirth.toString();
}
This gives you evenly distributed, correct dates formatted with 2 digits for month and day-of-month, e.g., 1926-07-05.
If you want to avoid the overflow check, you may of course rewrite your randBetween() to handle longs.
If you cannot use Java 8, you can do something similar with GregorianCalendar and SimpleDateFormat. Counting the exact number of days from lower to upper bound is complicated, though, so you will probably want to stick to your way of picking the date. SimpleDateFormat can still give you correct dates formatted with two digits for month and day. Edit: In your class, declare:
static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Now just substitute your if-else statement with this:
String date = formatter.format(gc.getTime());
If your randomDataOfBirth() may be accessed from more than one thread, this won’t work since SimpleDateFormat is not thread-safe. If so, each thread should have its own SimpleDateFormat instance.
With Java7 you can try with something like this:
public class DobMaker
{
public String getDOB(int min, int max)
{
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
int year = min + new Random().nextInt(max - min + 1);
calendar.set(Calendar.YEAR, year);
int day = 1+new Random().nextInt(calendar.getActualMaximum(Calendar.DAY_OF_YEAR));
calendar.set(Calendar.DAY_OF_YEAR, day);
return new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());
}
public static void main(String[] args)
{
DobMaker d = new DobMaker();
System.out.println(d.getDOB(1970, 1980));
System.out.println(d.getDOB(1970, 1971));
System.out.println(d.getDOB(2007, 2016));
}
}
I'm teaching myself Java and I am working on this application that will count the days until Christmas. The HOUR_OF_DAY, MONTH, and SECOND values of the GregorianCalendar and Date objects for today and Christmas are all set to zero. By debugging I can see the variable for the difference in days in milliseconds and it gives me a time of 1641599724 milliseconds which comes out to be 18.99999 days but it wont round up to 19 no matter what I try!
I have tried Math.ceil method to try and round up but I can't get it to equal 19.
FIRST: DataUtils class stores/modifies input from the user
package chapter13datesstrings;
import java.util.*;
public class DateUtils {
static final int MILLS_IN_DAY = 24 * 60 * 60 * 1000;
public static Date getCurrentDate(){
GregorianCalendar currentDate = new GregorianCalendar();
currentDate.set(Calendar.HOUR_OF_DAY, 0);
currentDate.set(Calendar.MINUTE, 0);
currentDate.set(Calendar.SECOND, 0);
return currentDate.getTime();
}
public static Date createDate(int year, int month, int day){
GregorianCalendar date = new GregorianCalendar(year, month, day);
return date.getTime();
}
public static Date stripTime(Date date){
GregorianCalendar noTimeDate = new GregorianCalendar();
noTimeDate.setTime(date);
noTimeDate.set(Calendar.HOUR, 0);
noTimeDate.set(Calendar.MINUTE,0);
noTimeDate.set(Calendar.SECOND, 0);
return noTimeDate.getTime();
}
public static double daysDiff(Date date1, Date date2){
date1 = stripTime(date1);
date2 = stripTime(date2);
long longDate1 = date1.getTime();
long longDate2 = date2.getTime();
long longDiff = longDate2 - longDate1;
return (int) (Math.ceil(longDiff / MILLS_IN_DAY));
}
}
SECOND: DateUtilExample class provides input to the DataUtils class
package chapter13datesstrings;
import java.util.*;
import java.text.*;
public class DateUtilExample {
public void thisIsCode(){
GregorianCalendar currentGC = new GregorianCalendar();
int currentYear = currentGC.get(Calendar.YEAR); //sets current year
Date currentDate = DateUtils.getCurrentDate(); //create current date object
Date christmas = DateUtils.createDate(currentYear, Calendar.DECEMBER, 25); //set christmas date
int daysToChristmas = DateUtils.daysDiff(currentDate, christmas); // days until christmas
DateFormat date = DateFormat.getDateInstance(Calendar.LONG);
String formattedToday = date.format(currentDate);
/**** Output Items *****/
System.out.println("Today is " + formattedToday);
System.out.println("Number of Days 'Till Xmas: " + daysToChristmas + " days");
}
}
MAIN METHOD CLASS
package chapter13datesstrings;
import java.util.Date;
import java.text.DateFormat;
public class Chapter13DatesStrings {
public static void theDates(){
DateUtilExample dateUtilExample = new DateUtilExample();
dateUtilExample.thisIsCode();
}
public static void main(String[] args) {
theDates();
}
}
When you divide two integers in Java, the result is rounded down ("truncated") automatically.
The expression longDiff / MILLS_IN_DAY isn't equal to 18.99999, it is equal to 18. Java essentially calculates 18.99999 and then throws everything after the decimal point away, before you can do anything with it. Rounding up with ceil won't help at that point, because you just end up calculating ceil(18) which is just 18.
One solution to this is to cast the numbers to double before you divide them. double's are floating point values, so dividing them does not round the result down. To use doubles, replace
longDiff / MILLS_IN_DAY
with
((double)longDiff) / ((double)MILLS_IN_DAY)
Another solution, which may be more computationally efficient but a little bit less elegant is to simply add 1 to the result. This isn't strictly equivalent, because when you are within a millisecond of midnight the result will be a day more than you expected, but this is essentially unnoticeable. To take this approach, you would replace the line:
return (int) (Math.ceil(longDiff / MILLS_IN_DAY));
with
return longDiff / MILLS_IN_DAY + 1;
Alternatively to what SelectricSimian said,
this is something that can be done in a couple of lines using the Calendar API provided by java.
To simply get the day difference between the current time and a given day, you can use:
public static void main(String[] args) {
System.out.println(getDaysUntil(Calendar.DECEMBER, 25) + " day(s).");
// If the date is December 25th, this will output "365 day(s)"
// If the date is December 24th, this will output "1 day(s)"
}
public static int getDaysUntil(int month, int day) {
/**
* First get a properly formatted calendar representing right now. This
* should include leap years and local. With this calendar, we get the
* day of the year.
*/
Calendar calendar = Calendar.getInstance();
int today = calendar.get(Calendar.DAY_OF_YEAR);
/**
* Now change the day and month of the current calendar to the given day
* and month.
*/
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
int desiredDay = calendar.get(Calendar.DAY_OF_YEAR);
/**
* Then we just get the difference between now and then.
*/
int difference = desiredDay - today;
/**
* If the desiredDay has passed already, or it's currently the
* desiredDay, we need to recalculate the difference.
*/
if (difference <= 0) {
/**
* We start by getting the days until the end of the year.
*/
calendar.set(Calendar.MONTH, 11);
calendar.set(Calendar.DAY_OF_MONTH, 31);
int daysUntilEnd = calendar.get(Calendar.DAY_OF_YEAR) - today;
/**
* Then, move the calendar forward a year and get the day of year
* for the desired day again. We recalculate the number of days just
* in case next year is a leap year.
*/
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 1);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
desiredDay = calendar.get(Calendar.DAY_OF_YEAR);
/**
* Finally, just add daysUntilEnd and desiredDay to get the updated
* difference.
*/
difference = daysUntilEnd + desiredDay;
}
return difference;
}
Is it possible to add weekdays to joda time?
For instance, if current date is Friday 01/03, date + 1 should return Monday 04/03, rather than 02/03.
As far as I know there is no built-in method to automatically do this for you in Joda Time. However, you could write your own method, that increments the date in a loop until you get to a weekday.
Note that, depending on what you need it for exactly, this could be (much) more complicated than you think. For example, should it skip holidays too? Which days are holidays depends on which country you're in. Also, in some countries (for example, Arabic countries) the weekend is on Thursday and Friday, not Saturday and Sunday.
LocalDate newDate = new LocalDate();
int i=0;
while(i<days)//days == as many days as u want too
{
newDate = newDate.plusDays(1);//here even sat and sun are added
//but at the end it goes to the correct week day.
//because i is only increased if it is week day
if(newDate.getDayOfWeek()<=5)
{
i++;
}
}
System.out.println("new date"+newDate);
Be aware that iterating through adding N days one at a time can be relatively expensive. For small values of N and/or non performance sensitive code, this is probably not an issue. Where it is, I'd recommend minimizing the add operations by working out how many weeks and days you need to adjust by:
/**
* Returns the date that is {#code n} weekdays after the specified date.
* <p>
* Weekdays are Monday through Friday.
* <p>
* If {#code date} is a weekend, 1 weekday after is Monday.
*/
public static LocalDate weekdaysAfter(int n, LocalDate date) {
if (n == 0)
return date;
if (n < 0)
return weekdaysBefore(-n, date);
LocalDate newDate = date;
int dow = date.getDayOfWeek();
if (dow >= DateTimeConstants.SATURDAY) {
newDate = date.plusDays(8 - dow);
n--;
}
int nWeeks = n / 5;
int nDays = n % 5;
newDate = newDate.plusWeeks(nWeeks);
return ( (newDate.getDayOfWeek() + nDays) > DateTimeConstants.FRIDAY)
? newDate.plusDays(nDays + 2)
: newDate.plusDays(nDays);
public LocalDate getBusinessDaysAddedDate(LocalDate localDate, int businessDays){
LocalDate result;
if(localDate.getDayOfWeek().getValue() + businessDays > 5) {
result = localDate.plusDays(2);
}
result = localDate.plusDays(businessDays);
return result;
}
In order to work with Date instead of LocalDate, refer https://stackoverflow.com/a/47719540/12794444 for the conversions.
Class YearMonthDay is deprecated and you shouldn't use it. If you change to simple DateTime you can obtain the week day by calling:
dateTime.getDayOfWeek();
For Friday it will be 5.
One of the approaches can be making a custom addDays method which should look something like that:
addDays(DateTime dateTime, int days) {
for(int i=0;i<days;i++){
dateTime.plusDays(1);
if(dateTime.getDayOfWeek()==6) dateTime.plusDays(2); // if Saturday add 2 more days }
}
I want to take all Saturday and Sunday from given date range...
my inputs are
Start Date : 01/01/2011
End Date : 01/01/2012
now search date which is in between given start date and end date and day would be Saturday or Sunday.
Please Suggest...
Firstly, I'd recommend using Joda Time if you possibly can. It's a much better date and time API than the one built into Java.
Secondly, unless you're really worried about efficiency I would personally go for the incredibly-simple-but-somewhat-wasteful approach of simply iterating over every day in the time period, and including those which fall on the right days. Alternating between adding one day and adding six days would certainly be more efficient, but harder to change.
Sample code:
import java.util.*;
import org.joda.time.*;
public class Test
{
public static void main(String[] args)
{
List<LocalDate> dates = getWeekendDates
(new LocalDate(2011, 1, 1), new LocalDate(2011, 12, 1));
for (LocalDate date : dates)
{
System.out.println(date);
}
}
private static List<LocalDate> getWeekendDates
(LocalDate start, LocalDate end)
{
List<LocalDate> result = new ArrayList<LocalDate>();
for (LocalDate date = start;
date.isBefore(end);
date = date.plusDays(1))
{
int day = date.getDayOfWeek();
// These could be passed in...
if (day == DateTimeConstants.SATURDAY ||
day == DateTimeConstants.SUNDAY)
{
result.add(date);
}
}
return result;
}
}
I recommend to take a look at this RFC-2445 Java open-source library. You can create a weekly recurrence rule with repeating on Sat and Sun, then iterate over the specified period to get all dates.
I think, you can use following way - it's really simple and you don't need to use other libraries.
Take weekday number (for Monday = 1, Sunday = 7). Then - choose new start date, which is first Sunday occurence -> it is startDate + (7 - weekdayNum). By the same algorithm, you can take last Sunday from interval (by substracting EndDate - weekdayNum - 1, I think). And now you can go in for loop through all occurences (use incremental step 7). Or if you want specific occurence, e.g. 3rd sunday, you can simply do newStartDate + 3 * 7.
I hope, this is clear. I'm not sure, if numbers are correct. Hope this helps for understanding the problem.
Here is the complete example.
Please do suggest if we can make it better.
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
*
* #author varun.vishwakarma
*/
public class FindWeekendsInDateRange {
static HashMap<Integer, String> daysOfWeek=null;
static {
daysOfWeek = new HashMap<Integer, String>();
daysOfWeek.put(new Integer(1), "Sun");
daysOfWeek.put(new Integer(2), "Mon");
daysOfWeek.put(new Integer(3), "Tue");
daysOfWeek.put(new Integer(4), "Wed");
daysOfWeek.put(new Integer(5), "Thu");
daysOfWeek.put(new Integer(6), "Fri");
daysOfWeek.put(new Integer(7), "Sat");
}
/**
*
* #param from_date
* #param to_date
* #return
*/
public static List<Date> calculateWeekendsInDateReange(Date fromDate, Date toDate) {
List<Date> listOfWeekends = new ArrayList<Date>();
Calendar from = Calendar.getInstance();
Calendar to = Calendar.getInstance();
from.setTime(fromDate);
to.setTime(toDate);
while (from.getTimeInMillis() < to.getTimeInMillis()) {
if (daysOfWeek.get(from.get(Calendar.DAY_OF_WEEK)) == "Sat") {
Date sat = from.getTime();
listOfWeekends.add(sat);
} else if (daysOfWeek.get(from.get(Calendar.DAY_OF_WEEK)) == "Sun") {
Date sun = from.getTime();
listOfWeekends.add(sun);
}
from.add(Calendar.DAY_OF_MONTH, 1);
}
return listOfWeekends;
}
public static void main(String[] args) {
String fromDate = "7-Oct-2019";
String toDate = "25-Oct-2019";
System.out.println(FindWeekendsInDateRange.calculateWeekendsInDateReange(new Date(fromDate), new Date(toDate)));
}
}
I'm assuming your start and end dates are given in milliseconds. Loop through the dates and check whether days are 'Saturday' or 'Sunday'. Below I'm returning the total no. of Saturday and Sunday in given date range.
private int totalWeekendDays(long start, long end)
{
int result=0;
long dayInMS = TimeUnit.DAYS.toMillis(1);
for (long i = start; i<=end; i = i + dayInMS)
{
String dayOfTheWeek = (String) DateFormat.format("EEEE", i);
if (dayOfTheWeek.equals("Sunday")||dayOfTheWeek.equals("Saturday"))
{
result = result+1;
}
}
return result;
}