comparison libpurple/util.c @ 31123:c61d3e5ec1da

Fix purple_str_to_time(). Fixes #13131. committer: John Bailey <rekkanoryo@rekkanoryo.org>
author morshed.nader@gmail.com
date Sun, 09 Jan 2011 20:08:29 +0000
parents a8cc50c2279f
children 1e1b598c725e 73b005a20d06
comparison
equal deleted inserted replaced
31115:80db1ca54fb6 31123:c61d3e5ec1da
662 tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60; 662 tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
663 663
664 return mktime(&tm); 664 return mktime(&tm);
665 } 665 }
666 666
667 /* originally taken from GLib trunk 1-6-11 */
668 /* originally licensed as LGPL 2+ */
669 static time_t
670 mktime_utc(struct tm *tm)
671 {
672 time_t retval;
673
674 #ifndef HAVE_TIMEGM
675 static const gint days_before[] =
676 {
677 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
678 };
679 #endif
680
681 #ifndef HAVE_TIMEGM
682 if (tm->tm_mon < 0 || tm->tm_mon > 11)
683 return (time_t) -1;
684
685 retval = (tm->tm_year - 70) * 365;
686 retval += (tm->tm_year - 68) / 4;
687 retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
688
689 if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
690 retval -= 1;
691
692 retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
693 #else
694 retval = timegm (tm);
695 #endif /* !HAVE_TIMEGM */
696
697 return retval;
698 }
699
667 time_t 700 time_t
668 purple_str_to_time(const char *timestamp, gboolean utc, 701 purple_str_to_time(const char *timestamp, gboolean utc,
669 struct tm *tm, long *tz_off, const char **rest) 702 struct tm *tm, long *tz_off, const char **rest)
670 { 703 {
671 time_t retval = 0; 704 struct tm t;
672 static struct tm t; 705 const gchar *str;
673 const char *c = timestamp; 706 gint year = 0;
674 int year = 0;
675 long tzoff = PURPLE_NO_TZ_OFF; 707 long tzoff = PURPLE_NO_TZ_OFF;
676 708 time_t retval;
677 time(&retval); 709 gboolean mktime_with_utc = TRUE;
678 localtime_r(&retval, &t);
679 710
680 if (rest != NULL) 711 if (rest != NULL)
681 *rest = NULL; 712 *rest = NULL;
682 713
714 g_return_val_if_fail(timestamp != NULL, 0);
715
716 str = timestamp;
717
718 /* Strip leading whitespace */
719 while (g_ascii_isspace(*str))
720 str++;
721
722 if (*str == '\0') {
723 if (rest != NULL && *str != '\0')
724 *rest = str;
725
726 return 0;
727 }
728
729 if (!g_ascii_isdigit(*str) && *str != '-' && *str != '+') {
730 if (rest != NULL && *str != '\0')
731 *rest = str;
732
733 return 0;
734 }
735
683 /* 4 digit year */ 736 /* 4 digit year */
684 if (sscanf(c, "%04d", &year) && year > 1900) 737 if (sscanf(str, "%04d", &year) && year >= 1900) {
685 { 738 str += 4;
686 c += 4; 739
687 if (*c == '-') 740 if (*str == '-' || *str == '/')
688 c++; 741 str++;
742
689 t.tm_year = year - 1900; 743 t.tm_year = year - 1900;
690 } 744 }
691 745
692 /* 2 digit month */ 746 /* 2 digit month */
693 if (!sscanf(c, "%02d", &t.tm_mon)) 747 if (!sscanf(str, "%02d", &t.tm_mon)) {
694 { 748 if (rest != NULL && *str != '\0')
695 if (rest != NULL && *c != '\0') 749 *rest = str;
696 *rest = c; 750
697 return 0; 751 return 0;
698 } 752 }
699 c += 2; 753
700 if (*c == '-' || *c == '/') 754 str += 2;
701 c++;
702 t.tm_mon -= 1; 755 t.tm_mon -= 1;
703 756
757 if (*str == '-' || *str == '/')
758 str++;
759
704 /* 2 digit day */ 760 /* 2 digit day */
705 if (!sscanf(c, "%02d", &t.tm_mday)) 761 if (!sscanf(str, "%02d", &t.tm_mday)) {
706 { 762 if (rest != NULL && *str != '\0')
707 if (rest != NULL && *c != '\0') 763 *rest = str;
708 *rest = c; 764
709 return 0; 765 return 0;
710 } 766 }
711 c += 2; 767
712 if (*c == '/') 768 str += 2;
713 { 769
714 c++; 770 /* Grab the year off the end if there's still stuff */
715 771 if (*str == '/' || *str == '-') {
716 if (!sscanf(c, "%04d", &t.tm_year)) 772 /* But make sure we don't read the year twice */
773 if (year >= 1900) {
774 if (rest != NULL && *str != '\0')
775 *rest = str;
776
777 return 0;
778 }
779
780 str++;
781
782 if (!sscanf(str, "%04d", &t.tm_year)) {
783 if (rest != NULL && *str != '\0')
784 *rest = str;
785
786 return 0;
787 }
788
789 t.tm_year -= 1900;
790 } else if (*str == 'T' || *str == '.') {
791 str++;
792
793 /* Continue grabbing the hours/minutes/seconds */
794 if ((sscanf(str, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 &&
795 (str += 8)) ||
796 (sscanf(str, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 &&
797 (str += 6)))
717 { 798 {
718 if (rest != NULL && *c != '\0') 799 gint sign, tzhrs, tzmins;
719 *rest = c; 800
720 return 0; 801 if (*str == '.') {
721 } 802 /* Cut off those pesky micro-seconds */
722 t.tm_year -= 1900;
723 }
724 else if (*c == 'T' || *c == '.')
725 {
726 c++;
727 /* we have more than a date, keep going */
728
729 /* 2 digit hour */
730 if ((sscanf(c, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 && (c = c + 8)) ||
731 (sscanf(c, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 && (c = c + 6)))
732 {
733 gboolean offset_positive = FALSE;
734 int tzhrs;
735 int tzmins;
736
737 t.tm_isdst = -1;
738
739 if (*c == '.') {
740 do { 803 do {
741 c++; 804 str++;
742 } while (*c >= '0' && *c <= '9'); /* dealing with precision we don't care about */ 805 } while (*str >= '0' && *str <= '9');
743 } 806 }
744 if (*c == '+') 807
745 offset_positive = TRUE; 808 sign = (*str == '+') ? -1 : 1;
746 if (((*c == '+' || *c == '-') && (c = c + 1)) && 809
747 ((sscanf(c, "%02d:%02d", &tzhrs, &tzmins) == 2 && (c = c + 5)) || 810 /* Process the timezone */
748 (sscanf(c, "%02d%02d", &tzhrs, &tzmins) == 2 && (c = c + 4)))) 811 if (*str == '+' || *str == '-') {
749 { 812 str++;
750 tzoff = tzhrs*60*60 + tzmins*60; 813
751 if (offset_positive) 814 if (((sscanf(str, "%02d:%02d", &tzhrs, &tzmins) == 2 && (str += 5)) ||
752 tzoff *= -1; 815 (sscanf(str, "%02d%02d", &tzhrs, &tzmins) == 2 && (str += 4))))
816 {
817 tzoff = tzhrs * 60 * 60 + tzmins * 60;
818 tzoff *= sign;
819 } else {
820 if (rest != NULL && *str != '\0')
821 *rest = str;
822
823 return 0;
824 }
825 } else if (*str == 'Z') {
826 /* 'Z' = Zulu = UTC */
827 str++;
828 utc = TRUE;
829 } else if (!utc) {
830 /* Local Time */
831 t.tm_isdst = -1;
832 mktime_with_utc = FALSE;
753 } 833 }
754 else if ((*c == 'Z') && (c = c + 1)) 834
755 { 835 if (utc)
756 /* 'Z' = Zulu = UTC */
757 tzoff = 0; 836 tzoff = 0;
758 } 837 }
759 else if (utc) 838 }
760 { 839
761 static struct tm tmptm; 840 if (rest != NULL && *str != '\0') {
762 time_t tmp; 841 /* Strip trailing whitespace */
763 tmp = mktime(&t); 842 while (g_ascii_isspace(*str))
764 /* we care about whether it *was* dst, and the offset, here on this 843 str++;
765 * date, not whether we are currently observing dst locally *now*. 844
766 * This isn't perfect, because we would need to know in advance the 845 if (*str != '\0')
767 * offset we are trying to work out in advance to be sure this 846 *rest = str;
768 * works for times around dst transitions but it'll have to do. */ 847 }
769 localtime_r(&tmp, &tmptm); 848
770 t.tm_isdst = tmptm.tm_isdst; 849 if (mktime_with_utc)
771 #ifdef HAVE_TM_GMTOFF 850 retval = mktime_utc(&t);
772 t.tm_gmtoff = tmptm.tm_gmtoff; 851 else
773 #endif 852 retval = mktime(&t);
774 }
775
776 if (rest != NULL && *c != '\0')
777 {
778 if (*c == ' ')
779 c++;
780 if (*c != '\0')
781 *rest = c;
782 }
783
784 if (tzoff != PURPLE_NO_TZ_OFF || utc)
785 {
786 #if defined(_WIN32)
787 long sys_tzoff;
788 #endif
789
790 #if defined(_WIN32) || defined(HAVE_TM_GMTOFF) || defined (HAVE_TIMEZONE)
791 if (tzoff == PURPLE_NO_TZ_OFF)
792 tzoff = 0;
793 #endif
794
795 #ifdef _WIN32
796 if ((sys_tzoff = wpurple_get_tz_offset()) == -1)
797 tzoff = PURPLE_NO_TZ_OFF;
798 else
799 tzoff += sys_tzoff;
800 #else
801 #ifdef HAVE_TM_GMTOFF
802 tzoff += t.tm_gmtoff;
803 #else
804 # ifdef HAVE_TIMEZONE
805 tzset(); /* making sure */
806 tzoff -= timezone;
807 # endif
808 #endif
809 #endif /* _WIN32 */
810 }
811 }
812 else
813 {
814 if (rest != NULL && *c != '\0')
815 *rest = c;
816 }
817 }
818
819 retval = mktime(&t);
820 853
821 if (tm != NULL) 854 if (tm != NULL)
822 *tm = t; 855 *tm = t;
823 856
824 if (tzoff != PURPLE_NO_TZ_OFF) 857 if (tzoff != PURPLE_NO_TZ_OFF)