/*
* Copyright (C) 2012 Artur Suilin
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ru.metrika4j;
import java.util.Calendar;
import java.util.Date;
/**
* Представление даты в API Яндекс.Метрики. Содержит год, месяц и день, без указания времени.
*
* @author Artur Suilin
*/
public class MetrikaDate {
private final int year;
private final int month; // 1-based
private final int day;
/**
* All minutes have this many milliseconds except the last minute of the day on a day defined with
* a leap second.
*/
public static final long MILLISECS_PER_MINUTE = 60 * 1000;
/** Number of milliseconds per hour, except when a leap second is inserted. */
public static final long MILLISECS_PER_HOUR = 60 * MILLISECS_PER_MINUTE;
/**
* Number of leap seconds per day expect on
* <BR/>1. days when a leap second has been inserted, e.g. 1999 JAN 1.
* <BR/>2. Daylight-savings "spring forward" or "fall back" days.
*/
protected static final long MILLISECS_PER_DAY = 24 * MILLISECS_PER_HOUR;
public MetrikaDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
}
protected MetrikaDate(Calendar c) {
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH) + 1;
day = c.get(Calendar.DAY_OF_MONTH);
}
public static MetrikaDate today() {
return new MetrikaDate();
}
public static MetrikaDate yesterday() {
return new MetrikaDate().plusDays(-1);
}
public MetrikaDate monthAgo() {
Calendar c = makeCalendar();
c.add(Calendar.MONTH, -1);
return new MetrikaDate(c);
}
public MetrikaDate yearAgo() {
Calendar c = makeCalendar();
c.add(Calendar.YEAR, -1);
return new MetrikaDate(c);
}
public MetrikaDate weekAgo() {
Calendar c = makeCalendar();
c.add(Calendar.WEEK_OF_MONTH, -1);
return new MetrikaDate(c);
}
public MetrikaDate dayAgo() {
Calendar c = makeCalendar();
c.add(Calendar.DAY_OF_MONTH, -1);
return new MetrikaDate(c);
}
public MetrikaDate() {
this(Calendar.getInstance());
}
/**
* Конструирует дату из формата YYYYMMDD или YYYYMM, используемого в API
*
* @see #toApiString()
*/
public MetrikaDate(String value) {
int length = value.length();
if (length == 6 || length == 8) {
year = Integer.parseInt(value.substring(0, 4));
month = Integer.parseInt(value.substring(4, 6));
if (length == 8) {
day = Integer.parseInt(value.substring(6, 8));
} else {
day = 0;
}
} else {
throw new IllegalArgumentException("Invalid date format: " + value);
}
}
public int getYear() {
return year;
}
public int getMonth() {
return month;
}
public int getDay() {
return day;
}
public boolean hasDay() {
return day > 0;
}
/**
* Представляет дату в формате YYYYMMDD или YYYYMM, используемом в API
*
* @see #MetrikaDate(String)
*/
public String toApiString() {
return String.format("%04d%02d%02d", year, month, day);
}
Calendar makeCalendar() {
Calendar c = Calendar.getInstance();
c.clear();
c.set(year, month - 1, day);
return c;
}
@Override
public String toString() {
return toApiString();
}
public Date toJavaDate() {
return makeCalendar().getTime();
}
public MetrikaDate plusDays(int days) {
Calendar c = makeCalendar();
c.add(Calendar.DAY_OF_MONTH, days);
return new MetrikaDate(c);
}
public long getUnixDay() {
Calendar c = makeCalendar();
long offset = c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET);
return (long) Math.floor((double) (c.getTime().getTime() + offset) / ((double) MILLISECS_PER_DAY));
}
/**
* find the number of days from this date to the given end date.
* later end dates result in positive values.
* Note this is not the same as subtracting day numbers. Just after midnight subtracted from just before
* midnight is 0 days for this method while subtracting day numbers would yields 1 day.
*
* @param end - any date representing the moment of time at the end of the interval for calculation.
*/
public int diffDayPeriods(MetrikaDate end) {
Calendar endCalendar = end.makeCalendar();
Calendar thisCalendar = this.makeCalendar();
long endL = endCalendar.getTimeInMillis() + endCalendar.getTimeZone().getOffset(endCalendar.getTimeInMillis());
long startL = thisCalendar.getTimeInMillis() + thisCalendar.getTimeZone()
.getOffset(thisCalendar.getTimeInMillis());
long result = (endL - startL) / MILLISECS_PER_DAY;
return (int) result;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MetrikaDate that = (MetrikaDate) o;
return day == that.day && month == that.month && year == that.year;
}
@Override
public int hashCode() {
int result = year;
result = 31 * result + month;
result = 31 * result + day;
return result;
}
}