Mar
4
花钱如流水 v0.0.1
By Felix021 @ 2010-03-04
http://www.felix021.com
一个PHP+MySQL的简易支出记录系统,提供一些最基本功能,界面简单,方便手机使用。
安装:随便找一个数据库,导入tbl.sql,然后修改config.php里的相关参数即可使用。
(界面很难看,代码很难看,原则:够用就好。)
下载文件 (已下载 1595 次)
By Felix021 @ 2010-03-04
http://www.felix021.com
一个PHP+MySQL的简易支出记录系统,提供一些最基本功能,界面简单,方便手机使用。
安装:随便找一个数据库,导入tbl.sql,然后修改config.php里的相关参数即可使用。
(界面很难看,代码很难看,原则:够用就好。)
下载文件 (已下载 1595 次)
Jan
24
参考这里的教程写的: http://www.developer.com/article.php/3417381
同时也终于知道了stmt原来是Statement的简写,惭愧。。。
同时也终于知道了stmt原来是Statement的简写,惭愧。。。
import java.sql.*;
public class Jdbc11 {
public static void main (String args[]) {
try {
Class.forName("com.mysql.jdbc.Driver");
Statement stmt = null;
String url = "jdbc:mysql://localhost:3306/test";
String dbuser = "root";
String dbpass = "123456";
String dbname = "felix021";
String tblname = "users";
Connection con = DriverManager.getConnection(url, dbuser, dbpass);
stmt = con.createStatement();
System.out.println("URL: " + url);
System.out.println("Connection: " + con);
//建库
stmt.executeUpdate("CREATE DATABASE IF NOT EXISTS " + dbname);
stmt.executeUpdate("USE " + dbname);
//建表
stmt.executeUpdate("DROP TABLE IF EXISTS " + tblname);
stmt.executeUpdate(
"CREATE TABLE " + tblname + "(\n" +
" `id` INT PRIMARY KEY AUTO_INCREMENT, \n" +
" `name` CHAR(20) NOT NULL, \n" +
" `description` varchar(255) DEFAULT NULL\n" +
")"
);
//插入
int count = stmt.executeUpdate(
"INSERT INTO " + tblname + "\n" +
"(`id`, `name`, `description`) VALUES \n" +
"(NULL, 'a', 'ooxx'), \n" +
"(NULL, 'b', NULL), \n" +
"(NULL, 'c', 'haha'), \n" +
"(NULL, 'd', 'hoho')"
);
System.out.println("Inserted " + count + " rows");
//statement for resultset
stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
//查询
ResultSet rs = stmt.executeQuery("SELECT * FROM " + tblname);
System.out.println("All results are listed below:");
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String description = rs.getString("description");
System.out.println(
"id=" + id + ", " +
"the name is " + name + ", " +
description
);
}
//删表
stmt.executeUpdate("DROP TABLE " + tblname);
//删库
stmt.executeUpdate("DROP DATABASE " + dbname);
}
catch (SQLException sqlE) {
System.out.println("SQL Error: " + sqlE);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
public class Jdbc11 {
public static void main (String args[]) {
try {
Class.forName("com.mysql.jdbc.Driver");
Statement stmt = null;
String url = "jdbc:mysql://localhost:3306/test";
String dbuser = "root";
String dbpass = "123456";
String dbname = "felix021";
String tblname = "users";
Connection con = DriverManager.getConnection(url, dbuser, dbpass);
stmt = con.createStatement();
System.out.println("URL: " + url);
System.out.println("Connection: " + con);
//建库
stmt.executeUpdate("CREATE DATABASE IF NOT EXISTS " + dbname);
stmt.executeUpdate("USE " + dbname);
//建表
stmt.executeUpdate("DROP TABLE IF EXISTS " + tblname);
stmt.executeUpdate(
"CREATE TABLE " + tblname + "(\n" +
" `id` INT PRIMARY KEY AUTO_INCREMENT, \n" +
" `name` CHAR(20) NOT NULL, \n" +
" `description` varchar(255) DEFAULT NULL\n" +
")"
);
//插入
int count = stmt.executeUpdate(
"INSERT INTO " + tblname + "\n" +
"(`id`, `name`, `description`) VALUES \n" +
"(NULL, 'a', 'ooxx'), \n" +
"(NULL, 'b', NULL), \n" +
"(NULL, 'c', 'haha'), \n" +
"(NULL, 'd', 'hoho')"
);
System.out.println("Inserted " + count + " rows");
//statement for resultset
stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
//查询
ResultSet rs = stmt.executeQuery("SELECT * FROM " + tblname);
System.out.println("All results are listed below:");
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String description = rs.getString("description");
System.out.println(
"id=" + id + ", " +
"the name is " + name + ", " +
description
);
}
//删表
stmt.executeUpdate("DROP TABLE " + tblname);
//删库
stmt.executeUpdate("DROP DATABASE " + dbname);
}
catch (SQLException sqlE) {
System.out.println("SQL Error: " + sqlE);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Jan
18
今天windy在群里提出一个问题: 为什么下面这段代码输出了why?
因为已经有结果了,反推过去,很容易就会想到,php把字符串当作数组来用了。再通过
由于此结论仅为猜测,想进一步证实,但是对php代码不熟悉,翻了zend_hash.c等文件都没找到北,因此请教了雪候鸟大人,得知真正的处理代码位于
@ php-5.2.8/Zend/zend_execute.c +1026
具体实现的代码是
简单写了一点注释,只能大致看懂逻辑,其他边边角角的,暂时就没时间看了:'(
$arr = 'windy';
if (isset($arr['why'])) {
echo 'why';
}
if (isset($arr['why'])) {
echo 'why';
}
因为已经有结果了,反推过去,很容易就会想到,php把字符串当作数组来用了。再通过
echo $arr['why'];
验证一下,发现输出的是w, 也就是$arr{0}的值,可以大致得出一个结论:当把字符串当作数组使用的时候,会自动把索引转换成整形,然后再当作字符串的偏移量读取返回。由于此结论仅为猜测,想进一步证实,但是对php代码不熟悉,翻了zend_hash.c等文件都没找到北,因此请教了雪候鸟大人,得知真正的处理代码位于
@ php-5.2.8/Zend/zend_execute.c +1026
static void zend_fetch_dimension_address(temp_variable *result, zval **container_ptr, zval *dim, int dim_is_tmp_var, int type TSRMLS_DC)
这个函数的作用是:将container_ptr所指向的container这个容器中,索引为dim的值取出放在result所指定的内存中去。具体实现的代码是
1063 switch (Z_TYPE_P(container)) {
1064 zval **retval;
....
1099 case IS_STRING: { //如果这个容器是string
1100 zval tmp;
1101
1102 if (dim == NULL) {
1103 zend_error_noreturn(E_ERROR, "[] operator not supported for strings");
1104 }
1105
1106 if (Z_TYPE_P(dim) != IS_LONG) { //如果偏移量的类型不是LONG
1107 switch(Z_TYPE_P(dim)) {
1108 /* case IS_LONG: */
1109 case IS_STRING:
1110 case IS_DOUBLE:
1111 case IS_NULL:
1112 case IS_BOOL:
1113 /* do nothing */ //允许STRING, DOUBLE, NULL, BOOL四种类型
1114 break;
1115 default: //其他类型都报错
1116 zend_error(E_WARNING, "Illegal offset type");
1117 break;
1118 }
1119
1120 tmp = *dim;
1121 zval_copy_ctor(&tmp);
1122 convert_to_long(&tmp);
1123 dim = &tmp; //将原来的dim转换成LONG
1124 }
1125 switch (type) {
1126 case BP_VAR_R:
1127 case BP_VAR_IS:
1128 case BP_VAR_UNSET:
1129 /* do nothing... */
1130 break;
1131 default:
1132 SEPARATE_ZVAL_IF_NOT_REF(container_ptr);
1133 break;
1134 }
1135 if (result) { //存放结果
1136 container = *container_ptr;
1137 result->str_offset.str = container;
1138 PZVAL_LOCK(container);
1139 result->str_offset.offset = Z_LVAL_P(dim);
1140 result->var.ptr_ptr = NULL;
1141 if (type == BP_VAR_R || type == BP_VAR_IS) {
1142 AI_USE_PTR(result->var);
1143 }
1144 }
1145 return;
1146 }
1147 break;
1064 zval **retval;
....
1099 case IS_STRING: { //如果这个容器是string
1100 zval tmp;
1101
1102 if (dim == NULL) {
1103 zend_error_noreturn(E_ERROR, "[] operator not supported for strings");
1104 }
1105
1106 if (Z_TYPE_P(dim) != IS_LONG) { //如果偏移量的类型不是LONG
1107 switch(Z_TYPE_P(dim)) {
1108 /* case IS_LONG: */
1109 case IS_STRING:
1110 case IS_DOUBLE:
1111 case IS_NULL:
1112 case IS_BOOL:
1113 /* do nothing */ //允许STRING, DOUBLE, NULL, BOOL四种类型
1114 break;
1115 default: //其他类型都报错
1116 zend_error(E_WARNING, "Illegal offset type");
1117 break;
1118 }
1119
1120 tmp = *dim;
1121 zval_copy_ctor(&tmp);
1122 convert_to_long(&tmp);
1123 dim = &tmp; //将原来的dim转换成LONG
1124 }
1125 switch (type) {
1126 case BP_VAR_R:
1127 case BP_VAR_IS:
1128 case BP_VAR_UNSET:
1129 /* do nothing... */
1130 break;
1131 default:
1132 SEPARATE_ZVAL_IF_NOT_REF(container_ptr);
1133 break;
1134 }
1135 if (result) { //存放结果
1136 container = *container_ptr;
1137 result->str_offset.str = container;
1138 PZVAL_LOCK(container);
1139 result->str_offset.offset = Z_LVAL_P(dim);
1140 result->var.ptr_ptr = NULL;
1141 if (type == BP_VAR_R || type == BP_VAR_IS) {
1142 AI_USE_PTR(result->var);
1143 }
1144 }
1145 return;
1146 }
1147 break;
简单写了一点注释,只能大致看懂逻辑,其他边边角角的,暂时就没时间看了:'(
Jan
18
@ php-5.2.8/Zend/zend_builtin_functions.c
1120 #ifdef ZEND_TEST_EXCEPTIONS
1121 ZEND_FUNCTION(crash)
1122 {
1123 char *nowhere=NULL;
1124
1125 memcpy(nowhere, "something", sizeof("something"));
1126 }
1127 #endif
1121 ZEND_FUNCTION(crash)
1122 {
1123 char *nowhere=NULL;
1124
1125 memcpy(nowhere, "something", sizeof("something"));
1126 }
1127 #endif
Jan
11
更多内容,参见laruence大牛的这篇: http://www.laruence.com/2009/07/23/994.html
这么短一段代码,有这么多考究的地方,很值得学习:
1. 5381
2. hash << 5 + hash --> hash * 33 (times 33算法)
3. -= 8
4. switch, break
...
这么短一段代码,有这么多考究的地方,很值得学习:
1. 5381
2. hash << 5 + hash --> hash * 33 (times 33算法)
3. -= 8
4. switch, break
...
static inline ulong zend_inline_hash_func(char *arKey, uint nKeyLength)
{
register ulong hash = 5381;
/* variant with the hash unrolled eight times */
for (; nKeyLength >= 8; nKeyLength -= 8) {
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
}
switch (nKeyLength) {
case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 1: hash = ((hash << 5) + hash) + *arKey++; break;
case 0: break;
EMPTY_SWITCH_DEFAULT_CASE()
}
return hash;
}
{
register ulong hash = 5381;
/* variant with the hash unrolled eight times */
for (; nKeyLength >= 8; nKeyLength -= 8) {
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
}
switch (nKeyLength) {
case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 1: hash = ((hash << 5) + hash) + *arKey++; break;
case 0: break;
EMPTY_SWITCH_DEFAULT_CASE()
}
return hash;
}
Jan
4
在发现有atoi这个函数之后的一段时间里(converts a string to an integer, cppreference.com),每当我需要把integer转换成字符串的时候,我就会想当然地写上itoa,直到编译器告诉我,这个函数不存在。反复几次以后,我终于记住了没有itoa,记住了应该用sprintf(现在想想,是应该用snprintf才对)。直到今天我才意识到自己从来就没有仔细想过,为什么有atoi,却没有itoa。也许还是自己求知欲太低了。
原因其实很简单,简单地说,就是因为不好处理返回值。atoi, atof, atol等函数之所以可以在C标准库中存在,可以列出来的有两个必要条件:一,需求使然;二,返回值类型是基本类型,可以使用匿名变量传递。itoa之所以不在C标准库中,我觉得并不是没有需求,而是因为返回值是字符串,也就是char数组,处理起来比较纠结。
在C里面,字符串始终是个硬伤。如果非要设计这么一个函数,有三种可选的办法:
一,使用一个static的char数组(貌似printf就是这么做的吧)。这有2个问题,1是数组的长度是固定的(当然,简单处理的话可以认为int最多就那么几位,只要初始化某个足够的长度肯定就OK),2是每次调用会覆盖上次的结果,特别地,会导致非线程安全。
二,每次调用,在函数内部malloc一段空间,写入,返回该空间的首地址。存在问题是,分配的空间函数内无法自行释放,需要调用者安排一个free,而这个free是非常容易被忽略的,这就存在了潜在的内存泄漏风险。
三,要求传入一个分配好空间的指针,函数将转换结果写入。这样实际上就是sprintf的做法,重复造轮子。而且还要考虑内存越界访问的问题,再增加一个参数n,然后这就是snprintf做的事情....
综合起来的结果就是:太麻烦了,不实现。
最后,推荐云风的一篇文章,这里探讨了与以上内容比较相关的”一个 C 接口设计的问题“
http://blog.codingnow.com/2009/01/c_interface.html
原因其实很简单,简单地说,就是因为不好处理返回值。atoi, atof, atol等函数之所以可以在C标准库中存在,可以列出来的有两个必要条件:一,需求使然;二,返回值类型是基本类型,可以使用匿名变量传递。itoa之所以不在C标准库中,我觉得并不是没有需求,而是因为返回值是字符串,也就是char数组,处理起来比较纠结。
在C里面,字符串始终是个硬伤。如果非要设计这么一个函数,有三种可选的办法:
一,使用一个static的char数组(貌似printf就是这么做的吧)。这有2个问题,1是数组的长度是固定的(当然,简单处理的话可以认为int最多就那么几位,只要初始化某个足够的长度肯定就OK),2是每次调用会覆盖上次的结果,特别地,会导致非线程安全。
二,每次调用,在函数内部malloc一段空间,写入,返回该空间的首地址。存在问题是,分配的空间函数内无法自行释放,需要调用者安排一个free,而这个free是非常容易被忽略的,这就存在了潜在的内存泄漏风险。
三,要求传入一个分配好空间的指针,函数将转换结果写入。这样实际上就是sprintf的做法,重复造轮子。而且还要考虑内存越界访问的问题,再增加一个参数n,然后这就是snprintf做的事情....
综合起来的结果就是:太麻烦了,不实现。
最后,推荐云风的一篇文章,这里探讨了与以上内容比较相关的”一个 C 接口设计的问题“
http://blog.codingnow.com/2009/01/c_interface.html
Dec
28
注:本文只是简单介绍这三个东西并对比一下其功能、差异,不讨论这些东西是否有存在的必要以及优劣。
· goto
· setjmp, longjmp
· try-catch
一、看看基本的使用
1. goto
这个比较简单,比较容易理解,只要设置一个行标就行了:
例子:
2. setjmp, longjmp
goto用起来是简单,但是存在一个先天缺陷:只能在同一个函数内使用。
有时候我们写一个代码,有两三层的函数嵌套,想要返回的时候就比较囧。
这时候用setjmp和longjmp就很happy。
例子:
输出是:
Hi, I'm foo!
Hi, I'm bar!
i = 1
3. try-catch (在这里算是歪用了,呵呵)
这个是c++提供的语言特性,可以用于捕获throw语句抛出的异常,不仅可以在函数内使用,也可以跨函数~~
例子:
输出是:
Hi, I'm foo!
Hi, I'm bar!
i = 1
二、简单对比一下:
· goto,使用简单方便,看起来比其他两个更容易一点(有行标),但是只能在函数内跳转;
· setjmp和longjmp,稍微麻烦点,需要带个参数(jmp_buf,全局变量,或者传参),好处是可以跨函数跳转,且可以根据setjmp的返回值得知从何处跳转。还有一个小缺陷就是,因为需要先执行setjmp以后才可以跳转,所以可以跳转的地方有一定限制,也使得代码看起来有点不够清晰。
· try-catch,C++有C没有,看起来结构比较清晰,不需要带额外的参数,抛出不同的值(和类型)方便判断来源。
三、重点考察一个问题:
如果有一个class,比如
所以在使用这三个东西的时候,一定要特别注意,建议在C++里头不使用setjmp,以免查错无门...
· goto
· setjmp, longjmp
· try-catch
一、看看基本的使用
1. goto
这个比较简单,比较容易理解,只要设置一个行标就行了:
例子:
int main ()
{
int i = 0, sum;
for (i = 0; i < 100; ++i) {
sum += i;
if (sum > 1000) goto JMP1;
}
JMP1:
printf("%d\n", i);
return 0;
}
{
int i = 0, sum;
for (i = 0; i < 100; ++i) {
sum += i;
if (sum > 1000) goto JMP1;
}
JMP1:
printf("%d\n", i);
return 0;
}
2. setjmp, longjmp
goto用起来是简单,但是存在一个先天缺陷:只能在同一个函数内使用。
有时候我们写一个代码,有两三层的函数嵌套,想要返回的时候就比较囧。
这时候用setjmp和longjmp就很happy。
例子:
#include <setjmp.h>
jmp_buf jmpbuf1;
void bar() {
printf("Hi, I'm bar!\n");
longjmp(jmpbuf1, 1);
}
void foo() {
printf("Hi, I'm foo!\n");
bar();
printf("Should never come here\n");
}
int main ()
{
int i = 0;
i = setjmp(jmpbuf1);
if (i == 0) { //setjmp第一次某个jmp_buf的时候返回0
foo();
}
else { //否则返回longjmp给出的值
printf("i = %d\n", i);
}
return 0;
}
jmp_buf jmpbuf1;
void bar() {
printf("Hi, I'm bar!\n");
longjmp(jmpbuf1, 1);
}
void foo() {
printf("Hi, I'm foo!\n");
bar();
printf("Should never come here\n");
}
int main ()
{
int i = 0;
i = setjmp(jmpbuf1);
if (i == 0) { //setjmp第一次某个jmp_buf的时候返回0
foo();
}
else { //否则返回longjmp给出的值
printf("i = %d\n", i);
}
return 0;
}
输出是:
Hi, I'm foo!
Hi, I'm bar!
i = 1
3. try-catch (在这里算是歪用了,呵呵)
这个是c++提供的语言特性,可以用于捕获throw语句抛出的异常,不仅可以在函数内使用,也可以跨函数~~
例子:
void bar() {
printf("Hi, I'm bar!\n");
throw 1;
}
void foo() {
printf("Hi, I'm foo!\n");
bar();
printf("Should never come here\n");
}
int main ()
{
try{
foo();
}
catch(int i) {
printf("i = %d\n", i);
}
return 0;
}
printf("Hi, I'm bar!\n");
throw 1;
}
void foo() {
printf("Hi, I'm foo!\n");
bar();
printf("Should never come here\n");
}
int main ()
{
try{
foo();
}
catch(int i) {
printf("i = %d\n", i);
}
return 0;
}
输出是:
Hi, I'm foo!
Hi, I'm bar!
i = 1
二、简单对比一下:
· goto,使用简单方便,看起来比其他两个更容易一点(有行标),但是只能在函数内跳转;
· setjmp和longjmp,稍微麻烦点,需要带个参数(jmp_buf,全局变量,或者传参),好处是可以跨函数跳转,且可以根据setjmp的返回值得知从何处跳转。还有一个小缺陷就是,因为需要先执行setjmp以后才可以跳转,所以可以跳转的地方有一定限制,也使得代码看起来有点不够清晰。
· try-catch,C++有C没有,看起来结构比较清晰,不需要带额外的参数,抛出不同的值(和类型)方便判断来源。
三、重点考察一个问题:
如果有一个class,比如
class T
{
public:
~T() { printf("I'm dead >_<\n"); }
};
那么使用goto、setjmp或try-catch的时候是否存在问题?{
public:
~T() { printf("I'm dead >_<\n"); }
};
if (1) {
T t1;
goto JMP1;
}
JMP1: ;
有输出I'm dead >_<T t1;
goto JMP1;
}
JMP1: ;
jmp_buf jmpbuf1;
if (setjmp(jmpbuf1) == 0) {
T t1;
longjmp(jmpbuf1, 1);
}
else {
;
}
无输出if (setjmp(jmpbuf1) == 0) {
T t1;
longjmp(jmpbuf1, 1);
}
else {
;
}
try {
T t1;
throw 1;
}
catch(int i) {
;
}
有输出I'm dead >_<T t1;
throw 1;
}
catch(int i) {
;
}
所以在使用这三个东西的时候,一定要特别注意,建议在C++里头不使用setjmp,以免查错无门...
Dec
26
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#define P(a, b) printf(#b ": %" #a "\n", b)
#define Ps(a, c, b) P(a, (c)->b)
#define alloc(name, type, n) type *name = (type *) malloc(sizeof(type) * (n))
#define allocs(name, type, n) alloc(name, struct type, (n))
int main ()
{
time_t t1 = time(NULL);
P(ld, t1);
struct timeval tv1;
gettimeofday(&tv1, NULL);
Ps(ld, &tv1, tv_sec);
Ps(ld, &tv1, tv_usec);
/*
* //t1 += 8 * 3600;
* struct tm *tm1 = gmtime(&t1); //标准时间
*/
struct tm *tm1 = localtime(&t1); //本地时间
Ps(d, tm1, tm_sec);
Ps(d, tm1, tm_min);
Ps(d, tm1, tm_hour);
Ps(d, tm1, tm_mday);
Ps(d, tm1, tm_mon+1); //0-based
Ps(d, tm1, tm_year+1900); //从1900年开始, 0-based
allocs(tm2, tm, 1);
tm2->tm_sec = 0;
tm2->tm_min = 0;
tm2->tm_hour= 0;
tm2->tm_mday= 1;
tm2->tm_mon = 0;
tm2->tm_year= 70;
tm2->tm_isdst = 0;
time_t t2 = mktime(tm2);
//t2 += 3600 * 8; //mktime是本地时间
P(ld, t2);
P(s, asctime(tm1)); //标准时间
P(s, ctime(&t1)); //本地时间
alloc(buf, char, 100);
strftime(buf, 100, "%Y-%m-%d %H:%M:%S", tm1); //标准时间
P(s, buf);
strftime(buf, 100, "%F %T", tm1); //标准时间, 格式和上面的一样
P(s, buf);
return 0;
}
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#define P(a, b) printf(#b ": %" #a "\n", b)
#define Ps(a, c, b) P(a, (c)->b)
#define alloc(name, type, n) type *name = (type *) malloc(sizeof(type) * (n))
#define allocs(name, type, n) alloc(name, struct type, (n))
int main ()
{
time_t t1 = time(NULL);
P(ld, t1);
struct timeval tv1;
gettimeofday(&tv1, NULL);
Ps(ld, &tv1, tv_sec);
Ps(ld, &tv1, tv_usec);
/*
* //t1 += 8 * 3600;
* struct tm *tm1 = gmtime(&t1); //标准时间
*/
struct tm *tm1 = localtime(&t1); //本地时间
Ps(d, tm1, tm_sec);
Ps(d, tm1, tm_min);
Ps(d, tm1, tm_hour);
Ps(d, tm1, tm_mday);
Ps(d, tm1, tm_mon+1); //0-based
Ps(d, tm1, tm_year+1900); //从1900年开始, 0-based
allocs(tm2, tm, 1);
tm2->tm_sec = 0;
tm2->tm_min = 0;
tm2->tm_hour= 0;
tm2->tm_mday= 1;
tm2->tm_mon = 0;
tm2->tm_year= 70;
tm2->tm_isdst = 0;
time_t t2 = mktime(tm2);
//t2 += 3600 * 8; //mktime是本地时间
P(ld, t2);
P(s, asctime(tm1)); //标准时间
P(s, ctime(&t1)); //本地时间
alloc(buf, char, 100);
strftime(buf, 100, "%Y-%m-%d %H:%M:%S", tm1); //标准时间
P(s, buf);
strftime(buf, 100, "%F %T", tm1); //标准时间, 格式和上面的一样
P(s, buf);
return 0;
}






