rjc.jplanner.model.DateTime.java Source code

Java tutorial

Introduction

Here is the source code for rjc.jplanner.model.DateTime.java

Source

/**************************************************************************
 *  Copyright (C) 2015 by Richard Crook                                   *
 *  https://github.com/dazzle50/JPlannerSWT                               *
 *                                                                        *
 *  This program is free software: you can redistribute it and/or modify  *
 *  it under the terms of the GNU General Public License as published by  *
 *  the Free Software Foundation, either version 3 of the License, or     *
 *  (at your option) any later version.                                   *
 *                                                                        *
 *  This program is distributed in the hope that it will be useful,       *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *  GNU General Public License for more details.                          *
 *                                                                        *
 *  You should have received a copy of the GNU General Public License     *
 *  along with this program.  If not, see http://www.gnu.org/licenses/    *
 **************************************************************************/

package rjc.jplanner.model;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

import org.apache.commons.lang.StringUtils;

/*************************************************************************************************/
/********************************* Date-time (with no timezone) **********************************/
/*************************************************************************************************/

public class DateTime implements Comparable<DateTime> {
    // range constrained by valid Date (approx 5,800,000 BC to 5,800,000 AD)
    private long m_milliseconds; // milliseconds from 00:00:00.000 start of epoch-day

    public static final long MILLISECONDS_IN_DAY = Time.MILLISECONDS_IN_DAY; // milliseconds in day
    public static final DateTime MIN_VALUE = new DateTime(Date.MIN_VALUE, Time.MAX_VALUE);
    public static final DateTime MAX_VALUE = new DateTime(Date.MAX_VALUE, Time.MIN_VALUE);

    public enum Interval {
        YEAR, HALFYEAR, QUARTERYEAR, MONTH, WEEK, DAY
    }

    private static final char QUOTE = '\'';
    private static final char CHARB = 'B';
    private static final String CODE = "#@B!";

    /***************************************** constructor *****************************************/
    public DateTime(long ms) {
        // constructor
        m_milliseconds = ms;
    }

    /***************************************** constructor *****************************************/
    public DateTime(String str) {
        // constructor, date must be split from time by a space
        int split = str.indexOf('T');
        Date date = Date.fromString(str.substring(0, split));
        Time time = Time.fromString(str.substring(split + 1, str.length()));
        m_milliseconds = date.epochday() * MILLISECONDS_IN_DAY + time.milliseconds();
    }

    /***************************************** constructor *****************************************/
    public DateTime(Date date, Time time) {
        // constructor
        m_milliseconds = date.epochday() * MILLISECONDS_IN_DAY + time.milliseconds();
    }

    /***************************************** constructor *****************************************/
    public DateTime(LocalDateTime dt) {
        // constructor
        Date date = new Date(dt.toLocalDate());
        Time time = new Time(dt.toLocalTime());
        m_milliseconds = date.epochday() * MILLISECONDS_IN_DAY + time.milliseconds();
    }

    /****************************************** toString *******************************************/
    @Override
    public String toString() {
        // convert to string to "YYYY-MM-DDThh:mm:ss.mmm" format
        return date().toString() + "T" + time().toString();
    }

    /****************************************** toString *******************************************/
    public String toString(String format) {
        // convert to string in specified format
        LocalDateTime ldt = LocalDateTime.ofEpochSecond(m_milliseconds / 1000L,
                (int) (m_milliseconds % 1000 * 1000000), ZoneOffset.UTC);

        // to support half-of-year using Bs, quote any unquoted Bs in format
        StringBuilder newFormat = new StringBuilder();
        boolean inQuote = false;
        boolean inB = false;
        char here;
        for (int i = 0; i < format.length(); i++) {
            here = format.charAt(i);

            // are we in quoted text?
            if (here == QUOTE)
                inQuote = !inQuote;

            // replace unquoted Bs with special code
            if (inB && here == CHARB) {
                newFormat.append(CODE);
                continue;
            }

            // come to end of unquoted Bs
            if (inB && here != CHARB) {
                newFormat.append(QUOTE);
                inB = false;
                inQuote = false;
            }

            // start of unquoted Bs, start quote with special code
            if (!inQuote && here == CHARB) {
                // avoid creating double quotes
                if (newFormat.length() > 0 && newFormat.charAt(newFormat.length() - 1) == QUOTE) {
                    newFormat.deleteCharAt(newFormat.length() - 1);
                    newFormat.append(CODE);
                } else
                    newFormat.append("'" + CODE);
                inQuote = true;
                inB = true;
            } else {
                newFormat.append(here);
            }
        }

        // close quote if quote still open
        if (inQuote)
            newFormat.append(QUOTE);

        String str = ldt.format(DateTimeFormatter.ofPattern(newFormat.toString()));

        // no special code so can return string immediately
        if (!str.contains(CODE))
            return str;

        // determine half-of-year
        String yearHalf;
        if (month() < 7)
            yearHalf = "1";
        else
            yearHalf = "2";

        // four or more Bs is not allowed
        String Bs = StringUtils.repeat(CODE, 4);
        if (str.contains(Bs))
            throw new IllegalArgumentException("Too many pattern letters: B");

        // replace three Bs
        Bs = StringUtils.repeat(CODE, 3);
        if (yearHalf.equals("1"))
            str = str.replace(Bs, yearHalf + "st half");
        else
            str = str.replace(Bs, yearHalf + "nd half");

        // replace two Bs
        Bs = StringUtils.repeat(CODE, 2);
        str = str.replace(Bs, "H" + yearHalf);

        // replace one Bs
        Bs = StringUtils.repeat(CODE, 1);
        str = str.replace(Bs, yearHalf);

        return str;
    }

    /******************************************** date *********************************************/
    public Date date() {
        if (m_milliseconds < 0)
            return new Date((int) (m_milliseconds / MILLISECONDS_IN_DAY) - 1);

        return new Date((int) (m_milliseconds / MILLISECONDS_IN_DAY));
    }

    /******************************************** time *********************************************/
    public Time time() {
        int ms = (int) (m_milliseconds % MILLISECONDS_IN_DAY);
        if (ms < 0)
            ms += MILLISECONDS_IN_DAY;

        return Time.fromMilliseconds(ms);
    }

    /********************************************* now *********************************************/
    public static DateTime now() {
        // return a new DateTime from current system clock
        return new DateTime(LocalDateTime.now());
    }

    /******************************************** year *********************************************/
    public int year() {
        return date().year();
    }

    /******************************************** month ********************************************/
    public int month() {
        // return month of year as number 1 to 12
        return date().month();
    }

    /***************************************** dayOfMonth ******************************************/
    public int dayOfMonth() {
        return date().dayOfMonth();
    }

    /******************************************** hours ********************************************/
    public int hours() {
        return time().hours();
    }

    /******************************************* minutes *******************************************/
    public int minutes() {
        return time().minutes();
    }

    /******************************************* seconds *******************************************/
    public int seconds() {
        return time().seconds();
    }

    /*************************************** addMilliseconds ***************************************/
    public DateTime plusMilliseconds(long ms) {
        return new DateTime(m_milliseconds + ms);
    }

    /***************************************** milliseconds ****************************************/
    public long milliseconds() {
        return m_milliseconds;
    }

    /******************************************** trunc ********************************************/
    public DateTime trunc(Interval interval) {
        // return new date-time truncated down to specified interval
        if (interval == Interval.YEAR) {
            Date date = new Date(date().year(), 1, 1);
            return new DateTime(date.epochday() * MILLISECONDS_IN_DAY);
        }

        if (interval == Interval.HALFYEAR) {
            Date date = date();
            int month = ((date.month() - 1) / 6) * 6 + 1;

            Date hy = new Date(date.year(), month, 1);
            return new DateTime(hy.epochday() * MILLISECONDS_IN_DAY);
        }

        if (interval == Interval.QUARTERYEAR) {
            Date date = date();
            int month = ((date.month() - 1) / 3) * 3 + 1;

            Date qy = new Date(date.year(), month, 1);
            return new DateTime(qy.epochday() * MILLISECONDS_IN_DAY);
        }

        if (interval == Interval.MONTH) {
            Date date = date();
            Date md = new Date(date.year(), date.month(), 1);
            return new DateTime(md.epochday() * MILLISECONDS_IN_DAY);
        }

        if (interval == Interval.WEEK) {
            int day = (int) (m_milliseconds / MILLISECONDS_IN_DAY);
            int dayOfWeek = (day + 3) % 7;
            return new DateTime((day - dayOfWeek) * MILLISECONDS_IN_DAY);
        }

        if (interval == Interval.DAY) {
            long ms = (m_milliseconds / MILLISECONDS_IN_DAY) * MILLISECONDS_IN_DAY;
            return new DateTime(ms);
        }

        throw new IllegalArgumentException("interval=" + interval);
    }

    /****************************************** plusDays *******************************************/
    public DateTime plusDays(int days) {
        // return new date-time specified days added or subtracted
        return new DateTime(m_milliseconds + days * MILLISECONDS_IN_DAY);
    }

    /***************************************** plusMonths ******************************************/
    public DateTime plusMonths(int months) {
        // return new date-time specified months added or subtracted
        return new DateTime(date().plusMonths(months), time());
    }

    /***************************************** plusYears *******************************************/
    public DateTime plusYears(int years) {
        // return new date-time specified months added or subtracted
        return new DateTime(date().plusYears(years), time());
    }

    /**************************************** plusInterval *****************************************/
    public DateTime plusInterval(Interval interval) {
        // add one specified interval to date-time
        if (interval == Interval.YEAR)
            return plusYears(1);

        if (interval == Interval.HALFYEAR)
            return plusMonths(6);

        if (interval == Interval.QUARTERYEAR)
            return plusMonths(3);

        if (interval == Interval.MONTH)
            return plusMonths(1);

        if (interval == Interval.WEEK)
            return plusDays(7);

        if (interval == Interval.DAY)
            return plusDays(1);

        throw new IllegalArgumentException("interval=" + interval);
    }

    /****************************************** isLessThan *****************************************/
    public boolean isLessThan(DateTime other) {
        return m_milliseconds < other.m_milliseconds;
    }

    /******************************************* equals ********************************************/
    public boolean equals(DateTime other) {
        return m_milliseconds == other.m_milliseconds;
    }

    /****************************************** compareTo ******************************************/
    @Override
    public int compareTo(DateTime other) {
        long sign = m_milliseconds - other.m_milliseconds;
        if (sign > 0)
            return 1;
        if (sign < 0)
            return -1;
        return 0;
    }

}