iOS: Convertir UTC NSDate a zona horaria local

138

¿Cómo convierto un UTC NSDatea la zona horaria local NSDate en Objective C y / y Swift?

Krunal
fuente
14
Las fechas ciertamente tienen zonas horarias.
Glenn Maynard
1
Si ayuda, piense en las temperaturas. Se pueden expresar en Fahrenheit, Celsius o Kelvin. Pero la información que se expresa (el movimiento promedio de las moléculas) no tiene una unidad intrínseca, aunque solo es significativa para nosotros cuando se expresa en alguna unidad.
software evolucionó el
77
@DaveDeLong NSDate tiene una zona horaria. De la referencia de clase NSDate: "Este método devuelve un valor de tiempo relativo a una fecha de referencia absoluta, el primer instante del 1 de enero de 2001, GMT". Tenga en cuenta la referencia clara y específica a GMT.
Murray Sagal
3
Estoy en desacuerdo. NSDate NO tiene una zona horaria. Para especificar la zona horaria para NSDate, use un objeto NSCalendar o un objeto NSDateFormatter. Si crea un NSDate a partir de una cadena que no tiene una zona horaria especificada, entonces el NSDate asumirá que la cadena está en hora GMT.
Rickster
1
@MurraySagal El hecho de que ese método en particular devuelva un valor de tiempo relativo a una fecha en una zona horaria específica no significa que NSDate modele una fecha como relativa a una zona horaria.
eremzeit

Respuestas:

139
NSTimeInterval seconds; // assume this exists
NSDate* ts_utc = [NSDate dateWithTimeIntervalSince1970:seconds];

NSDateFormatter* df_utc = [[[NSDateFormatter alloc] init] autorelease];
[df_utc setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
[df_utc setDateFormat:@"yyyy.MM.dd G 'at' HH:mm:ss zzz"];

NSDateFormatter* df_local = [[[NSDateFormatter alloc] init] autorelease];
[df_local setTimeZone:[NSTimeZone timeZoneWithName:@"EST"]];
[df_local setDateFormat:@"yyyy.MM.dd G 'at' HH:mm:ss zzz"];

NSString* ts_utc_string = [df_utc stringFromDate:ts_utc];
NSString* ts_local_string = [df_local stringFromDate:ts_utc];

// you can also use NSDateFormatter dateFromString to go the opposite way

Tabla de parámetros de cadena de formato:

https://waracle.com/iphone-nsdateformatter-date-formatting-table/

Si el rendimiento es una prioridad, puede considerar usar strftime

https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/strftime.3.html

slf
fuente
Probablemente valga la pena mencionar que también puede usar el formateador para leer las fechas desde las cadenas
slf
34
@DaveDeLong está muy bien si solo está mostrando la fecha como una cadena. Pero hay razones perfectamente válidas para realizar conversiones de zona horaria en una fecha. Por ejemplo, si desea predeterminar la fecha en un UIDatePicker usando setDate :. Las fechas devueltas por los servicios web a menudo son UTC, pero representan un evento en la zona horaria local del usuario, como una lista de TV. Pasar una fecha no convertida mostrará la hora incorrecta en el selector.
Christopher Pickslay
55
@GlennMaynard No estoy de acuerdo. La esencia de esta respuesta es que no NSDatees necesaria la conversión al objeto, lo cual es correcto. La conversión a una zona horaria se produce cuando se formatea la fecha, no cuando se crea, porque las fechas no tienen zonas horarias.
Dave DeLong
1
@GlennMaynard ... excepto que NSCalendarDateestá en desuso.
Dave DeLong
1
También tenga en cuenta esto: oleb.net/blog/2011/11/… donde dice "GMT! = UTC"
huggie
106

EDITAR Cuando escribí esto, no sabía que debería usar un formateador de fecha que probablemente sea un mejor enfoque, así que revise slfla respuesta también.

Tengo un servicio web que devuelve fechas en UTC. Lo uso toLocalTimepara convertirlo a la hora local y toGlobalTimepara volver a convertirlo si es necesario.

Aquí es de donde obtuve mi respuesta:

https://agilewarrior.wordpress.com/2012/06/27/how-to-convert-nsdate-to-different-time-zones/

@implementation NSDate(Utils)

-(NSDate *) toLocalTime
{
  NSTimeZone *tz = [NSTimeZone defaultTimeZone];
  NSInteger seconds = [tz secondsFromGMTForDate: self];
  return [NSDate dateWithTimeInterval: seconds sinceDate: self];
}

-(NSDate *) toGlobalTime
{
  NSTimeZone *tz = [NSTimeZone defaultTimeZone];
  NSInteger seconds = -[tz secondsFromGMTForDate: self];
  return [NSDate dateWithTimeInterval: seconds sinceDate: self];
}

@end
gyozo kudor
fuente
25
No hagas esto. Las NSDates siempre están en UTC. Esto simplemente confunde el problema.
JeremyP
13
Esto puede ser muy útil para el caso del "servicio web" mencionado anteriormente. Supongamos que tiene un servidor que almacena eventos en UTC y el cliente desea solicitar todos los eventos que ocurrieron hoy. Para hacer esto, el cliente necesita obtener la fecha actual (UTC / GMT) y luego cambiarla por su desplazamiento de zona horaria antes de enviarla al servidor.
Taylor Lafrinere
@JeremyP Sería más exacto decir que los NSDates siempre están en GMT. De la referencia de clase NSDate: "Este método devuelve un valor de tiempo relativo a una fecha de referencia absoluta, el primer instante del 1 de enero de 2001, GMT". Tenga en cuenta la referencia clara y específica a GMT. Existe una diferencia técnica entre GMT y UTC, pero eso es en gran medida irrelevante para las soluciones que la mayoría de la gente está buscando.
Murray Sagal
3
Sería bueno tener en cuenta dónde copió el código de: agilewarrior.wordpress.com/2012/06/27/…
aryaxt
2
@aryaxt tienes razón, lo siento. Sinceramente, no recordaba de dónde copié cuando publiqué la respuesta.
gyozo kudor
49

El método más fácil que he encontrado es este:

NSDate *someDateInUTC = …;
NSTimeInterval timeZoneSeconds = [[NSTimeZone localTimeZone] secondsFromGMT];
NSDate *dateInLocalTimezone = [someDateInUTC dateByAddingTimeInterval:timeZoneSeconds];
Sendoa
fuente
3
Esta respuesta se siente más portátil. La respuesta a continuación supone que la zona horaria se fija en tiempo de ejecución, mientras que la respuesta anterior deriva la zona horaria de la plataforma.
bleeckerj
9
Muy útil. Una adición, secondsFromGMTForDatedebe usarse si desea contabilizar el horario de verano. Ver Documentos de Apple
Sergey Markelov
1
Esto no tiene en cuenta los cambios de horario de verano.
lkraider
36

Swift 3+ : UTC a Local y Local a UTC

extension Date {

    // Convert UTC (or GMT) to local time
    func toLocalTime() -> Date {
        let timezone = TimeZone.current
        let seconds = TimeInterval(timezone.secondsFromGMT(for: self))
        return Date(timeInterval: seconds, since: self)
    }

    // Convert local time to UTC (or GMT)
    func toGlobalTime() -> Date {
        let timezone = TimeZone.current
        let seconds = -TimeInterval(timezone.secondsFromGMT(for: self))
        return Date(timeInterval: seconds, since: self)
    }
}
Krunal
fuente
¿Convertirá cualquier zona horaria a UTC o viceversa?
Mitesh
26

Si quieres fecha y hora local. Prueba este código: -

NSString *localDate = [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterMediumStyle];
Mohd Iftekhar Qurashi
fuente
¡Gran respuesta! Esto tomará la fecha actual. Una adaptación de esto que usa una cadena de fecha sería reemplazar [NSDate date]con [NSDate dateWithNaturalLanguageString:sMyDateString].
Volomike
7

Convierta su fecha UTC a fecha local

-(NSString *)getLocalDateTimeFromUTC:(NSString *)strDate
{
    NSDateFormatter *dtFormat = [[NSDateFormatter alloc] init];
    [dtFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    [dtFormat setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    NSDate *aDate = [dtFormat dateFromString:strDate];

    [dtFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    [dtFormat setTimeZone:[NSTimeZone systemTimeZone]];

    return [dtFormat stringFromDate:aDate];
}

Usar así

NSString *localDate = [self getLocalDateTimeFromUTC:@"yourUTCDate"];
Vvk
fuente
1
No funciona para mí, mi hora local es +3 y este código devuelve +2
Fadi Abuzant
6

Aquí la entrada es una cadena currentUTCTime (en formato 30/08/2012 11:11) convierte la hora de entrada en GMT a la hora de zona establecida por el sistema

//UTC time
NSDateFormatter *utcDateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[utcDateFormatter setDateFormat:@"MM/dd/yyyy HH:mm"];
[utcDateFormatter setTimeZone :[NSTimeZone timeZoneForSecondsFromGMT: 0]];

// utc format
NSDate *dateInUTC = [utcDateFormatter dateFromString: currentUTCTime];

// offset second
NSInteger seconds = [[NSTimeZone systemTimeZone] secondsFromGMT];

// format it and send
NSDateFormatter *localDateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[localDateFormatter setDateFormat:@"MM/dd/yyyy HH:mm"];
[localDateFormatter setTimeZone :[NSTimeZone timeZoneForSecondsFromGMT: seconds]];

// formatted string
NSString *localDate = [localDateFormatter stringFromDate: dateInUTC];
return localDate;
Ashwin Kumar
fuente
4
//This is basic way to get time of any GMT time.

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"hh:mm a"];  // 09:30 AM
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:1]]; // For GMT+1
NSString *time = [formatter stringFromDate:[NSDate date]];  // Current time
Linh Nguyen
fuente
2

Escribo este método para convertir la fecha y hora a nuestra LocalTimeZone

-Aquí (NSString *) El parámetro TimeZone es una zona horaria del servidor

-(NSString *)convertTimeIntoLocal:(NSString *)defaultTime :(NSString *)TimeZone
{
    NSDateFormatter *serverFormatter = [[NSDateFormatter alloc] init];
    [serverFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:TimeZone]];
    [serverFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *theDate = [serverFormatter dateFromString:defaultTime];
    NSDateFormatter *userFormatter = [[NSDateFormatter alloc] init];
    [userFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    [userFormatter setTimeZone:[NSTimeZone localTimeZone]];
    NSString *dateConverted = [userFormatter stringFromDate:theDate];
    return dateConverted;
}
imjaydeep
fuente
1

Dado que nadie parecía estar usando NSDateComponents, pensé que lanzaría uno en ... En esta versión, no NSDateFormatterse usa no, por lo tanto, no se analiza la cadena y NSDateno se usa para representar el tiempo fuera de GMT (UTC). El original NSDateestá en la variable i_date.

NSCalendar *anotherCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:i_anotherCalendar];
anotherCalendar.timeZone = [NSTimeZone timeZoneWithName:i_anotherTimeZone];

NSDateComponents *anotherComponents = [anotherCalendar components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitNanosecond) fromDate:i_date];

// The following is just for checking   
anotherComponents.calendar = anotherCalendar; // anotherComponents.date is nil without this
NSDate *anotherDate = anotherComponents.date;

i_anotherCalendarpodría ser NSCalendarIdentifierGregoriano cualquier otro calendario. Lo NSStringpermitido i_anotherTimeZonepuede ser adquirido con [NSTimeZone knownTimeZoneNames], pero anotherCalendar.timeZonepodría ser [NSTimeZone defaultTimeZone]o [NSTimeZone localTimeZone]o[NSTimeZone systemTimeZone] completo.

En realidad está anotherComponentsreteniendo la hora en la nueva zona horaria. Notarás que anotherDatees igual a i_date, porque tiene tiempo en GMT (UTC).

techniao
fuente
0

Puedes probar este:

NSDate *currentDate = [[NSDate alloc] init];
NSTimeZone *timeZone = [NSTimeZone defaultTimeZone];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterLongStyle];
[dateFormatter setTimeStyle:NSDateFormatterLongStyle];
[dateFormatter setTimeZone:timeZone];
[dateFormatter setDateFormat:@"ZZZ"];
NSString *localDateString = [dateFormatter stringFromDate:currentDate];
NSMutableString *mu = [NSMutableString stringWithString:localDateString];
[mu insertString:@":" atIndex:3];
 NSString *strTimeZone = [NSString stringWithFormat:@"(GMT%@)%@",mu,timeZone.name];
 NSLog(@"%@",strTimeZone);
kalpesh satasiya
fuente
-1

Convierta la hora UTC a la zona horaria actual.

función de llamada

NSLocale *locale = [NSLocale autoupdatingCurrentLocale];

NSString *myLanguageCode = [locale objectForKey: NSLocaleLanguageCode];
NSString *myCountryCode = [locale objectForKey: NSLocaleCountryCode];

NSString *rfc3339DateTimeString = @"2015-02-15 00:00:00"];
NSDate *myDateTime = (NSDate*)[_myCommonFunctions _ConvertUTCTimeToLocalTimeWithFormat:rfc3339DateTimeString LanguageCode:myLanguageCode CountryCode:myCountryCode Formated:NO];

Función

-NSObject*)_ConvertUTCTimeToLocalTimeWithFormat:rfc3339DateTimeString     LanguageCode:(NSString *)lgc CountryCode:(NSString *)ctc Formated:(BOOL) formated
{
    NSDateFormatter *sUserVisibleDateFormatter = nil;
    NSDateFormatter *sRFC3339DateFormatter = nil;

    NSTimeZone *timeZone = [NSTimeZone defaultTimeZone];

    if (sRFC3339DateFormatter == nil)
    {
        sRFC3339DateFormatter = [[NSDateFormatter alloc] init];

        NSLocale *myPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:[NSString stringWithFormat:@"%@", timeZone]];

        [sRFC3339DateFormatter setLocale:myPOSIXLocale];
        [sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
        [sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
    }

    // Convert the RFC 3339 date time string to an NSDate.
    NSDate *date = [sRFC3339DateFormatter dateFromString:rfc3339DateTimeString];

    if (formated == YES)
    {
        NSString *userVisibleDateTimeString;

        if (date != nil)
        {
            if (sUserVisibleDateFormatter == nil)
            {
                sUserVisibleDateFormatter = [[NSDateFormatter alloc] init];
                [sUserVisibleDateFormatter setDateStyle:NSDateFormatterMediumStyle];
                [sUserVisibleDateFormatter setTimeStyle:NSDateFormatterShortStyle];
            }

            // Convert the date object to a user-visible date string.
            userVisibleDateTimeString = [sUserVisibleDateFormatter stringFromDate:date];

            return (NSObject*)userVisibleDateTimeString;
        }
    }

    return (NSObject*)date;
}
Alan10977
fuente