I'm attempting to model an iCalendar VTIMEZONE object using Java's ZoneId and ZoneOffsetTransitionRule.
My VTIMEZONE object looks like
BEGIN:VTIMEZONE
TZID:Central European Standard Time
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=1;BYDAY=MO
END:DAYLIGHT
END:VTIMEZONE
I need to create my own ZoneId to model this because, as far as I know, there isn't a ZoneId available with these offsets and in which DST starts on the first Monday of January (as opposed to some Sunday of March).
I have the following for creating a ZoneOffsetTransitionRule
ZoneOffsetTransitionRule of =
ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.MONDAY, LocalTime.of(2, 0),
false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, ZoneOffset.ofHours(1),
ZoneOffset.ofHours(1), ZoneOffset.ofHours(2));
But I'm not sure if it's correct or how to create a ZoneId from this.
Is that transition rule accurate to model the DAYLIGHT component of my VTIMEZONE?
How can I create a ZoneId from this so I can eventually create a ZonedDateTime?
The only way to get a ZoneId (at least if we’re not very hacky) is through the factory methods of ZoneId and its subclass ZoneOffset. It might seem at first that this leaves of with the built-in ZoneIds. However, there’s a backdoor for specifying additional ZoneIds that ZoneId.of can then produce. It’s called ZoneRulesProvider. We need to specify an new and unique ID and we need to specify the zone rules (hence the name ZoneRulesProvider).
So with your ZoneOffsetTransitionRule you are already on the way. We need two of them, though, yours for transitioning to DST (which would normally have happened in the spring) and one for going the other way in the fall.
The following listing isn’t production code, of course, but is just to demonstrate that it is doable to develop and register your own ZoneRulesProvider.
final String customZoneId = "Custom-CEST-1";
final ZoneOffset standardOffset = ZoneOffset.ofHours(1);
final ZoneOffset summerTimeOffset = ZoneOffset.ofHours(2);
// At least one transistion is required
ZoneOffsetTransition initialTransition = ZoneOffsetTransition.of(
LocalDateTime.of(1601, 1, 1, 3, 0), summerTimeOffset, standardOffset);
List<ZoneOffsetTransition> transitionList = List.of(initialTransition);
// Rules for going to and from summer time (DST)
ZoneOffsetTransitionRule springRule =
ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.MONDAY, LocalTime.of(2, 0),
false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, standardOffset,
standardOffset, summerTimeOffset);
ZoneOffsetTransitionRule fallRule =
ZoneOffsetTransitionRule.of(Month.OCTOBER, -1, DayOfWeek.SUNDAY, LocalTime.of(2, 0),
false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, standardOffset,
summerTimeOffset, standardOffset);
ZoneRules rules = ZoneRules.of(standardOffset, standardOffset,
transitionList, transitionList, List.of(springRule, fallRule));
// The heart of the magic: the ZoneRulesProvider
ZoneRulesProvider customProvider = new ZoneRulesProvider() {
#Override
protected Set<String> provideZoneIds() {
return Set.of(customZoneId);
}
#Override
protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
return new TreeMap<>(Map.of(customZoneId, rules));
}
#Override
protected ZoneRules provideRules(String zoneId, boolean forCaching) {
return rules;
}
};
// Registering the ZoneRulesProvider is the key to ZoneId using it
ZoneRulesProvider.registerProvider(customProvider);
// Get an instance of our custom ZoneId
ZoneId customZone = ZoneId.of(customZoneId);
// Transition to standard time was Sunday, October 29, 2017,
// so try the day before and the day after
System.out.println(LocalDate.of(2017, Month.OCTOBER, 28).atStartOfDay(customZone));
System.out.println(LocalDate.of(2017, Month.OCTOBER, 30).atStartOfDay(customZone));
// The special thing about our custom ZoneID is that transition to DST
// happened on Monday, January 1. Try the day before and the day after.
System.out.println(LocalDate.of(2017, Month.DECEMBER, 31).atStartOfDay(customZone));
System.out.println(LocalDate.of(2018, Month.JANUARY, 2).atStartOfDay(customZone));
The code prints:
2017-10-28T00:00+02:00[Custom-CEST-1]
2017-10-30T00:00+01:00[Custom-CEST-1]
2017-12-31T00:00+01:00[Custom-CEST-1]
2018-01-02T00:00+02:00[Custom-CEST-1]
We see that we get the expected DST offset of +02:00 exactly before the transition to standard time and again after the transition to summer time.
Related
I'm attempting to model an iCalendar VTIMEZONE object using Java's ZoneId and ZoneOffsetTransitionRule.
My VTIMEZONE object looks like
BEGIN:VTIMEZONE
TZID:Central European Standard Time
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=1;BYDAY=MO
END:DAYLIGHT
END:VTIMEZONE
I need to create my own ZoneId to model this because, as far as I know, there isn't a ZoneId available with these offsets and in which DST starts on the first Monday of January (as opposed to some Sunday of March).
I have the following for creating a ZoneOffsetTransitionRule
ZoneOffsetTransitionRule of =
ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.MONDAY, LocalTime.of(2, 0),
false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, ZoneOffset.ofHours(1),
ZoneOffset.ofHours(1), ZoneOffset.ofHours(2));
But I'm not sure if it's correct or how to create a ZoneId from this.
Is that transition rule accurate to model the DAYLIGHT component of my VTIMEZONE?
How can I create a ZoneId from this so I can eventually create a ZonedDateTime?
The only way to get a ZoneId (at least if we’re not very hacky) is through the factory methods of ZoneId and its subclass ZoneOffset. It might seem at first that this leaves of with the built-in ZoneIds. However, there’s a backdoor for specifying additional ZoneIds that ZoneId.of can then produce. It’s called ZoneRulesProvider. We need to specify an new and unique ID and we need to specify the zone rules (hence the name ZoneRulesProvider).
So with your ZoneOffsetTransitionRule you are already on the way. We need two of them, though, yours for transitioning to DST (which would normally have happened in the spring) and one for going the other way in the fall.
The following listing isn’t production code, of course, but is just to demonstrate that it is doable to develop and register your own ZoneRulesProvider.
final String customZoneId = "Custom-CEST-1";
final ZoneOffset standardOffset = ZoneOffset.ofHours(1);
final ZoneOffset summerTimeOffset = ZoneOffset.ofHours(2);
// At least one transistion is required
ZoneOffsetTransition initialTransition = ZoneOffsetTransition.of(
LocalDateTime.of(1601, 1, 1, 3, 0), summerTimeOffset, standardOffset);
List<ZoneOffsetTransition> transitionList = List.of(initialTransition);
// Rules for going to and from summer time (DST)
ZoneOffsetTransitionRule springRule =
ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.MONDAY, LocalTime.of(2, 0),
false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, standardOffset,
standardOffset, summerTimeOffset);
ZoneOffsetTransitionRule fallRule =
ZoneOffsetTransitionRule.of(Month.OCTOBER, -1, DayOfWeek.SUNDAY, LocalTime.of(2, 0),
false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, standardOffset,
summerTimeOffset, standardOffset);
ZoneRules rules = ZoneRules.of(standardOffset, standardOffset,
transitionList, transitionList, List.of(springRule, fallRule));
// The heart of the magic: the ZoneRulesProvider
ZoneRulesProvider customProvider = new ZoneRulesProvider() {
#Override
protected Set<String> provideZoneIds() {
return Set.of(customZoneId);
}
#Override
protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
return new TreeMap<>(Map.of(customZoneId, rules));
}
#Override
protected ZoneRules provideRules(String zoneId, boolean forCaching) {
return rules;
}
};
// Registering the ZoneRulesProvider is the key to ZoneId using it
ZoneRulesProvider.registerProvider(customProvider);
// Get an instance of our custom ZoneId
ZoneId customZone = ZoneId.of(customZoneId);
// Transition to standard time was Sunday, October 29, 2017,
// so try the day before and the day after
System.out.println(LocalDate.of(2017, Month.OCTOBER, 28).atStartOfDay(customZone));
System.out.println(LocalDate.of(2017, Month.OCTOBER, 30).atStartOfDay(customZone));
// The special thing about our custom ZoneID is that transition to DST
// happened on Monday, January 1. Try the day before and the day after.
System.out.println(LocalDate.of(2017, Month.DECEMBER, 31).atStartOfDay(customZone));
System.out.println(LocalDate.of(2018, Month.JANUARY, 2).atStartOfDay(customZone));
The code prints:
2017-10-28T00:00+02:00[Custom-CEST-1]
2017-10-30T00:00+01:00[Custom-CEST-1]
2017-12-31T00:00+01:00[Custom-CEST-1]
2018-01-02T00:00+02:00[Custom-CEST-1]
We see that we get the expected DST offset of +02:00 exactly before the transition to standard time and again after the transition to summer time.
Initialize java.util.Calendar with May, 31 1900. Then add one year to it twenty times.
Here's code:
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
fun main(args : Array<String>) {
val f = SimpleDateFormat("yyyy.dd.MM")
val cal = Calendar.getInstance()
cal.set(1900, Calendar.MAY, 31)
for(i in 1..20) {
println(f.format(cal.time))
cal.add(Calendar.YEAR, 1)
}
}
The output is following:
1900.31.05
1901.31.05
1902.31.05
1903.31.05
1904.31.05
1905.31.05
1906.31.05
1907.31.05
1908.31.05
1909.31.05
1910.31.05
1911.31.05
1912.31.05
1913.31.05
1914.31.05
1915.31.05
1916.31.05
1917.31.05
1918.01.06
1919.01.06
Why I get June, 1 instead of May, 31 since 1918?
UPD: with time information
1917.31.05 23:38:50.611
1918.01.06 01:38:50.611
If this is DST invention, how do I prevent that?
You seem to be running your code in a timezone that changed its offset by two hours in 1917 or 1918. That is, the number of hours ahead or behind UTC changed. I've no idea why your timezone would have done that, but I'm sure there's a good historical reason for it.
If you're only interested in dates, without the Time component, use the java.time.LocalDate class, which effectively represents a day, month and year only. It's not subject to any daylight savings anomalies.
LocalDate today = LocalDate.now();
or
LocalDate moonLanding = LocalDate.of(1969, 7, 20);
I am assuming that you are in Europe/Moscow time zone. Turing85 in a comment correctly spotted the cause of the behaviour you observed: In 1918 summer time (DST) in your time zone began on May 31. The clock was moved forward from 22:00 to 24:00, that is, by two hours. Your Calendar object is aware of this and therefore refuses to give 23:38:50.611 on this date. Instead it picks the time 2 hours later, 1918.01.06 01:38:50.611. Now the month and day-of-month have changed to 1st of June.
Unfortunately this change is kept in the Calendar and carried on to the following year.
If this is DST invention, how do I prevent that?
Thomas Kläger in a comment gave the right solution: If you only need the dates, use LocalDate from java.time:
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu.dd.MM");
LocalDate date = LocalDate.of(1900, Month.MAY, 31);
for (int i = 1; i <= 20; i++) {
System.out.println(date.format(dateFormatter));
date = date.plusYears(1);
}
Output (abbreviated):
1900.31.05
1901.31.05
…
1917.31.05
1918.31.05
1919.31.05
The “local” in LocalDate means “without timezone” in java.time jargon, so this is guaranteed to keep you free of surprises from time zone anomalies.
If you need a time, you may consider LocalDateTime, but since this is without time zone too, it will give you the non-existing time of 1918.31.05 23:38:50.611, so maybe not.
An alternative thing you may consider is adding the right number of years to your origin of 1900.31.05 23:38:50.611. Then at least you will only have surprises in years where you hit a non-existing time. I am using ZonedDateTime for this demonstration:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu.dd.MM HH:mm:ss.SSS");
ZonedDateTime originalDateTime = ZonedDateTime.of(1900, Month.MAY.getValue(), 31,
23, 30, 50, 611000000, ZoneId.of("Europe/Moscow"));
for (int i = 0; i < 25; i++) {
System.out.println(originalDateTime.plusYears(i).format(formatter));
}
Output:
1900.31.05 23:30:50.611
1901.31.05 23:30:50.611
…
1917.31.05 23:30:50.611
1918.01.06 01:30:50.611
1919.01.06 00:30:50.611
1920.31.05 23:30:50.611
…
1924.31.05 23:30:50.611
Again in 1919 summer time began on May 31. This time the clock was only advanced by 1 hour, from 23 to 24, so you get only 1 hour later than the imaginary time of 23:30:50.611.
I am recommending java.time for date and time work, not least when doing math on dates like you do. The Calendar class is considered long outmoded. java.time was designed acknowledging that Calendar and the other old classes were poorly designed. The modern ones are so much nicer to work with.
How could I be sure it was Moscow?
In no other time zone than Europe/Moscow is the time of 1918.31.05 23:38:50.611 nonexistent. I checked:
LocalDateTime dateTime = LocalDateTime.of(1918, Month.MAY, 31, 23, 38, 50, 611000000);
for (String zid : ZoneId.getAvailableZoneIds()) {
ZonedDateTime zdt = dateTime.atZone(ZoneId.of(zid));
LocalDateTime newDateTime = zdt.toLocalDateTime();
if (! newDateTime.equals(dateTime)) {
System.out.println(zid + ": -> " + zdt + " -> " + newDateTime);
}
}
Output:
Europe/Moscow: -> 1918-06-01T01:38:50.611+04:31:19[Europe/Moscow] -> 1918-06-01T01:38:50.611
W-SU: -> 1918-06-01T01:38:50.611+04:31:19[W-SU] -> 1918-06-01T01:38:50.611
“W-SU” is a deprecated name for the same time zone, it stands for Western Soviet Union.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Time Changes in Moscow Over the Years
List of tz database time zones showing W-SU as deprecated.
An old message on an IANA mailing list stating “But long ago … we based the name on some more-political entity than a city name. For example, we used "W-SU" for the western Soviet Union…”
I created two ZonedDateTime objects and I think they are should be equal:
public static void main(String[] args) {
ZoneId zid = ZoneId.of("America/New_York");
ZoneOffset offset = ZoneOffset.from(LocalDateTime.now().atZone(zid));
ZonedDateTime zdt0 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, offset);
ZonedDateTime zdt1 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, zid);
boolean equals = Objects.equals(zdt0, zdt1);
System.out.println("equals: " + equals);
}
In debugger I see that class of member of ZonedDateTime zone in first case is java.time.ZoneOffset and in second java.time.ZoneRegion and this is makes ZonedDateTime objects not equal. This is confusing...
Any ideas?
You are checking for object equality which evaluates to false as these objects are not equivalent. One is bound to a ZoneId, the other to a ZoneOffset. If you want to check whether they represent the same time, you can use the not very intuitively named method isEqual.
E.g.:
ZoneId zid = ZoneId.of("America/New_York");
ZoneOffset offset = ZoneOffset.from(LocalDateTime.now().atZone(zid));
ZonedDateTime zdt0 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, offset);
ZonedDateTime zdt1 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, zid);
System.out.println("isEqual:" + zdt0.isEqual(zdt1));
System.out.println("equals: " + zdt0.equals(zdt1));
prints:
isEqual:true
equals: false
Btw, note that you don’t need to use Objects.equals(a,b) for two objects you already know to be non-null. You can invoke a.equals(b) directly.
This played hell on me for hours too when using Jackson to serialize / deserialize instances of ZonedDateTime and then compare them against each other for equality to verify that my code was working correctly. I don't fully understand the implications but all I've learned is to use isEqual instead of equals. But this throws a big wrench in testing plans as most assertion utilities will just call the standard .equals().
Here's what I finally came up with after struggling for quite some time:
#Test
public void zonedDateTimeCorrectlyRestoresItself() {
// construct a new instance of ZonedDateTime
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Z"));
// offset = {ZoneOffset#3820} "Z"
// zone = {ZoneOffset#3820} "Z"
String starting = now.toString();
// restore an instance of ZonedDateTime from String
ZonedDateTime restored = ZonedDateTime.parse(starting);
// offset = {ZoneOffset#3820} "Z"
// zone = {ZoneOffset#3820} "Z"
assertThat(now).isEqualTo(restored); // ALWAYS succeeds
System.out.println("test");
}
#Test
public void jacksonIncorrectlyRestoresZonedDateTime() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
// construct a new instance of ZonedDateTime
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Z"));
// offset = {ZoneOffset#3820} "Z"
// zone = {ZoneOffset#3820} "Z"
String converted = objectMapper.writeValueAsString(now);
// restore an instance of ZonedDateTime from String
ZonedDateTime restored = objectMapper.readValue(converted, ZonedDateTime.class);
// offset = {ZoneOffset#3820} "Z"
// zone = {ZoneOffset#3821} "UTC"
assertThat(now).isEqualTo(restored); // NEVER succeeds
System.out.println("break point me");
}
The equals() method on ZonedDateTime requires that all component parts of the object are equal. Since a ZoneOffset is not equal to a ZoneRegion (even though both are subclasses of ZoneId), the method returns false. Read about VALJOs to understand more as to why value types are compared in this way.
The isEqual method only compares the instant on the time-line, which may or may not be what you want. You can also use the timeLineOrder() method to compare two ZoneDateTime only using the time-line.
This caused us pain for a while too. The ZonedDateTime values actually represent identical times, however their zone "types" are different, which is why they are not equal.
The above answers are correct, however I thought I'd add a visual that might further help:
In the ZonedDateTime code, we find, which shows the zone comparison:
In my java program I do something like this
1.)
LocalDateTime currentDateTime = new LocalDateTime();
LocalDateTime newDateTime = new LocalDateTime(currentDateTime);
newDateTime = newDateTime.plusDays(daysOffset);
newDateTime = newDateTime.plusHours(hoursOffset);
newDateTime = newDateTime.plusMinutes(minutesOffset);
Later in the code I do
2.)
boolean newDateTimeIsInWinter =
dateTimeZone.getOffset(newDateTime.toDateTime().getMillis()) == dateTimeZone.getStandardOffset(newDateTime.toDateTime().getMillis());
The of call newDateTime.toDateTime() may result java.lang.IllegalArgumentException: Illegal instant due to time zone offset transition.
So I'd like to put something like this between 1.) and 2.)
if (dateTimeZone.isLocalDateTimeGap(newDateTime))
{
int dstOffsetMinutes = ???;
newDateTime = newDateTime.plusMinutes(dstOffsetMinutes);
}
Can anyone tell me the right replacement for ??? It's not as easy as setting it to 60. For example the LHST timezone hast only 30 Minutes offset.
Ask DateTimeZome About DST
To determine if a particular moment is in Daylight Saving Time or not for a particular time zone, ask the [DateTimeZone][1] object.
boolean isStandardTime = DateTimeZone.forID( "America/Montreal" ).isStandardOffset( DateTime.now().getMillis() );
When To Use "Local" Classes
If you care about time zone, offsets, and Daylight Saving Time, do not use LocalDateTime, LocalDate, or LocalTime. That is what DateTime is for.
Use the "Local" classes when you mean a date and/or time in general not for a specific place or time zone. For example if you want to say “Christmas starts at 2014-12-25T00:00:00.000" that means at midnight on the morning of the 25th at any particular location. But that LocalDateTime could mean one DateTime for Paris but a different DateTime (different moment) in Montréal.
I solved my problem by using DateTime instead LocalDateTime
1.) now is
DateTime newDateTimeUTC = currentDateTime.toDateTime();
newDateTimeUTC = newDateTimeUTC.plusDays(daysOffset);
newDateTimeUTC = newDateTimeUTC.plusHours(hoursOffset);
newDateTimeUTC = newDateTimeUTC.plusMinutes(minutesOffset);
LocalDateTime newDateTime = newDateTimeUTC.toLocalDateTime();
2.) still is
boolean newDateTimeIsInWinter =
dateTimeZone.getOffset(newDateTime.toDateTime().getMillis()) == dateTimeZone.getStandardOffset(newDateTime.toDateTime().getMillis());
There is no need for isLocalDateTimeGap or anything else.
But this still does not anwser the original question How to get DST offset with Joda Time?
My API allows library client to pass Date:
method(java.util.Date date)
Working with Joda-Time, from this date I would like to extract the month and iterate over all days this month contains.
Now, the passed date is usually new Date() - meaning current instant. My problem actually is setting the new DateMidnight(jdkDate) instance to be at the start of the month.
Could someone please demonstrates this use case with Joda-Time?
Midnight at the start of the first day of the current month is given by:
// first midnight in this month
DateMidnight first = new DateMidnight().withDayOfMonth(1);
// last midnight in this month
DateMidnight last = first.plusMonths(1).minusDays(1);
If starting from a java.util.Date, a different DateMidnight constructor is used:
// first midnight in java.util.Date's month
DateMidnight first = new DateMidnight( date ).withDayOfMonth(1);
Joda Time java doc - https://www.joda.org/joda-time/apidocs/overview-summary.html
An alternative way (without taking DateMidnight into account) to get the first day of the month would be to use:
DateTime firstDayOfMonth = new DateTime().dayOfMonth().withMinimumValue();
First Moment Of The Day
The answer by ngeek is correct, but fails to put the time to the first moment of the day. To adjust the time, append a call to withTimeAtStartOfDay.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
org.joda.time.DateTime startOfThisMonth = new org.joda.time.DateTime().dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
org.joda.time.DateTime startofNextMonth = startOfThisMonth.plusMonths( 1 ).dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
System.out.println( "startOfThisMonth: " + startOfThisMonth );
System.out.println( "startofNextMonth: " + startofNextMonth );
When run in Seattle US…
startOfThisMonth: 2013-11-01T00:00:00.000-07:00
startofNextMonth: 2013-12-01T00:00:00.000-08:00
Note the difference in those two lines of console output: -7 vs -8 because of Daylight Saving Time.
Generally one should always specify the time zone rather than rely on default. Omitted here for simplicity. One should add a line like this, and pass the time zone object to the constructors used in example above.
// Time Zone list: http://joda-time.sourceforge.net/timezones.html (Possibly out-dated, read note on that page)
// UTC time zone (no offset) has a constant, so no need to construct: org.joda.time.DateTimeZone.UTC
org.joda.time.DateTimeZone kolkataTimeZone = org.joda.time.DateTimeZone.forID( "Asia/Kolkata" );
java.time
The above is correct but outdated. The Joda-Time library is now supplanted by the java.time framework built into Java 8 and later.
The LocalDate represents a date-only value without time-of-day and without time zone. A time zone is crucial in determine a date. For any given moment the date varies by zone around the globe.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
Use one of the TemporalAdjusters to get first of month.
LocalDate firstOfMonth = today.with( TemporalAdjusters.firstDayOfMonth() );
The LocalDate can generate a ZonedDateTime that represents the first moment of the day.
ZonedDateTime firstMomentOfCurrentMonth = firstOfMonth.atStartOfDay( zoneId );
Oh, I did not see that this was about jodatime. Anyway:
Calendar c = Calendar.getInstance();
c.setTime(date);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
int min = c.getActualMinimum(Calendar.DAY_OF_MONTH);
int max = c.getActualMaximum(Calendar.DAY_OF_MONTH);
for (int i = min; i <= max; i++) {
c.set(Calendar.DAY_OF_MONTH, i);
System.out.println(c.getTime());
}
Or using commons-lang:
Date min = DateUtils.truncate(date, Calendar.MONTH);
Date max = DateUtils.addMonths(min, 1);
for (Date cur = min; cur.before(max); cur = DateUtils.addDays(cur, 1)) {
System.out.println(cur);
}
DateMidnight is now deprecated. Instead you can do:
LocalDate firstOfMonth = new LocalDate(date).withDayOfMonth(1);
LocalDate lastOfMonth = firstOfMonth.plusMonths(1).minusDays(1);
If you know the time zone use new LocalDate(date, timeZone) instead for greater accuracy.
You can also do .dayOfMonth().withMinimumValue() instead of .withDayOfMonth(1)
EDIT:
This will give you 12/1/YYYY 00:00 and 12/31/YYYY 00:00. If you rather the last of the month be actually the first of the next month (because you are doing a between clause), then remove the minusDays(1) from the lastOfMonth calculation
You can get Start date and end date of month using this:
DateTime monthStartDate = new DateTime().dayOfMonth().withMinimumValue();
DateTime monthEndDate = new DateTime().dayOfMonth().withMaximumValue();