summaryrefslogtreecommitdiffstats
path: root/include/tools/date.hxx
blob: 6179d637e8ba44660f8f1c9ff2b52fe907a683f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */
#ifndef INCLUDED_TOOLS_DATE_HXX
#define INCLUDED_TOOLS_DATE_HXX

#include <tools/toolsdllapi.h>

#include <ostream>

#include <com/sun/star/util/Date.hpp>

namespace com::sun::star::util { struct DateTime; }

enum DayOfWeek { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
                 SATURDAY, SUNDAY };

/** Represents a date in the proleptic Gregorian calendar.

    Largest representable date is 32767-12-31 = 327671231

    Smallest representable date is -32768-01-01 = -327680101

    Due to possible conversions to css::util::Date, which has a short
    Year member variable, these limits are fix.

    Year value 0 is unused. The year before year 1 CE is year 1 BCE, which is
    the traditional proleptic Gregorian calendar.

    This is not how ISO 8601:2000 defines things (but ISO 8601:1998 Draft
    Revision did), but it enables class Date to be used for writing XML files
    as XML Schema Part 2 in D.3.2 No Year Zero says
    "The year "0000" is an illegal year value.", see
    https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#noYearZero
    and furthermore the note for 3.2.7 dateTime
    https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#dateTime

 */
class SAL_WARN_UNUSED TOOLS_DLLPUBLIC Date
{
private:
    sal_Int32       mnDate;
    void            setDateFromDMY( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear );

public:
    enum DateInitSystem
    {
        SYSTEM
    };

    enum DateInitEmpty
    {
        EMPTY
    };

                    explicit Date( DateInitEmpty ) : mnDate(0) {}
                    explicit Date( DateInitSystem );
                    explicit Date( sal_Int32 nDate ) : mnDate(nDate) {}
                    Date( const Date& rDate ) : mnDate(rDate.mnDate) {}

                    /** nDay and nMonth both must be <100, nYear must be != 0 */
                    Date( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear )
                        { setDateFromDMY(nDay, nMonth, nYear); }

                    Date( const css::util::Date& rUDate )
                    {
                        setDateFromDMY(rUDate.Day, rUDate.Month, rUDate.Year);
                    }
                    Date( const css::util::DateTime& _rDateTime );

    bool            IsEmpty() const { return mnDate == 0; }

    void            SetDate( sal_Int32 nNewDate );
    sal_Int32       GetDate() const { return mnDate; }
    /** Type safe access for values that are guaranteed to be unsigned, like Date::SYSTEM. */
    sal_uInt32      GetDateUnsigned() const { return static_cast<sal_uInt32>(mnDate < 0 ? -mnDate : mnDate); }
    css::util::Date GetUNODate() const { return css::util::Date(GetDay(), GetMonth(), GetYear()); }

                    /** nNewDay must be <100 */
    void            SetDay( sal_uInt16 nNewDay );
                    /** nNewMonth must be <100 */
    void            SetMonth( sal_uInt16 nNewMonth );
                    /** nNewYear must be != 0 */
    void            SetYear( sal_Int16 nNewYear );

    sal_uInt16      GetDay() const
                    {
                        return mnDate < 0 ?
                            static_cast<sal_uInt16>(-mnDate % 100) :
                            static_cast<sal_uInt16>( mnDate % 100);
                    }
    sal_uInt16      GetMonth() const
                    {
                        return mnDate < 0 ?
                            static_cast<sal_uInt16>((-mnDate / 100) % 100) :
                            static_cast<sal_uInt16>(( mnDate / 100) % 100);
                    }
    sal_Int16       GetYear() const { return static_cast<sal_Int16>(mnDate / 10000); }
    /** Type safe access for values that are guaranteed to be unsigned, like Date::SYSTEM. */
    sal_uInt16      GetYearUnsigned() const { return static_cast<sal_uInt16>((mnDate < 0 ? -mnDate : mnDate) / 10000); }
    sal_Int16       GetNextYear() const { sal_Int16 nY = GetYear(); return nY == -1 ? 1 : nY + 1; }
    sal_Int16       GetPrevYear() const { sal_Int16 nY = GetYear(); return nY == 1 ? -1 : nY - 1; }

    /** Add years skipping year 0 and truncating at limits. If the original
        date was on Feb-29 and the resulting date is not a leap year, the
        result is adjusted to Feb-28.
    */
    void            AddYears( sal_Int16 nAddYears );

    /** Add months skipping year 0 and truncating at limits. If the original
        date was on Feb-29 or day 31 and the resulting date is not a leap year
        or a month with fewer days, the result is adjusted to Feb-28 or day 30.
    */
    void            AddMonths( sal_Int32 nAddMonths );

    /** Add days skipping year 0 and truncating at limits.
     */
    void            AddDays( sal_Int32 nAddDays );

    /** Obtain the day of the week for the date.

        Internally normalizes a copy of values.
        The result may be unexpected for a non-normalized invalid date like
        Date(31,11,2000) or a sequence of aDate.SetDay(31); aDate.SetMonth(11);
     */
    DayOfWeek       GetDayOfWeek() const;

    /** Obtain the day of the year for the date.

        Internally normalizes a copy of values.
        The result may be unexpected for a non-normalized invalid date like
        Date(31,11,2000) or a sequence of aDate.SetDay(31); aDate.SetMonth(11);
     */
    sal_uInt16      GetDayOfYear() const;

    /** Obtain the week of the year for a date.

        @param nMinimumNumberOfDaysInWeek
               How many days of a week must reside in the first week of a year.

        Internally normalizes a copy of values.
        The result may be unexpected for a non-normalized invalid date like
        Date(31,11,2000) or a sequence of aDate.SetDay(31); aDate.SetMonth(11);
     */
    sal_uInt16      GetWeekOfYear( DayOfWeek eStartDay = MONDAY,
                                   sal_Int16 nMinimumNumberOfDaysInWeek = 4 ) const;

    /** Obtain the number of days in the month of the year of the date.

        Internally normalizes a copy of values.

        The result may be unexpected for a non-normalized invalid date like
        Date(31,11,2000) or a sequence of aDate.SetDay(31); aDate.SetMonth(11);

        These would result in 31 as --11-31 rolls over to --12-01 and the
        number of days in December is returned.

        Instead, to obtain the value for the actual set use the static method
        Date::GetDaysInMonth( aDate.GetMonth(), aDate.GetYear()) in such cases.
     */
    sal_uInt16      GetDaysInMonth() const;

    sal_uInt16      GetDaysInYear() const { return (IsLeapYear()) ? 366 : 365; }
    bool            IsLeapYear() const;

    /** If the represented date is valid (1<=month<=12, 1<=day<=(28,29,30,31)
        depending on month/year) AND is of the Gregorian calendar (1582-10-15
        <= date)
     */
    bool            IsValidAndGregorian() const;

    /** If the represented date is valid (1<=month<=12, 1<=day<=(28,29,30,31)
        depending on month/year) */
    bool            IsValidDate() const;

    // Returns true, if the date is the end of the month, false otherwise.
    bool            IsEndOfMonth() const;

    /** Normalize date, invalid day or month values are adapted such that they
        carry over to the next month or/and year, for example 1999-02-32
        becomes 1999-03-04, 1999-13-01 becomes 2000-01-01, 1999-13-42 becomes
        2000-02-11. Truncates at -32768-01-01 or 32767-12-31, 0001-00-x will
        yield the normalized value of -0001-12-x

        This may be necessary after Date ctors or if the SetDate(), SetDay(),
        SetMonth(), SetYear() methods set individual non-matching values.
        Adding/subtracting to/from dates never produces invalid dates.
     */
    void            Normalize();

    bool            IsBetween( const Date& rFrom, const Date& rTo ) const
                        { return ((mnDate >= rFrom.mnDate) &&
                                 (mnDate <= rTo.mnDate)); }

    bool            operator ==( const Date& rDate ) const
                        { return (mnDate == rDate.mnDate); }
    bool            operator !=( const Date& rDate ) const
                        { return (mnDate != rDate.mnDate); }
    bool            operator  >( const Date& rDate ) const
                        { return (mnDate > rDate.mnDate); }
    bool            operator  <( const Date& rDate ) const
                        { return (mnDate < rDate.mnDate); }
    bool            operator >=( const Date& rDate ) const
                        { return (mnDate >= rDate.mnDate); }
    bool            operator <=( const Date& rDate ) const
                        { return (mnDate <= rDate.mnDate); }

    Date&           operator =( const Date& rDate )
                        { mnDate = rDate.mnDate; return *this; }
    Date&           operator =( const css::util::Date& rUDate )
                        { setDateFromDMY( rUDate.Day, rUDate.Month, rUDate.Year); return *this; }
    Date&           operator ++();
    Date&           operator --();

    TOOLS_DLLPUBLIC friend Date      operator +( const Date& rDate, sal_Int32 nDays );
    TOOLS_DLLPUBLIC friend Date      operator -( const Date& rDate, sal_Int32 nDays );
    TOOLS_DLLPUBLIC friend sal_Int32 operator -( const Date& rDate1, const Date& rDate2 );

    /** Obtain number of days in a month of a year.

        Internally sanitizes nMonth to values 1 <= nMonth <= 12, does not
        normalize values.
     */
    static sal_uInt16 GetDaysInMonth( sal_uInt16 nMonth, sal_Int16 nYear );

    /// Internally normalizes values.
    static sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear );
    /// Semantically identical to IsValidDate() member method.
    static bool IsValidDate( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear );
    /// Semantically identical to IsEndOfMonth() member method.
    static bool IsEndOfMonth(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear);
    /// Semantically identical to Normalize() member method.
    static bool Normalize( sal_uInt16 & rDay, sal_uInt16 & rMonth, sal_Int16 & rYear );

 private:
    /// An accelerated form of DateToDays on this date
    sal_Int32 GetAsNormalizedDays() const;
};

TOOLS_DLLPUBLIC std::ostream& operator<<(std::ostream& os, const Date& rDate);

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */