datetime.cpp
来自「Shorthand是一个强大的脚本语言」· C++ 代码 · 共 826 行 · 第 1/2 页
CPP
826 行
/////////////////////////////////////////////////////////////////////////////
// $Header: /shorthand/src/datetime.cpp 5 1/09/03 7:14p Arm $
//---------------------------------------------------------------------------
// This file is part of "libAndrix" library - a collection of classes
// and functions developed by Andrei Remenchuk.
//---------------------------------------------------------------------------
// While you may own complete copyright on the project with which you have
// received this file, the author reserves the right to use code contained
// in this very file for any purposes, including publishing and usage in
// any free or commercial software.
//
// You may re-distribute this file or re-use it in your own free or
// commercial software provided that this text is included in the file.
// If you change this file you must include clear notice stating that
// you changed this file and the date of change.
//
// This statement doesn't apply to other files that are part of the same
// package unless otherwise noted.
//---------------------------------------------------------------------------
// (c) 1998-2002 Andrei Remenchuk <andrei@remenchuk.com>
//---------------------------------------------------------------------------
// datetime.cpp - datetime object implementation
/////////////////////////////////////////////////////////////////////////////
#include "config.h"
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#ifdef SOLARIS
#define _POSIX_PTHREAD_SEMANTICS
#include <time.h>
#include <sys/time.h>
#endif
#include "datetime.h"
#include "cstring.h"
#include "regexx.h"
/*
* GREGORIAN_ROME is Julian Date Number (JDN) when conversion
* from Julian to Gregorian calendar took place - October 4, 1582.
* The day of October 4, 1582 was followed by October 15, 1582.
*
* This constant is necessary for dates arithmetic.
*/
#define GREGORIAN_ROME 2299160
/* Make sure C runtime library timezone variables are set */
static int tz_set_globals() { /*tzset();*/ return 0; }
static int tz_dummy = tz_set_globals();
/**
* Returns true if year is leap
*/
bool year_is_leap(int year) {
if (year <= 1600 && year%4==0) return true;
else if (year%4 == 0 && (year%100 != 0 || year%400 == 0)) return true;
else return false;
}
/**
* Returns number of leap years between 1 A.D. and specified year,
* not including specified year itself.
*/
static int leap_delta(int year) {
register int delta, y;
y = year-1;
delta = y/4;
if (y <= 1699) return delta;
else return delta - (y-1600)/100 + (y-1600)/400;
}
static const char* weekday_names[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char* month_names[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
// month sizes
const unsigned char month_sizes[12] = {31,28,31,30, 31, 30, 31, 31, 30, 31, 30, 31};
// zero-based year-day-numbers of first day of each month
const short month_deltas[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
/**
* Converts year/month/day to JDN (Julian Day Number) WITHOUT validation
* of year/month/day components. This function handles only A.D. days.
*/
unsigned int date2julian_relaxed(int year, int month, int day)
{
// 1721424 is a Julian Day Number of the first day of Common Era (1-Jan-1Y)
// this is optimized rule, unoptimized looks like this:
// int j = 1721424 + (year-1)*365 + month_delta[month-1] + day - 1;
int j = 1721058 + year*365 + month_deltas[month-1] + day;
if (year_is_leap(year) && month>2) j++;
j += leap_delta(year);
if (j>GREGORIAN_ROME) j -= 10;
return j;
}
/**
* Determines weekday of arbitrary date (0=Sunday,1=Monday,...).
*/
int determine_weekday(int year, int month, int day)
{
int k = day;
int m = month;
if (month <= 2) { year--; m+=10; } else m -= 2;
int Y = year%100;
int C = year/100;
int W = (k + ((int)floor(2.6*m - 0.2)) - 2*C + Y + Y/4 + C/4)%7;
if (W < 0) W = W + 7;
return W;
}
/**
* Converts year/month/day to JDN (Julian Day Number) WITH validation
* of year/month/day components. If components are invalid, exception
* is thrown.
* This function is optimized to handle only A.D. dates.
*/
unsigned int date2julian_strict(int year, int month, int day)
{
if (year < 1600)
throw new ShhObjectException(4101, "Invalid year in date operation: %d", year);
if (month < 1 || month > 12)
throw new ShhObjectException(4102, "Invalid month in date operation: %d", month);
if (month == 2) {
if (!year_is_leap(year) && day>28) throw new ShhObjectException(4103, "Invalid day number in date operation: %d", day);
} else
if (day > month_sizes[month-1]) throw new ShhObjectException(4103, "Invalid day number in date operation: %d", day);
return date2julian_relaxed(year, month, day);
}
/**
* Returns JDN of first day of the year
*/
int get_year_start(int year)
{
return date2julian_relaxed(year, 1, 1);
}
/**
* Converts JDN (Julian Day Number) to year/month/day
* Basic validation is performed and if JDN is zero is not in valid range,
* output variables are not modified and false is returned.
* If conversion is successfull, return value is true
*/
bool julian2date_relaxed(unsigned long jdn, int* year, int* month, int* day)
{
// B.C. dates are returned as January 1st, Year 1.
if (jdn <= 1721424) return false;
int yday;
int ystart_year, ystart_month, ystart_day;
if (jdn == 0) return false;
// this is approximate estimation of year to speed further calculations up.
// it's start should be less than `jdn' argument
ystart_year = jdn/365 - 4713 - jdn/532900;
if (ystart_year == 0) return false;
ystart_month = ystart_day = 1;
unsigned int julian_ystart = date2julian_relaxed( ystart_year, ystart_month, ystart_day );
if (julian_ystart < 0)
{
return false;
}
//XASSERT( julian_ystart <= jdn );
// hop to the nearest julian year start,
while( (yday = jdn-julian_ystart+1) > 365 )
{
if (yday == 366 && year_is_leap(ystart_year)) break;
ystart_year++;
julian_ystart = date2julian_relaxed( ystart_year, ystart_month, ystart_day );
}
*year = ystart_year;
bool leap = year_is_leap(*year);
// yday #60 is 29-Feb in leap year and 1-Mar is regular year
// For days after 29-Feb in leap years, we decrease yday,
// because in leap year days after 29Feb have greater numbers
if (yday == 60 && leap)
{
*month = 2; *day = 29;
}
else
{
if (yday > 60 && leap) yday--; // SVCH
*month = yday / 32;
while (yday - month_deltas[*month] > month_sizes[*month]) (*month)++;
//XASSERT(*month>=0 && *month<=11);
//DEBUGF(1, "yday=%d month=%d month_deltas[month]=%d\n", yday, *month, month_deltas[*month]);
*day = yday - month_deltas[*month];
(*month)++;
}
return true;
}
/**
* Converts JDN (Julian Day Number) to year/month/day
* Throws exception when date is invalid.
*/
void julian2date_strict(unsigned long j, int* year, int* month, int* day)
{
if (! julian2date_relaxed(j, year, month, day))
{
throw new ShhObjectException(4105, "Invalid integer-to-date conversion: cannot derive date from number %u", j);
}
}
/**
* Portable method for determining local timezone. On Windows, this doesn't rely
* on any environment variables and takes information from more reliable
* Win32 API functions.
* On Unix, it relies on global timezone variables.
*
* The return value is offset of the local timezone from GMT/UTC in minutes,
* including any daylight adjustments.
*/
int portable_zone_offset()
{
#ifdef WIN32
TIME_ZONE_INFORMATION local_zone;
memset(&local_zone, 0, sizeof(local_zone));
DWORD mode = GetTimeZoneInformation(&local_zone);
if (mode == TIME_ZONE_ID_DAYLIGHT)
{
local_zone.Bias += local_zone.DaylightBias;
}
return local_zone.Bias;
#else
#ifdef SOLARIS
time_t clock = time(NULL);
struct tm local_stm;
localtime_r(&clock, &local_stm);
int bias;
if (local_stm.tm_isdst > 0)
bias = altzone / 60;
else
bias = timezone / 60;
return bias;
/*
struct tm greenwich_stm;
gmtime_r (&clock, &greenwich_stm);
int bias = mktime(&greenwich_stm) - clock;
bias /= 60;
if (greenwich_stm.tm_isdst > 0) bias += 60;
return bias;*/
#else // !SOLARIS
time_t clock = time(NULL);
struct tm local_stm; localtime_r(&clock, &local_stm);
//struct tm universal_stm; gmtime_r(&clock, &universal_stm);
//int bias = mktime(&universal_stm) - mktime(&local_stm);
int bias = -local_stm.tm_gmtoff;
bias /= 60;
return bias;
#endif // !SOLARIS
#endif // !WIN32
}
datetime::datetime()
{
local();
}
datetime::datetime(const SYSTEMTIME* t)
{
memcpy(&m_time, t, sizeof(SYSTEMTIME));
}
datetime::datetime(const datetime& other)
{
memcpy(&m_time, &other.m_time, sizeof(SYSTEMTIME));
}
datetime::~datetime()
{
}
void datetime::import_stm(const struct tm* stm)
{
m_time.wYear = stm->tm_year + 1900;
m_time.wMonth = stm->tm_mon + 1;
m_time.wDay = stm->tm_mday;
m_time.wHour = stm->tm_hour;
m_time.wMinute = stm->tm_min;
m_time.wSecond = stm->tm_sec;
m_time.wMilliseconds = 0;
}
void datetime::export_stm(struct tm* stm) const
{
stm->tm_year = m_time.wYear - 1900;
stm->tm_mon = m_time.wMonth - 1;
stm->tm_mday = m_time.wDay;
stm->tm_hour = m_time.wHour;
stm->tm_min = m_time.wMinute;
stm->tm_sec = m_time.wSecond;
}
void datetime::local()
{
#ifdef WIN32
GetLocalTime(&m_time);
#else
time_t t = time(NULL);
struct tm stm;
memcpy(&stm, localtime(&t), sizeof(struct tm));
import_stm(&stm);
#endif
}
// normalizes time
bool datetime::normalize()
{
#ifdef WIN32
FILETIME ft;
return SystemTimeToFileTime(&m_time, &ft) &&
FileTimeToSystemTime(&ft, &m_time);
#else
return true;
#endif
}
// returns true if time is not zero
bool datetime::is_valid() const
{
if (m_time.wYear == 0) return false;
if (m_time.wMonth == 0) return false;
if (m_time.wDay == 0) return false;
return true;
}
// exports value to "YYYY-MM-DD HH:MM:SS" format
void datetime::export_common(string& s) const
{
s.printf("%04d-%02d-%02d %02d:%02d:%02d",
m_time.wYear, m_time.wMonth, m_time.wDay,
m_time.wHour, m_time.wMinute, m_time.wSecond);
}
// imports value from "YYYY-MM-DD HH:MM:SS" format
bool datetime::import(const char* s)
{
memset(&m_time, 0, sizeof(SYSTEMTIME));
int n = string::split(s, "i-i-ii:i:i",
&m_time.wYear, &m_time.wMonth, &m_time.wDay,
&m_time.wHour, &m_time.wMinute, &m_time.wSecond
);
if (n < 5) return false;
return normalize();
}
/**
* Imports value from RFC-822 "e-mail" format which is
* "Wed, 24 Jul 2002 22:04:50 -0700". This format is also used in HTTP headers, cookies
* and a number of other places.
* Some reasoable deviations from the standard are allowed (like two-digit year,
* omitted time, etc).
*/
bool datetime::import_rfc(const char* s)
{
string weekday, month, zone, year, day, hour, min, sec;
int offset = 0;
RX date_rx("([a-z]+, *)?([0-9]+)[ :-]+([a-z]+)[ :-]+([0-9]+) +([0-9]+):([0-9]+):([0-9]+) +(.*)$");
if (!date_rx.match(s)) return false;
date_rx.submatch(2, day);
date_rx.submatch(3, month);
date_rx.submatch(4, year);
date_rx.submatch(5, hour);
date_rx.submatch(6, min);
date_rx.submatch(7, sec);
date_rx.submatch(8, zone);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?